go-ethereum/chain/chain_manager.go
obscuren f8d0cd9906 Added a callback mechanism to chain adding.
Not sure if this is the right approach. Why? BlockChain shouldn't need
the "Ethereum" object. BlockChain shouldn't need to worry about
notifying listeners or message propagation.
2014-11-18 19:44:17 +01:00

369 lines
8.8 KiB
Go

package chain
import (
"bytes"
"container/list"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
)
var chainlogger = logger.NewLogger("CHAIN")
func AddTestNetFunds(block *types.Block) {
for _, addr := range []string{
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
"e4157b34ea9615cfbde6b4fda419828124b70c78",
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
"6c386a4b26f73c802f34673f7248bb118f97424a",
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
"e6716f9544a56c530d868e4bfbacb172315bdead",
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
} {
codedAddr := ethutil.Hex2Bytes(addr)
account := block.State().GetAccount(codedAddr)
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
block.State().UpdateStateObject(account)
}
}
func CalcDifficulty(block, parent *types.Block) *big.Int {
diff := new(big.Int)
adjust := new(big.Int).Rsh(parent.Difficulty, 10)
if block.Time >= parent.Time+5 {
diff.Sub(parent.Difficulty, adjust)
} else {
diff.Add(parent.Difficulty, adjust)
}
return diff
}
type ChainManager struct {
//eth EthManager
processor types.BlockProcessor
genesisBlock *types.Block
// Last known total difficulty
TD *big.Int
LastBlockNumber uint64
CurrentBlock *types.Block
LastBlockHash []byte
workingChain *BlockChain
}
func NewChainManager() *ChainManager {
bc := &ChainManager{}
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
//bc.eth = ethereum
bc.setLastBlock()
return bc
}
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
self.processor = proc
}
func (bc *ChainManager) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 {
// Prep genesis
AddTestNetFunds(bc.genesisBlock)
block := types.NewBlockFromBytes(data)
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
bc.LastBlockNumber = block.Number.Uint64()
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
} else {
bc.Reset()
}
chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash())
}
// Block creation & chain handling
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
var root interface{}
hash := ZeroHash256
if bc.CurrentBlock != nil {
root = bc.CurrentBlock.Root()
hash = bc.LastBlockHash
}
block := types.CreateBlock(
root,
hash,
coinbase,
ethutil.BigPow(2, 32),
nil,
"")
block.MinGasPrice = big.NewInt(10000000000000)
parent := bc.CurrentBlock
if parent != nil {
block.Difficulty = CalcDifficulty(block, parent)
block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1)
block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
}
return block
}
func (bc *ChainManager) Reset() {
AddTestNetFunds(bc.genesisBlock)
bc.genesisBlock.Trie().Sync()
// Prepare the genesis block
bc.add(bc.genesisBlock)
bc.CurrentBlock = bc.genesisBlock
bc.SetTotalDifficulty(ethutil.Big("0"))
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
}
// Add a block to the chain and record addition information
func (bc *ChainManager) add(block *types.Block) {
bc.writeBlockInfo(block)
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
encodedBlock := block.RlpEncode()
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
//chainlogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4])
}
// Accessors
func (bc *ChainManager) Genesis() *types.Block {
return bc.genesisBlock
}
// Block fetching methods
func (bc *ChainManager) HasBlock(hash []byte) bool {
data, _ := ethutil.Config.Db.Get(hash)
return len(data) != 0
}
func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
block := self.GetBlock(hash)
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++ {
chain = append(chain, block.Hash())
if block.Number.Cmp(ethutil.Big0) <= 0 {
break
}
block = self.GetBlock(block.PrevHash)
}
return
}
func (self *ChainManager) GetBlock(hash []byte) *types.Block {
data, _ := ethutil.Config.Db.Get(hash)
if len(data) == 0 {
if self.workingChain != nil {
// Check the temp chain
for e := self.workingChain.Front(); e != nil; e = e.Next() {
if bytes.Compare(e.Value.(*link).block.Hash(), hash) == 0 {
return e.Value.(*link).block
}
}
}
return nil
}
return types.NewBlockFromBytes(data)
}
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
block := self.CurrentBlock
for ; block != nil; block = self.GetBlock(block.PrevHash) {
if block.Number.Uint64() == num {
break
}
}
if block != nil && block.Number.Uint64() == 0 && num != 0 {
return nil
}
return block
}
func (bc *ChainManager) SetTotalDifficulty(td *big.Int) {
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
bc.TD = td
}
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
parent := self.GetBlock(block.PrevHash)
if parent == nil {
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash)
}
parentTd := parent.BlockInfo().TD
uncleDiff := new(big.Int)
for _, uncle := range block.Uncles {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
}
td := new(big.Int)
td = td.Add(parentTd, uncleDiff)
td = td.Add(td, block.Difficulty)
return td, nil
}
func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo {
bi := types.BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
bi.RlpDecode(data)
return bi
}
// Unexported method for writing extra non-essential block info to the db
func (bc *ChainManager) writeBlockInfo(block *types.Block) {
bc.LastBlockNumber++
bi := types.BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.TD}
// For now we use the block hash with the words "info" appended as key
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
}
func (bc *ChainManager) Stop() {
if bc.CurrentBlock != nil {
chainlogger.Infoln("Stopped")
}
}
func (self *ChainManager) NewIterator(startHash []byte) *ChainIterator {
return &ChainIterator{self, self.GetBlock(startHash)}
}
// This function assumes you've done your checking. No checking is done at this stage anymore
func (self *ChainManager) InsertChain(chain *BlockChain, call func(*types.Block, state.Messages)) {
for e := chain.Front(); e != nil; e = e.Next() {
link := e.Value.(*link)
self.add(link.block)
self.SetTotalDifficulty(link.td)
call(link.block, link.messages)
}
b, e := chain.Front(), chain.Back()
if b != nil && e != nil {
front, back := b.Value.(*link).block, e.Value.(*link).block
chainlogger.Infof("Imported %d blocks. #%v (%x) / %#v (%x)", chain.Len(), front.Number, front.Hash()[0:4], back.Number, back.Hash()[0:4])
}
}
func (self *ChainManager) TestChain(chain *BlockChain) (td *big.Int, err error) {
self.workingChain = chain
defer func() { self.workingChain = nil }()
for e := chain.Front(); e != nil; e = e.Next() {
var (
l = e.Value.(*link)
block = l.block
parent = self.GetBlock(block.PrevHash)
)
if parent == nil {
err = fmt.Errorf("incoming chain broken on hash %x\n", block.PrevHash[0:4])
return
}
var messages state.Messages
td, messages, err = self.processor.ProcessWithParent(block, parent) //self.eth.BlockManager().ProcessWithParent(block, parent)
if err != nil {
chainlogger.Infoln(err)
chainlogger.Debugf("Block #%v failed (%x...)\n", block.Number, block.Hash()[0:4])
chainlogger.Debugln(block)
err = fmt.Errorf("incoming chain failed %v\n", err)
return
}
l.td = td
l.messages = messages
}
if td.Cmp(self.TD) <= 0 {
err = &TDError{td, self.TD}
return
}
self.workingChain = nil
return
}
type link struct {
block *types.Block
messages state.Messages
td *big.Int
}
type BlockChain struct {
*list.List
}
func NewChain(blocks types.Blocks) *BlockChain {
chain := &BlockChain{list.New()}
for _, block := range blocks {
chain.PushBack(&link{block, nil, nil})
}
return chain
}
func (self *BlockChain) RlpEncode() []byte {
dat := make([]interface{}, 0)
for e := self.Front(); e != nil; e = e.Next() {
dat = append(dat, e.Value.(*link).block.RlpData())
}
return ethutil.Encode(dat)
}
type ChainIterator struct {
cm *ChainManager
block *types.Block // current block in the iterator
}
func (self *ChainIterator) Prev() *types.Block {
self.block = self.cm.GetBlock(self.block.PrevHash)
return self.block
}