diff options
-rw-r--r-- | cmd/geth/admin.go | 72 | ||||
-rw-r--r-- | cmd/geth/main.go | 2 | ||||
-rw-r--r-- | core/block_processor.go | 28 | ||||
-rw-r--r-- | core/blocks.go | 7 | ||||
-rw-r--r-- | core/chain_makers.go | 4 | ||||
-rw-r--r-- | core/chain_manager.go | 55 | ||||
-rw-r--r-- | core/chain_manager_test.go | 7 | ||||
-rw-r--r-- | core/genesis.go | 1 | ||||
-rw-r--r-- | core/types/block.go | 16 | ||||
-rw-r--r-- | core/types/common.go | 2 | ||||
-rw-r--r-- | eth/downloader/downloader.go | 2 | ||||
-rw-r--r-- | eth/handler.go | 3 | ||||
-rw-r--r-- | miner/remote_agent.go | 13 |
13 files changed, 168 insertions, 44 deletions
diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go index c42e91615..defbb43ec 100644 --- a/cmd/geth/admin.go +++ b/cmd/geth/admin.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "os" "time" @@ -9,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" @@ -50,15 +52,11 @@ func (js *jsre) adminBindings() { debug.Set("printBlock", js.printBlock) debug.Set("dumpBlock", js.dumpBlock) debug.Set("getBlockRlp", js.getBlockRlp) + debug.Set("setHead", js.setHead) + debug.Set("block", js.debugBlock) } -func (js *jsre) downloadProgress(call otto.FunctionCall) otto.Value { - current, max := js.ethereum.Downloader().Stats() - - return js.re.ToVal(fmt.Sprintf("%d/%d", current, max)) -} - -func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value { +func (js *jsre) getBlock(call otto.FunctionCall) (*types.Block, error) { var block *types.Block if len(call.ArgumentList) > 0 { if call.Argument(0).IsNumber() { @@ -68,12 +66,66 @@ func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value { hash, _ := call.Argument(0).ToString() block = js.ethereum.ChainManager().GetBlock(common.HexToHash(hash)) } else { - fmt.Println("invalid argument for dump. Either hex string or number") + return nil, errors.New("invalid argument for dump. Either hex string or number") } + return block, nil + } - } else { - block = js.ethereum.ChainManager().CurrentBlock() + return nil, errors.New("requires block number or block hash as argument") +} + +func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value { + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + if block == nil { + fmt.Println("block not found") + return otto.UndefinedValue() } + + old := vm.Debug + vm.Debug = true + _, err = js.ethereum.BlockProcessor().RetryProcess(block) + if err != nil { + glog.Infoln(err) + } + vm.Debug = old + + return otto.UndefinedValue() +} + +func (js *jsre) setHead(call otto.FunctionCall) otto.Value { + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + if block == nil { + fmt.Println("block not found") + return otto.UndefinedValue() + } + + js.ethereum.ChainManager().SetHead(block) + return otto.UndefinedValue() +} + +func (js *jsre) downloadProgress(call otto.FunctionCall) otto.Value { + current, max := js.ethereum.Downloader().Stats() + + return js.re.ToVal(fmt.Sprintf("%d/%d", current, max)) +} + +func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value { + block, err := js.getBlock(call) + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + if block == nil { fmt.Println("block not found") return otto.UndefinedValue() diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 5b523da3e..97d358407 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -49,7 +49,7 @@ import _ "net/http/pprof" const ( ClientIdentifier = "Geth" - Version = "0.9.10" + Version = "0.9.11" ) var app = utils.NewApp(Version, "the go-ethereum command line interface") diff --git a/core/block_processor.go b/core/block_processor.go index e3c284979..3030af27d 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -149,28 +149,42 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state return receipts, err } +func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err error) { + // Processing a blocks may never happen simultaneously + sm.mutex.Lock() + defer sm.mutex.Unlock() + + header := block.Header() + if !sm.bc.HasBlock(header.ParentHash) { + return nil, ParentError(header.ParentHash) + } + parent := sm.bc.GetBlock(header.ParentHash) + + return sm.processWithParent(block, parent) +} + // Process block will attempt to process the given block's transactions and applies them // on top of the block's parent state (given it exists) and will return wether it was // successful or not. -func (sm *BlockProcessor) Process(block *types.Block) (td *big.Int, logs state.Logs, err error) { +func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, err error) { // Processing a blocks may never happen simultaneously sm.mutex.Lock() defer sm.mutex.Unlock() header := block.Header() if sm.bc.HasBlock(header.Hash()) { - return nil, nil, &KnownBlockError{header.Number, header.Hash()} + return nil, &KnownBlockError{header.Number, header.Hash()} } if !sm.bc.HasBlock(header.ParentHash) { - return nil, nil, ParentError(header.ParentHash) + return nil, ParentError(header.ParentHash) } parent := sm.bc.GetBlock(header.ParentHash) return sm.processWithParent(block, parent) } -func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big.Int, logs state.Logs, err error) { +func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, err error) { sm.lastAttemptedBlock = block // Create a new state based on the parent's root (e.g., create copy) @@ -183,7 +197,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big // There can be at most two uncles if len(block.Uncles()) > 2 { - return nil, nil, ValidationError("Block can only contain one uncle (contained %v)", len(block.Uncles())) + return nil, ValidationError("Block can only contain one uncle (contained %v)", len(block.Uncles())) } receipts, err := sm.TransitionState(state, parent, block, false) @@ -232,7 +246,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big } // Calculate the td for this block - td = CalculateTD(block, parent) + //td = CalculateTD(block, parent) // Sync the current block's state to the database state.Sync() @@ -244,7 +258,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big putTx(sm.extraDb, tx, block, uint64(i)) } - return td, state.Logs(), nil + return state.Logs(), nil } // Validates the current block. Returns an error if the block was invalid, diff --git a/core/blocks.go b/core/blocks.go new file mode 100644 index 000000000..b26e8f6ee --- /dev/null +++ b/core/blocks.go @@ -0,0 +1,7 @@ +package core + +import "github.com/ethereum/go-ethereum/common" + +var badHashes = []common.Hash{ + common.HexToHash("f269c503aed286caaa0d114d6a5320e70abbc2febe37953207e76a2873f2ba79"), +} diff --git a/core/chain_makers.go b/core/chain_makers.go index 810741820..250671ef8 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -93,12 +93,12 @@ func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Dat blocks := make(types.Blocks, max) for i := 0; i < max; i++ { block := makeBlock(bman, parent, i, db, seed) - td, _, err := bman.processWithParent(block, parent) + _, err := bman.processWithParent(block, parent) if err != nil { fmt.Println("process with parent failed", err) panic(err) } - block.Td = td + block.Td = CalculateTD(block, parent) blocks[i] = block parent = block } diff --git a/core/chain_manager.go b/core/chain_manager.go index 4f1e1e68a..3f2b3a26a 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -97,6 +97,21 @@ type ChainManager struct { func NewChainManager(blockDb, stateDb common.Database, mux *event.TypeMux) *ChainManager { bc := &ChainManager{blockDb: blockDb, stateDb: stateDb, genesisBlock: GenesisBlock(stateDb), eventMux: mux, quit: make(chan struct{}), cache: NewBlockCache(blockCacheLimit)} bc.setLastBlock() + + // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain + for _, hash := range badHashes { + if block := bc.GetBlock(hash); block != nil { + glog.V(logger.Error).Infof("Found bad hash. Reorganising chain to state %x\n", block.ParentHash().Bytes()[:4]) + block = bc.GetBlock(block.ParentHash()) + if block == nil { + glog.Fatal("Unable to complete. Parent block not found. Corrupted DB?") + } + bc.SetHead(block) + + glog.V(logger.Error).Infoln("Chain reorg was successfull. Resuming normal operation") + } + } + bc.transState = bc.State().Copy() // Take ownership of this particular state bc.txState = state.ManageState(bc.State().Copy()) @@ -109,6 +124,26 @@ func NewChainManager(blockDb, stateDb common.Database, mux *event.TypeMux) *Chai return bc } +func (bc *ChainManager) SetHead(head *types.Block) { + bc.mu.Lock() + defer bc.mu.Unlock() + + for block := bc.currentBlock; block != nil && block.Hash() != head.Hash(); block = bc.GetBlock(block.Header().ParentHash) { + bc.removeBlock(block) + } + + bc.cache = NewBlockCache(blockCacheLimit) + bc.currentBlock = head + bc.makeCache() + + statedb := state.New(head.Root(), bc.stateDb) + bc.txState = state.ManageState(statedb) + bc.transState = statedb.Copy() + bc.setTotalDifficulty(head.Td) + bc.insert(head) + bc.setLastBlock() +} + func (self *ChainManager) Td() *big.Int { self.mu.RLock() defer self.mu.RUnlock() @@ -287,7 +322,12 @@ func (self *ChainManager) Export(w io.Writer) error { last := self.currentBlock.NumberU64() for nr := uint64(0); nr <= last; nr++ { - if err := self.GetBlockByNumber(nr).EncodeRLP(w); err != nil { + block := self.GetBlockByNumber(nr) + if block == nil { + return fmt.Errorf("export failed on #%d: not found", nr) + } + + if err := block.EncodeRLP(w); err != nil { return err } } @@ -461,7 +501,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { } // Call in to the block processor and check for errors. It's likely that if one block fails // all others will fail too (unless a known block is returned). - td, logs, err := self.processor.Process(block) + logs, err := self.processor.Process(block) if err != nil { if IsKnownBlockErr(err) { continue @@ -486,13 +526,14 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { h := block.Header() - glog.V(logger.Error).Infof("INVALID block #%v (%x)\n", h.Number, h.Hash().Bytes()[:4]) + glog.V(logger.Error).Infof("INVALID block #%v (%x)\n", h.Number, h.Hash().Bytes()) glog.V(logger.Error).Infoln(err) glog.V(logger.Debug).Infoln(block) return err } - block.Td = td + + block.Td = new(big.Int).Set(CalculateTD(block, self.GetBlock(block.ParentHash()))) self.mu.Lock() { @@ -502,14 +543,14 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { self.write(block) // Compare the TD of the last known block in the canonical chain to make sure it's greater. // At this point it's possible that a different chain (fork) becomes the new canonical chain. - if td.Cmp(self.td) > 0 { + if block.Td.Cmp(self.td) > 0 { //if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, common.Big1)) < 0 { if block.Number().Cmp(cblock.Number()) <= 0 { chash := cblock.Hash() hash := block.Hash() if glog.V(logger.Info) { - glog.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, hash[:4], td, cblock.Header().Number, chash[:4], self.td) + glog.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, hash[:4], block.Td, cblock.Header().Number, chash[:4], self.td) } // during split we merge two different chains and create the new canonical chain self.merge(self.getBlockByNumber(block.NumberU64()), block) @@ -518,7 +559,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { queueEvent.splitCount++ } - self.setTotalDifficulty(td) + self.setTotalDifficulty(block.Td) self.insert(block) jsonlogger.LogJson(&logger.EthChainNewHead{ diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index 19afe0d5c..f16c0f0c3 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -69,15 +69,16 @@ func printChain(bc *ChainManager) { func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) { td := new(big.Int) for _, block := range chainB { - td2, _, err := bman.bc.processor.Process(block) + _, err := bman.bc.processor.Process(block) if err != nil { if IsKnownBlockErr(err) { continue } return nil, err } - block.Td = td2 - td = td2 + parent := bman.bc.GetBlock(block.ParentHash()) + block.Td = CalculateTD(block, parent) + td = block.Td bman.bc.mu.Lock() { diff --git a/core/genesis.go b/core/genesis.go index 0ce60054e..e72834822 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -52,6 +52,7 @@ func GenesisBlock(db common.Database) *types.Block { } statedb.Sync() genesis.Header().Root = statedb.Root() + genesis.Td = params.GenesisDifficulty return genesis } diff --git a/core/types/block.go b/core/types/block.go index c47b555ed..f9206ec76 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -347,22 +347,20 @@ func (self *Block) Copy() *Block { } func (self *Block) String() string { - return fmt.Sprintf(`BLOCK(%x): Size: %v TD: %v { -NoNonce: %x -Header: -[ + return fmt.Sprintf(`Block(#%v): Size: %v TD: %v { +MinerHash: %x %v -] Transactions: %v Uncles: %v } -`, self.header.Hash(), self.Size(), self.Td, self.header.HashNoNonce(), self.header, self.transactions, self.uncles) +`, self.Number(), self.Size(), self.Td, self.header.HashNoNonce(), self.header, self.transactions, self.uncles) } func (self *Header) String() string { - return fmt.Sprintf(` + return fmt.Sprintf(`Header(%x): +[ ParentHash: %x UncleHash: %x Coinbase: %x @@ -377,8 +375,8 @@ func (self *Header) String() string { Time: %v Extra: %s MixDigest: %x - Nonce: %x`, - self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.MixDigest, self.Nonce) + Nonce: %x +]`, self.Hash(), self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.MixDigest, self.Nonce) } type Blocks []*Block diff --git a/core/types/common.go b/core/types/common.go index 4397d4938..dbdaaba0c 100644 --- a/core/types/common.go +++ b/core/types/common.go @@ -10,7 +10,7 @@ import ( ) type BlockProcessor interface { - Process(*Block) (*big.Int, state.Logs, error) + Process(*Block) (state.Logs, error) } const bloomLength = 256 diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index addcbcc44..cfc494b2f 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -472,6 +472,8 @@ func (d *Downloader) process() error { } break } else if err != nil { + // immediatly unregister the false peer but do not disconnect + d.UnregisterPeer(d.activePeer) // Reset chain completely. This needs much, much improvement. // instead: check all blocks leading down to this block false block and remove it blocks = nil diff --git a/eth/handler.go b/eth/handler.go index 5c0660d84..622f22132 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -36,6 +36,7 @@ pm.chainman.InsertChain(blocks) import ( "fmt" + "math" "math/big" "sync" @@ -326,7 +327,7 @@ func (pm *ProtocolManager) BroadcastBlock(hash common.Hash, block *types.Block) } // Broadcast block to peer set // XXX due to the current shit state of the network disable the limit - //peers = peers[:int(math.Sqrt(float64(len(peers))))] + peers = peers[:int(math.Sqrt(float64(len(peers))))] for _, peer := range peers { peer.sendNewBlock(block) } diff --git a/miner/remote_agent.go b/miner/remote_agent.go index aa04a58aa..87456cfec 100644 --- a/miner/remote_agent.go +++ b/miner/remote_agent.go @@ -1,6 +1,8 @@ package miner import ( + "math/big" + "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -50,7 +52,6 @@ out: break out case work := <-a.workCh: a.work = work - a.returnCh <- nil } } } @@ -58,12 +59,18 @@ out: func (a *RemoteAgent) GetWork() [3]string { var res [3]string - // XXX Wait here until work != nil ? if a.work != nil { + a.currentWork = a.work + res[0] = a.work.HashNoNonce().Hex() seedHash, _ := ethash.GetSeedHash(a.currentWork.NumberU64()) res[1] = common.Bytes2Hex(seedHash) - res[2] = common.Bytes2Hex(a.work.Difficulty().Bytes()) + // Calculate the "target" to be returned to the external miner + n := big.NewInt(1) + n.Lsh(n, 255) + n.Div(n, a.work.Difficulty()) + n.Lsh(n, 1) + res[2] = common.Bytes2Hex(n.Bytes()) } return res |