aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/block_cache.go5
-rw-r--r--core/block_processor.go36
-rw-r--r--core/chain_manager.go30
-rw-r--r--core/error.go5
-rw-r--r--core/events.go4
-rw-r--r--miner/remote_agent.go2
-rw-r--r--miner/worker.go78
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
}