From 059a1e9e4edeb75cef6da05936835cfbc61e1350 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Sun, 28 Jun 2015 13:38:21 +0200 Subject: [PATCH 1/3] miner: broadcast block before insertion/validation --- miner/worker.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/miner/worker.go b/miner/worker.go index 55c23376cd..2955a9dee9 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -239,12 +239,16 @@ func (self *worker) wait() { continue } + // broadcast before waiting for validation + go self.mux.Post(core.NewMinedBlockEvent{block}) + // insert mined block in to our own chain if _, err := self.chain.InsertChain(types.Blocks{block}); err == nil { + // remove uncles we've previously inserted for _, uncle := range block.Uncles() { delete(self.possibleUncles, uncle.Hash()) } - self.mux.Post(core.NewMinedBlockEvent{block}) + // check staleness and display confirmation var stale, confirm string canonBlock := self.chain.GetBlockByNumber(block.NumberU64()) if canonBlock != nil && canonBlock.Hash() != block.Hash() { @@ -256,6 +260,7 @@ func (self *worker) wait() { glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm) + // XXX remove old structured json logging jsonlogger.LogJson(&logger.EthMinerNewBlock{ BlockHash: block.Hash().Hex(), BlockNumber: block.Number(), From d1e93db3ebc218044339a6c5c95b4907e6351ede Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Mon, 29 Jun 2015 12:12:30 +0200 Subject: [PATCH 2/3] core, miner: added write block method & changed mining propagation --- core/chain_manager.go | 112 +++++++++++++++++++++++++----------------- miner/worker.go | 56 ++++++++++----------- 2 files changed, 95 insertions(+), 73 deletions(-) diff --git a/core/chain_manager.go b/core/chain_manager.go index 3b9b7517be..4a80d4a69a 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -548,6 +548,58 @@ func (self *ChainManager) procFutureBlocks() { } } +type writeStatus byte + +const ( + nonStatTy writeStatus = iota + canonStatTy + splitStatTy + sideStatTy +) + +func (self *ChainManager) WriteBlock(block *types.Block) (status writeStatus, err error) { + self.wg.Add(1) + defer self.wg.Done() + + cblock := self.currentBlock + // 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 block.Td.Cmp(self.Td()) > 0 { + // chain fork + if block.ParentHash() != cblock.Hash() { + // during split we merge two different chains and create the new canonical chain + err := self.merge(cblock, block) + if err != nil { + return nonStatTy, err + } + + status = splitStatTy + } + + self.mu.Lock() + self.setTotalDifficulty(block.Td) + self.insert(block) + self.mu.Unlock() + + self.setTransState(state.New(block.Root(), self.stateDb)) + self.txState.SetState(state.New(block.Root(), self.stateDb)) + + status = canonStatTy + } else { + status = sideStatTy + } + + // Write block to database. Eventually we'll have to improve on this and throw away blocks that are + // not in the canonical chain. + self.mu.Lock() + self.write(block) + self.mu.Unlock() + // Delete from future blocks + self.futureBlocks.Delete(block.Hash()) + + return +} + // InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. It an error is returned // it will return the index number of the failing block as well an error describing what went wrong (for possible errors see core/errors.go). func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { @@ -641,59 +693,29 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { txcount += len(block.Transactions()) - cblock := self.currentBlock - // 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 block.Td.Cmp(self.Td()) > 0 { - // chain fork - if block.ParentHash() != cblock.Hash() { - // during split we merge two different chains and create the new canonical chain - err := self.merge(cblock, block) - if err != nil { - return i, err - } - - queue[i] = ChainSplitEvent{block, logs} - queueEvent.splitCount++ - } - - self.mu.Lock() - self.setTotalDifficulty(block.Td) - self.insert(block) - self.mu.Unlock() - - jsonlogger.LogJson(&logger.EthChainNewHead{ - BlockHash: block.Hash().Hex(), - BlockNumber: block.Number(), - ChainHeadHash: cblock.Hash().Hex(), - BlockPrevHash: block.ParentHash().Hex(), - }) - - self.setTransState(state.New(block.Root(), self.stateDb)) - self.txState.SetState(state.New(block.Root(), self.stateDb)) - - queue[i] = ChainEvent{block, block.Hash(), logs} - queueEvent.canonicalCount++ - + // write the block to the chain and get the status + status, err := self.WriteBlock(block) + if err != nil { + return i, err + } + switch status { + case canonStatTy: if glog.V(logger.Debug) { glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) } - } else { + queue[i] = ChainEvent{block, block.Hash(), logs} + queueEvent.canonicalCount++ + case sideStatTy: if glog.V(logger.Detail) { glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) } - queue[i] = ChainSideEvent{block, logs} queueEvent.sideCount++ + case splitStatTy: + queue[i] = ChainSplitEvent{block, logs} + queueEvent.splitCount++ } - // Write block to database. Eventually we'll have to improve on this and throw away blocks that are - // not in the canonical chain. - self.write(block) - // Delete from future blocks - self.futureBlocks.Delete(block.Hash()) - stats.processed++ - blockInsertTimer.UpdateSince(bstart) } if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) { @@ -752,9 +774,9 @@ func (self *ChainManager) diff(oldBlock, newBlock *types.Block) (types.Blocks, e } } - if glog.V(logger.Info) { + if glog.V(logger.Debug) { commonHash := commonBlock.Hash() - glog.Infof("Fork detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4]) + glog.Infof("Chain split detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4]) } return newChain, nil diff --git a/miner/worker.go b/miner/worker.go index 2955a9dee9..ab01ba09e2 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -239,43 +239,35 @@ func (self *worker) wait() { continue } - // broadcast before waiting for validation - go self.mux.Post(core.NewMinedBlockEvent{block}) - // insert mined block in to our own chain - if _, err := self.chain.InsertChain(types.Blocks{block}); err == nil { - // remove uncles we've previously inserted - for _, uncle := range block.Uncles() { - delete(self.possibleUncles, uncle.Hash()) - } - - // check staleness and display confirmation - var stale, confirm string - canonBlock := self.chain.GetBlockByNumber(block.NumberU64()) - if canonBlock != nil && canonBlock.Hash() != block.Hash() { - stale = "stale " - } else { - confirm = "Wait 5 blocks for confirmation" - self.current.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), self.current.localMinedBlocks) - } - - glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm) + _, err := self.chain.WriteBlock(block) + if err != nil { + glog.V(logger.Error).Infoln("error writing block to chain", err) + continue + } - // XXX remove old structured json logging - jsonlogger.LogJson(&logger.EthMinerNewBlock{ - BlockHash: block.Hash().Hex(), - BlockNumber: block.Number(), - ChainHeadHash: block.ParentHeaderHash.Hex(), - BlockPrevHash: block.ParentHeaderHash.Hex(), - }) + // check staleness and display confirmation + var stale, confirm string + canonBlock := self.chain.GetBlockByNumber(block.NumberU64()) + if canonBlock != nil && canonBlock.Hash() != block.Hash() { + stale = "stale " } else { - self.commitNewWork() + confirm = "Wait 5 blocks for confirmation" + self.current.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), self.current.localMinedBlocks) } + + glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm) + + // broadcast before waiting for validation + go self.mux.Post(core.NewMinedBlockEvent{block}) + + self.commitNewWork() } } } func (self *worker) push() { if atomic.LoadInt32(&self.mining) == 1 { + self.current.state.Sync() self.current.block.SetRoot(self.current.state.Root()) // push new work to agents @@ -302,6 +294,13 @@ func (self *worker) makeCurrent() { if block.Time() <= parent.Time() { block.Header().Time = parent.Header().Time + 1 } + // this will ensure we're not going off too far in the future + if now := time.Now().Unix(); block.Time() > now+4 { + wait := time.Duration(block.Time()-now) * time.Second + glog.V(logger.Info).Infoln("We are too far in the future. Waiting for", wait) + time.Sleep(wait) + } + block.Header().Extra = self.extra // when 08 is processed ancestors contain 07 (quick block) @@ -428,6 +427,7 @@ func (self *worker) commitNewWork() { self.current.block.SetUncles(uncles) core.AccumulateRewards(self.current.state, self.current.block) + self.current.block.Td = new(big.Int).Set(core.CalcTD(self.current.block, self.chain.GetBlock(self.current.block.ParentHash()))) self.current.state.Update() From b39042db5672e830ddec41ea97c642d93be61c30 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Mon, 29 Jun 2015 12:42:47 +0200 Subject: [PATCH 3/3] core, miner: implemented canary --- core/canary.go | 28 ++++++++++++++++++++++++++++ miner/worker.go | 6 ++++++ 2 files changed, 34 insertions(+) create mode 100644 core/canary.go diff --git a/core/canary.go b/core/canary.go new file mode 100644 index 0000000000..de77c4bbaa --- /dev/null +++ b/core/canary.go @@ -0,0 +1,28 @@ +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" +) + +var ( + jeff = common.HexToAddress("9d38997c624a71b21278389ea2fdc460d000e4b2") + vitalik = common.HexToAddress("b1e570be07eaa673e4fd0c8265b64ef739385709") + christoph = common.HexToAddress("529bc43a5d93789fa28de1961db6a07e752204ae") + gav = common.HexToAddress("e3e942b2aa524293c84ff6c7f87a6635790ad5e4") +) + +// Canary will check the 0'd address of the 4 contracts above. +// If two or more are set to anything other than a 0 the canary +// dies a horrible death. +func Canary(statedb *state.StateDB) bool { + r := new(big.Int) + r.Add(r, statedb.GetState(jeff, common.Hash{}).Big()) + r.Add(r, statedb.GetState(vitalik, common.Hash{}).Big()) + r.Add(r, statedb.GetState(christoph, common.Hash{}).Big()) + r.Add(r, statedb.GetState(gav, common.Hash{}).Big()) + + return r.Cmp(big.NewInt(1)) > 0 +} diff --git a/miner/worker.go b/miner/worker.go index ab01ba09e2..1e5d479656 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -267,6 +267,12 @@ func (self *worker) wait() { func (self *worker) push() { if atomic.LoadInt32(&self.mining) == 1 { + if core.Canary(self.current.state) { + glog.Infoln("Toxicity levels rising to deadly levels. Your canary has died. You can go back or continue down the mineshaft --more--") + glog.Infoln("You turn back and abort mining") + return + } + self.current.state.Sync() self.current.block.SetRoot(self.current.state.Root())