diff options
-rw-r--r-- | core/block_cache.go | 5 | ||||
-rw-r--r-- | core/block_processor.go | 36 | ||||
-rw-r--r-- | core/chain_manager.go | 30 | ||||
-rw-r--r-- | core/error.go | 5 | ||||
-rw-r--r-- | core/events.go | 4 | ||||
-rw-r--r-- | miner/remote_agent.go | 2 | ||||
-rw-r--r-- | miner/worker.go | 78 |
7 files changed, 117 insertions, 43 deletions
diff --git a/core/block_cache.go b/core/block_cache.go index 321021eb4..ea39e78e8 100644 --- a/core/block_cache.go +++ b/core/block_cache.go @@ -66,3 +66,8 @@ func (bc *BlockCache) Get(hash common.Hash) *types.Block { return nil } + +func (bc *BlockCache) Has(hash common.Hash) bool { + _, ok := bc.blocks[hash] + return ok +} diff --git a/core/block_processor.go b/core/block_processor.go index 99c5fea05..ae8d5fe7b 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -166,9 +166,15 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big // Create a new state based on the parent's root (e.g., create copy) state := state.New(parent.Root(), sm.db) + // track (possible) uncle block + var uncle bool // Block validation if err = sm.ValidateHeader(block.Header(), parent.Header()); err != nil { - return + if err != BlockEqualTSErr { + return + } + err = nil + uncle = true } // There can be at most two uncles @@ -223,14 +229,22 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big td = CalculateTD(block, parent) // Sync the current block's state to the database state.Sync() - // Remove transactions from the pool - sm.txpool.RemoveSet(block.Transactions()) + + if !uncle { + // Remove transactions from the pool + sm.txpool.RemoveSet(block.Transactions()) + } for _, tx := range block.Transactions() { putTx(sm.extraDb, tx) } - chainlogger.Infof("processed block #%d (%x...)\n", header.Number, block.Hash().Bytes()[0:4]) + if uncle { + chainlogger.Infof("found possible uncle block #%d (%x...)\n", header.Number, block.Hash().Bytes()[0:4]) + return td, nil, BlockEqualTSErr + } else { + chainlogger.Infof("processed block #%d (%x...)\n", header.Number, block.Hash().Bytes()[0:4]) + } return td, state.Logs(), nil } @@ -255,10 +269,6 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error { return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b) } - if block.Time <= parent.Time { - return ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time) - } - if int64(block.Time) > time.Now().Unix() { return BlockFutureErr } @@ -272,6 +282,10 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error { return ValidationError("Block's nonce is invalid (= %x)", block.Nonce) } + if block.Time <= parent.Time { + return BlockEqualTSErr //ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time) + } + return nil } @@ -307,14 +321,10 @@ func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, paren return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) } - if err := sm.ValidateHeader(uncle, ancestorHeaders[uncle.ParentHash]); err != nil { + if err := sm.ValidateHeader(uncle, ancestorHeaders[uncle.ParentHash]); err != nil && err != BlockEqualTSErr { return ValidationError(fmt.Sprintf("%v", err)) } - if !sm.Pow.Verify(types.NewBlockWithHeader(uncle)) { - return ValidationError("Uncle's nonce is invalid (= %x)", uncle.Nonce) - } - r := new(big.Int) r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16)) diff --git a/core/chain_manager.go b/core/chain_manager.go index 1bc8edea6..192fa1df0 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -106,12 +106,7 @@ func NewChainManager(blockDb, stateDb common.Database, mux *event.TypeMux) *Chai // Take ownership of this particular state bc.txState = state.ManageState(bc.State().Copy()) - // load in last `blockCacheLimit` - 1 blocks. Last block is the current. - ancestors := bc.GetAncestors(bc.currentBlock, blockCacheLimit-1) - ancestors = append(ancestors, bc.currentBlock) - for _, block := range ancestors { - bc.cache.Push(block) - } + bc.makeCache() go bc.update() @@ -194,6 +189,18 @@ func (bc *ChainManager) setLastBlock() { chainlogger.Infof("Last block (#%v) %x TD=%v\n", bc.currentBlock.Number(), bc.currentBlock.Hash(), bc.td) } +func (bc *ChainManager) makeCache() { + if bc.cache == nil { + bc.cache = NewBlockCache(blockCacheLimit) + } + // load in last `blockCacheLimit` - 1 blocks. Last block is the current. + ancestors := bc.GetAncestors(bc.currentBlock, blockCacheLimit-1) + ancestors = append(ancestors, bc.currentBlock) + for _, block := range ancestors { + bc.cache.Push(block) + } +} + // Block creation & chain handling func (bc *ChainManager) NewBlock(coinbase common.Address) *types.Block { bc.mu.RLock() @@ -240,10 +247,15 @@ func (bc *ChainManager) Reset() { bc.removeBlock(block) } + if bc.cache == nil { + bc.cache = NewBlockCache(blockCacheLimit) + } + // Prepare the genesis block bc.write(bc.genesisBlock) bc.insert(bc.genesisBlock) bc.currentBlock = bc.genesisBlock + bc.makeCache() bc.setTotalDifficulty(common.Big("0")) } @@ -265,6 +277,7 @@ func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) { bc.write(bc.genesisBlock) bc.insert(bc.genesisBlock) bc.currentBlock = bc.genesisBlock + bc.makeCache() } // Export writes the active chain to the given writer. @@ -434,6 +447,11 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { continue } + if err == BlockEqualTSErr { + queue[i] = ChainSideEvent{block, logs} + continue + } + h := block.Header() chainlogger.Infof("INVALID block #%v (%x)\n", h.Number, h.Hash().Bytes()[:4]) chainlogger.Infoln(err) diff --git a/core/error.go b/core/error.go index f6ac26cff..0642948cd 100644 --- a/core/error.go +++ b/core/error.go @@ -9,8 +9,9 @@ import ( ) var ( - BlockNumberErr = errors.New("block number invalid") - BlockFutureErr = errors.New("block time is in the future") + BlockNumberErr = errors.New("block number invalid") + BlockFutureErr = errors.New("block time is in the future") + BlockEqualTSErr = errors.New("block time stamp equal to previous") ) // Parent error. In case a parent is unknown this error will be thrown diff --git a/core/events.go b/core/events.go index 8c5fb592a..b93f27c51 100644 --- a/core/events.go +++ b/core/events.go @@ -38,6 +38,10 @@ type PendingBlockEvent struct { Logs state.Logs } +type ChainUncleEvent struct { + Block *types.Block +} + type ChainHeadEvent struct{ Block *types.Block } // Mining operation events diff --git a/miner/remote_agent.go b/miner/remote_agent.go index 8ea164f34..3911ac61e 100644 --- a/miner/remote_agent.go +++ b/miner/remote_agent.go @@ -17,7 +17,6 @@ type RemoteAgent struct { func NewRemoteAgent() *RemoteAgent { agent := &RemoteAgent{} - go agent.run() return agent } @@ -33,6 +32,7 @@ func (a *RemoteAgent) SetWorkCh(returnCh chan<- Work) { func (a *RemoteAgent) Start() { a.quit = make(chan struct{}) a.workCh = make(chan *types.Block, 1) + go a.run() } func (a *RemoteAgent) Stop() { diff --git a/miner/worker.go b/miner/worker.go index 4a52a40fe..587036f22 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -24,7 +24,7 @@ type environment struct { state *state.StateDB coinbase *state.StateObject block *types.Block - ancestors *set.Set + family *set.Set uncles *set.Set } @@ -34,13 +34,10 @@ func env(block *types.Block, eth core.Backend) *environment { totalUsedGas: new(big.Int), state: state, block: block, - ancestors: set.New(), + family: set.New(), uncles: set.New(), coinbase: state.GetOrNewStateObject(block.Coinbase()), } - for _, ancestor := range eth.ChainManager().GetAncestors(block, 7) { - env.ancestors.Add(ancestor.Hash()) - } return env } @@ -75,17 +72,21 @@ type worker struct { current *environment + uncleMu sync.Mutex + possibleUncles map[common.Hash]*types.Block + mining bool } func newWorker(coinbase common.Address, eth core.Backend) *worker { return &worker{ - eth: eth, - mux: eth.EventMux(), - recv: make(chan Work), - chain: eth.ChainManager(), - proc: eth.BlockProcessor(), - coinbase: coinbase, + eth: eth, + mux: eth.EventMux(), + recv: make(chan Work), + chain: eth.ChainManager(), + proc: eth.BlockProcessor(), + possibleUncles: make(map[common.Hash]*types.Block), + coinbase: coinbase, } } @@ -115,7 +116,7 @@ func (self *worker) register(agent Agent) { } func (self *worker) update() { - events := self.mux.Subscribe(core.ChainHeadEvent{}, core.NewMinedBlockEvent{}) + events := self.mux.Subscribe(core.ChainHeadEvent{}, core.NewMinedBlockEvent{}, core.ChainSideEvent{}) timer := time.NewTicker(2 * time.Second) @@ -130,6 +131,10 @@ out: } case core.NewMinedBlockEvent: self.commitNewWork() + case core.ChainSideEvent: + self.uncleMu.Lock() + self.possibleUncles[ev.Block.Hash()] = ev.Block + self.uncleMu.Unlock() } case <-self.quit: // stop all agents @@ -145,6 +150,9 @@ out: events.Unsubscribe() } +func (self *worker) addUncle(uncle *types.Block) { +} + func (self *worker) wait() { for { for work := range self.recv { @@ -162,6 +170,10 @@ func (self *worker) wait() { }) if err := self.chain.InsertChain(types.Blocks{self.current.block}); err == nil { + for _, uncle := range self.current.block.Uncles() { + delete(self.possibleUncles, uncle.Hash()) + } + self.mux.Post(core.NewMinedBlockEvent{self.current.block}) } else { self.commitNewWork() @@ -191,15 +203,21 @@ func (self *worker) commitNewWork() { block := self.chain.NewBlock(self.coinbase) self.current = env(block, self.eth) + for _, ancestor := range self.chain.GetAncestors(block, 7) { + self.current.family.Add(ancestor.Hash()) + } + parent := self.chain.GetBlock(self.current.block.ParentHash()) self.current.coinbase.SetGasPool(core.CalcGasLimit(parent, self.current.block)) transactions := self.eth.TxPool().GetTransactions() sort.Sort(types.TxByNonce{transactions}) - minerlogger.Infof("committing new work with %d txs\n", len(transactions)) // Keep track of transactions which return errors so they can be removed - var remove types.Transactions + var ( + remove types.Transactions + tcount = 0 + ) gasLimit: for i, tx := range transactions { err := self.commitTransaction(tx) @@ -217,10 +235,30 @@ gasLimit: minerlogger.Infof("Gas limit reached for block. %d TXs included in this block\n", i) // Break on gas limit break gasLimit + default: + tcount++ } } self.eth.TxPool().RemoveSet(remove) + var uncles []*types.Header + for hash, uncle := range self.possibleUncles { + if len(uncles) == 2 { + break + } + + if err := self.commitUncle(uncle.Header()); err != nil { + minerlogger.Infof("Bad uncle found and will be removed (%x)\n", hash[:4]) + minerlogger.Debugln(uncle) + } else { + minerlogger.Infof("commiting %x as uncle\n", hash[:4]) + uncles = append(uncles, uncle.Header()) + } + } + minerlogger.Infof("commit new work with %d txs & %d uncles\n", tcount, len(uncles)) + + self.current.block.SetUncles(uncles) + self.current.state.AddBalance(self.coinbase, core.BlockReward) self.current.state.Update(common.Big0) @@ -240,18 +278,16 @@ func (self *worker) commitUncle(uncle *types.Header) error { } self.current.uncles.Add(uncle.Hash()) - if !self.current.ancestors.Has(uncle.ParentHash) { + if !self.current.family.Has(uncle.ParentHash) { return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) } - if !self.pow.Verify(types.NewBlockWithHeader(uncle)) { - return core.ValidationError("Uncle's nonce is invalid (= %x)", uncle.Nonce) + if self.current.family.Has(uncle.Hash()) { + return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", uncle.Hash())) } - uncleAccount := self.current.state.GetAccount(uncle.Coinbase) - uncleAccount.AddBalance(uncleReward) - - self.current.coinbase.AddBalance(uncleReward) + self.current.state.AddBalance(uncle.Coinbase, uncleReward) + self.current.state.AddBalance(self.coinbase, inclusionReward) return nil } |