diff options
Diffstat (limited to 'core/chain_manager.go')
-rw-r--r-- | core/chain_manager.go | 163 |
1 files changed, 116 insertions, 47 deletions
diff --git a/core/chain_manager.go b/core/chain_manager.go index 959bfd398..9dc41f421 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -14,25 +14,28 @@ import ( "github.com/ethereum/go-ethereum/state" ) -var chainlogger = logger.NewLogger("CHAIN") - -type ChainEvent struct { - Block *types.Block - Td *big.Int -} +var ( + chainlogger = logger.NewLogger("CHAIN") + jsonlogger = logger.NewJsonLogger() +) type StateQuery interface { GetAccount(addr []byte) *state.StateObject } -func CalcDifficulty(block, parent *types.Block) *big.Int { +func CalcDifficulty(block, parent *types.Header) *big.Int { diff := new(big.Int) - adjust := new(big.Int).Rsh(parent.Difficulty(), 10) - if block.Time() >= parent.Time()+8 { - diff.Sub(parent.Difficulty(), adjust) + min := big.NewInt(2048) + adjust := new(big.Int).Div(parent.Difficulty, min) + if (block.Time - parent.Time) < 8 { + diff.Add(parent.Difficulty, adjust) } else { - diff.Add(parent.Difficulty(), adjust) + diff.Sub(parent.Difficulty, adjust) + } + + if diff.Cmp(GenesisDiff) < 0 { + return GenesisDiff } return diff @@ -58,7 +61,6 @@ func CalcGasLimit(parent, block *types.Block) *big.Int { } // ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024 - previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit()) current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed()), big.NewRat(6, 5)) curInt := new(big.Int).Div(current.Num(), current.Denom()) @@ -73,7 +75,8 @@ func CalcGasLimit(parent, block *types.Block) *big.Int { type ChainManager struct { //eth EthManager - db ethutil.Database + blockDb ethutil.Database + stateDb ethutil.Database processor types.BlockProcessor eventMux *event.TypeMux genesisBlock *types.Block @@ -86,13 +89,16 @@ type ChainManager struct { transState *state.StateDB txState *state.StateDB + + quit chan struct{} } -func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager { - bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux} +func NewChainManager(blockDb, stateDb ethutil.Database, mux *event.TypeMux) *ChainManager { + bc := &ChainManager{blockDb: blockDb, stateDb: stateDb, genesisBlock: GenesisBlock(stateDb), eventMux: mux, quit: make(chan struct{})} bc.setLastBlock() bc.transState = bc.State().Copy() bc.txState = bc.State().Copy() + go bc.update() return bc } @@ -122,7 +128,7 @@ func (self *ChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlo self.mu.RLock() defer self.mu.RUnlock() - return self.td, self.currentBlock.Hash(), self.Genesis().Hash() + return self.td, self.currentBlock.Hash(), self.genesisBlock.Hash() } func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { @@ -130,7 +136,7 @@ func (self *ChainManager) SetProcessor(proc types.BlockProcessor) { } func (self *ChainManager) State() *state.StateDB { - return state.New(self.CurrentBlock().Root(), self.db) + return state.New(self.CurrentBlock().Root(), self.stateDb) } func (self *ChainManager) TransState() *state.StateDB { @@ -158,7 +164,7 @@ func (self *ChainManager) setTransState(statedb *state.StateDB) { } func (bc *ChainManager) setLastBlock() { - data, _ := bc.db.Get([]byte("LastBlock")) + data, _ := bc.blockDb.Get([]byte("LastBlock")) if len(data) != 0 { var block types.Block rlp.Decode(bytes.NewReader(data), &block) @@ -166,7 +172,7 @@ func (bc *ChainManager) setLastBlock() { bc.lastBlockHash = block.Hash() // Set the last know difficulty (might be 0x0 as initial value, Genesis) - bc.td = ethutil.BigD(bc.db.LastKnownTD()) + bc.td = ethutil.BigD(bc.blockDb.LastKnownTD()) } else { bc.Reset() } @@ -192,7 +198,7 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block { coinbase, root, ethutil.BigPow(2, 32), - nil, + 0, "") block.SetUncles(nil) block.SetTransactions(nil) @@ -201,7 +207,7 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block { parent := bc.currentBlock if parent != nil { header := block.Header() - header.Difficulty = CalcDifficulty(block, parent) + header.Difficulty = CalcDifficulty(block.Header(), parent.Header()) header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1) header.GasLimit = CalcGasLimit(parent, block) @@ -215,7 +221,7 @@ func (bc *ChainManager) Reset() { defer bc.mu.Unlock() for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) { - bc.db.Delete(block.Hash()) + bc.blockDb.Delete(block.Hash()) } // Prepare the genesis block @@ -226,6 +232,21 @@ func (bc *ChainManager) Reset() { bc.setTotalDifficulty(ethutil.Big("0")) } +func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) { + bc.mu.Lock() + defer bc.mu.Unlock() + + for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) { + bc.blockDb.Delete(block.Hash()) + } + + // Prepare the genesis block + bc.genesisBlock = gb + bc.write(bc.genesisBlock) + bc.insert(bc.genesisBlock) + bc.currentBlock = bc.genesisBlock +} + func (self *ChainManager) Export() []byte { self.mu.RLock() defer self.mu.RUnlock() @@ -242,14 +263,14 @@ func (self *ChainManager) Export() []byte { func (bc *ChainManager) insert(block *types.Block) { encodedBlock := ethutil.Encode(block) - bc.db.Put([]byte("LastBlock"), encodedBlock) + bc.blockDb.Put([]byte("LastBlock"), encodedBlock) bc.currentBlock = block bc.lastBlockHash = block.Hash() } func (bc *ChainManager) write(block *types.Block) { encodedBlock := ethutil.Encode(block.RlpDataForStorage()) - bc.db.Put(block.Hash(), encodedBlock) + bc.blockDb.Put(block.Hash(), encodedBlock) } // Accessors @@ -259,7 +280,7 @@ func (bc *ChainManager) Genesis() *types.Block { // Block fetching methods func (bc *ChainManager) HasBlock(hash []byte) bool { - data, _ := bc.db.Get(hash) + data, _ := bc.blockDb.Get(hash) return len(data) != 0 } @@ -268,7 +289,6 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain if block == nil { return } - // XXX Could be optimised by using a different database which only holds hashes (i.e., linked list) for i := uint64(0); i < max; i++ { parentHash := block.Header().ParentHash @@ -288,7 +308,7 @@ func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain } func (self *ChainManager) GetBlock(hash []byte) *types.Block { - data, _ := self.db.Get(hash) + data, _ := self.blockDb.Get(hash) if len(data) == 0 { return nil } @@ -342,7 +362,7 @@ func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { } func (bc *ChainManager) setTotalDifficulty(td *big.Int) { - bc.db.Put([]byte("LTD"), td.Bytes()) + bc.blockDb.Put([]byte("LTD"), td.Bytes()) bc.td = td } @@ -367,16 +387,24 @@ func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) { } func (bc *ChainManager) Stop() { - if bc.CurrentBlock != nil { - chainlogger.Infoln("Stopped") - } + close(bc.quit) +} + +type queueEvent struct { + queue []interface{} + canonicalCount int + sideCount int + splitCount int } func (self *ChainManager) InsertChain(chain types.Blocks) error { - self.tsmu.Lock() - defer self.tsmu.Unlock() + //self.tsmu.Lock() + //defer self.tsmu.Unlock() - for _, block := range chain { + // A queued approach to delivering events. This is generally faster than direct delivery and requires much less mutex acquiring. + var queue = make([]interface{}, len(chain)) + var queueEvent = queueEvent{queue: queue} + for i, block := range chain { // 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, err := self.processor.Process(block) @@ -393,43 +421,84 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error { } block.Td = td - var canonical, split bool self.mu.Lock() + cblock := self.currentBlock { // 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) - 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 td.Cmp(self.td) > 0 { if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 { chainlogger.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, block.Hash()[:4], td, cblock.Header().Number, cblock.Hash()[:4], self.td) - split = true + + queue[i] = ChainSplitEvent{block} + queueEvent.splitCount++ } self.setTotalDifficulty(td) self.insert(block) - canonical = true + jsonlogger.LogJson(&logger.EthChainNewHead{ + BlockHash: ethutil.Bytes2Hex(block.Hash()), + BlockNumber: block.Number(), + ChainHeadHash: ethutil.Bytes2Hex(cblock.Hash()), + BlockPrevHash: ethutil.Bytes2Hex(block.ParentHash()), + }) + + self.setTransState(state.New(block.Root(), self.stateDb)) + queue[i] = ChainEvent{block} + queueEvent.canonicalCount++ + } else { + queue[i] = ChainSideEvent{block} + queueEvent.sideCount++ } } self.mu.Unlock() - if canonical { - self.setTransState(state.New(block.Root(), self.db)) - self.eventMux.Post(ChainEvent{block, td}) - } - - if split { - self.setTxState(state.New(block.Root(), self.db)) - self.eventMux.Post(ChainSplitEvent{block}) - } } + // XXX put this in a goroutine? + go self.eventMux.Post(queueEvent) + return nil } +func (self *ChainManager) update() { + events := self.eventMux.Subscribe(queueEvent{}) + +out: + for { + select { + case ev := <-events.Chan(): + switch ev := ev.(type) { + case queueEvent: + for i, event := range ev.queue { + switch event := event.(type) { + case ChainEvent: + // We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long + // and in most cases isn't even necessary. + if i == ev.canonicalCount { + self.eventMux.Post(ChainHeadEvent{event.Block}) + } + case ChainSplitEvent: + // On chain splits we need to reset the transaction state. We can't be sure whether the actual + // state of the accounts are still valid. + if i == ev.splitCount { + self.setTxState(state.New(event.Block.Root(), self.stateDb)) + } + } + + self.eventMux.Post(event) + } + } + case <-self.quit: + break out + } + } +} + // Satisfy state query interface func (self *ChainManager) GetAccount(addr []byte) *state.StateObject { return self.State().GetAccount(addr) |