good morning!!!!

Skip to content
Snippets Groups Projects
chain_manager.go 8.41 KiB
Newer Older
  • Learn to ignore specific revisions
  • package core
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    import (
    
    	"bytes"
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	"fmt"
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	"sync"
    
    	"github.com/ethereum/go-ethereum/core/types"
    
    	"github.com/ethereum/go-ethereum/ethutil"
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	"github.com/ethereum/go-ethereum/event"
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	"github.com/ethereum/go-ethereum/logger"
    
    	"github.com/ethereum/go-ethereum/rlp"
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	"github.com/ethereum/go-ethereum/state"
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    )
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    var chainlogger = logger.NewLogger("CHAIN")
    
    Viktor Trón's avatar
    Viktor Trón committed
    
    
    type StateQuery interface {
    	GetAccount(addr []byte) *state.StateObject
    }
    
    
    func CalcDifficulty(block, parent *types.Block) *big.Int {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	diff := new(big.Int)
    
    
    	bh, ph := block.Header(), parent.Header()
    	adjust := new(big.Int).Rsh(ph.Difficulty, 10)
    
    	if bh.Time >= ph.Time+13 {
    
    		diff.Sub(ph.Difficulty, adjust)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	} else {
    
    		diff.Add(ph.Difficulty, adjust)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	}
    
    	return diff
    }
    
    
    func CalcGasLimit(parent, block *types.Block) *big.Int {
    	if block.Number().Cmp(big.NewInt(0)) == 0 {
    		return ethutil.BigPow(10, 6)
    	}
    
    	// ((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())
    
    	result := new(big.Int).Add(previous, curInt)
    	result.Div(result, big.NewInt(1024))
    
    	min := big.NewInt(125000)
    
    	return ethutil.BigMax(min, result)
    }
    
    
    type ChainManager struct {
    
    	//eth          EthManager
    	processor    types.BlockProcessor
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	eventMux     *event.TypeMux
    
    	genesisBlock *types.Block
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	// Last known total difficulty
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	mu              sync.RWMutex
    	td              *big.Int
    	lastBlockNumber uint64
    	currentBlock    *types.Block
    	lastBlockHash   []byte
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	transState *state.StateDB
    }
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    func (self *ChainManager) Td() *big.Int {
    	self.mu.RLock()
    	defer self.mu.RUnlock()
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	return self.td
    }
    
    func (self *ChainManager) LastBlockNumber() uint64 {
    	self.mu.RLock()
    	defer self.mu.RUnlock()
    
    	return self.lastBlockNumber
    }
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    func (self *ChainManager) LastBlockHash() []byte {
    	self.mu.RLock()
    	defer self.mu.RUnlock()
    
    	return self.lastBlockHash
    }
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    func (self *ChainManager) CurrentBlock() *types.Block {
    	self.mu.RLock()
    	defer self.mu.RUnlock()
    
    	return self.currentBlock
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    }
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    func NewChainManager(mux *event.TypeMux) *ChainManager {
    
    	bc := &ChainManager{}
    
    	bc.genesisBlock = GenesisBlock()
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	bc.eventMux = mux
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    	bc.setLastBlock()
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	bc.transState = bc.State().Copy()
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	return bc
    }
    
    
    func (self *ChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	self.mu.RLock()
    	defer self.mu.RUnlock()
    
    	return self.td, self.currentBlock.Hash(), self.Genesis().Hash()
    
    func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
    	self.processor = proc
    }
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    func (self *ChainManager) State() *state.StateDB {
    
    	return state.New(self.CurrentBlock().Trie())
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    }
    
    func (self *ChainManager) TransState() *state.StateDB {
    	return self.transState
    }
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    func (bc *ChainManager) setLastBlock() {
    	data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
    	if len(data) != 0 {
    
    		var block types.Block
    		rlp.Decode(bytes.NewReader(data), &block)
    		bc.currentBlock = &block
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		bc.lastBlockHash = block.Hash()
    
    		bc.lastBlockNumber = block.Header().Number.Uint64()
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    		// Set the last know difficulty (might be 0x0 as initial value, Genesis)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	} else {
    		bc.Reset()
    	}
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	chainlogger.Infof("Last block (#%d) %x\n", bc.lastBlockNumber, bc.currentBlock.Hash())
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    }
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    // Block creation & chain handling
    
    func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	bc.mu.RLock()
    	defer bc.mu.RUnlock()
    
    
    	var root []byte
    	parentHash := ZeroHash256
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    	if bc.CurrentBlock != nil {
    
    		root = bc.currentBlock.Header().Root
    		parentHash = bc.lastBlockHash
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	}
    
    	block := types.NewBlock(
    		parentHash,
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		coinbase,
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		ethutil.BigPow(2, 32),
    		nil,
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		"")
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	parent := bc.currentBlock
    
    	if parent != nil {
    
    		header := block.Header()
    		header.Difficulty = CalcDifficulty(block, parent)
    		header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1)
    		header.GasLimit = CalcGasLimit(parent, block)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	}
    
    	return block
    }
    
    
    func (bc *ChainManager) Reset() {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	bc.mu.Lock()
    	defer bc.mu.Unlock()
    
    
    	for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
    		ethutil.Config.Db.Delete(block.Hash())
    	}
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	// Prepare the genesis block
    
    	bc.write(bc.genesisBlock)
    	bc.insert(bc.genesisBlock)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	bc.currentBlock = bc.genesisBlock
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	bc.setTotalDifficulty(ethutil.Big("0"))
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    func (self *ChainManager) Export() []byte {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	self.mu.RLock()
    	defer self.mu.RUnlock()
    
    
    	chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Header().Number)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    
    	blocks := make([]*types.Block, int(self.currentBlock.NumberU64())+1)
    	for block := self.currentBlock; block != nil; block = self.GetBlock(block.Header().ParentHash) {
    		blocks[block.NumberU64()] = block
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	}
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	return ethutil.Encode(blocks)
    }
    
    
    func (bc *ChainManager) insert(block *types.Block) {
    
    	encodedBlock := ethutil.Encode(block)
    
    	ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	bc.currentBlock = block
    	bc.lastBlockHash = block.Hash()
    
    }
    
    func (bc *ChainManager) write(block *types.Block) {
    	bc.writeBlockInfo(block)
    
    	encodedBlock := ethutil.Encode(block)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	ethutil.Config.Db.Put(block.Hash(), encodedBlock)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    // Accessors
    
    func (bc *ChainManager) Genesis() *types.Block {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	return bc.genesisBlock
    }
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    // Block fetching methods
    func (bc *ChainManager) HasBlock(hash []byte) bool {
    	data, _ := ethutil.Config.Db.Get(hash)
    	return len(data) != 0
    }
    
    
    func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	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.Header().Number.Cmp(ethutil.Big0) <= 0 {
    
    		block = self.GetBlock(block.Header().ParentHash)
    
    func (self *ChainManager) GetBlock(hash []byte) *types.Block {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	data, _ := ethutil.Config.Db.Get(hash)
    
    	if len(data) == 0 {
    		return nil
    	}
    
    	var block types.Block
    	if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
    		fmt.Println(err)
    		return nil
    	}
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    
    	return &block
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    }
    
    
    func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	self.mu.RLock()
    	defer self.mu.RUnlock()
    
    
    	var block *types.Block
    
    	if num <= self.currentBlock.Number().Uint64() {
    		block = self.currentBlock
    		for ; block != nil; block = self.GetBlock(block.Header().ParentHash) {
    			if block.Header().Number.Uint64() == num {
    				break
    			}
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		}
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	bc.td = td
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    }
    
    func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
    
    	parent := self.GetBlock(block.Header().ParentHash)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	if parent == nil {
    
    		return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.Header().ParentHash)
    
    	parentTd := parent.Td
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	uncleDiff := new(big.Int)
    
    	for _, uncle := range block.Uncles() {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
    	}
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	td := new(big.Int)
    	td = td.Add(parentTd, uncleDiff)
    
    	td = td.Add(td, block.Header().Difficulty)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    
    	return td, nil
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    }
    
    // Unexported method for writing extra non-essential block info to the db
    
    func (bc *ChainManager) writeBlockInfo(block *types.Block) {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	bc.lastBlockNumber++
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    }
    
    
    func (bc *ChainManager) Stop() {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	if bc.CurrentBlock != nil {
    
    Viktor Trón's avatar
    Viktor Trón committed
    		chainlogger.Infoln("Stopped")
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    	}
    }
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    func (self *ChainManager) InsertChain(chain types.Blocks) error {
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		td, messages, err := self.processor.Process(block)
    
    		if err != nil {
    
    			if IsKnownBlockErr(err) {
    				continue
    			}
    
    			h := block.Header()
    			chainlogger.Infof("block #%v process failed (%x)\n", h.Number, h.Hash()[:4])
    
    			chainlogger.Infoln(block)
    			chainlogger.Infoln(err)
    
    		block.Td = td
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		self.mu.Lock()
    		{
    			self.write(block)
    
    			cblock := self.currentBlock
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    			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), was #%v (%x)\n", block.Header().Number, block.Hash()[:4], cblock.Header().Number, cblock.Hash()[:4])
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    				}
    
    				self.setTotalDifficulty(td)
    				self.insert(block)
    
    				self.transState = state.New(cblock.Trie().Copy())
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		self.mu.Unlock()
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    		self.eventMux.Post(NewBlockEvent{block})
    		self.eventMux.Post(messages)
    
    Jeffrey Wilcke's avatar
    Jeffrey Wilcke committed
    }
    
    
    // Satisfy state query interface
    func (self *ChainManager) GetAccount(addr []byte) *state.StateObject {
    	return self.State().GetAccount(addr)
    }