From 1d42888d3047dabfb352c94a2051e7af14d2a509 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 16 Jun 2015 12:41:50 +0200 Subject: [PATCH] core/types: make blocks immutable --- core/block_cache_test.go | 6 +- core/block_processor.go | 95 +++---- core/block_processor_test.go | 16 +- core/chain_makers.go | 58 ++-- core/chain_manager.go | 94 ++----- core/chain_manager_test.go | 34 +-- core/genesis.go | 37 +-- core/state_transition.go | 9 +- core/transaction_pool.go | 2 +- core/types/block.go | 437 +++++++++++++----------------- core/vm_env.go | 34 +-- eth/downloader/downloader_test.go | 5 +- miner/agent.go | 6 +- miner/remote_agent.go | 4 +- miner/worker.go | 186 ++++++------- tests/block_test_util.go | 11 +- xeth/xeth.go | 10 +- 17 files changed, 457 insertions(+), 587 deletions(-) diff --git a/core/block_cache_test.go b/core/block_cache_test.go index 43ab847f9..80d118599 100644 --- a/core/block_cache_test.go +++ b/core/block_cache_test.go @@ -11,12 +11,12 @@ import ( func newChain(size int) (chain []*types.Block) { var parentHash common.Hash for i := 0; i < size; i++ { - block := types.NewBlock(parentHash, common.Address{}, common.Hash{}, new(big.Int), 0, nil) - block.Header().Number = big.NewInt(int64(i)) + head := &types.Header{ParentHash: parentHash, Number: big.NewInt(int64(i))} + block := types.NewBlock(head, nil, nil, nil) chain = append(chain, block) parentHash = block.Hash() } - return + return chain } func insertChainCache(cache *BlockCache, chain []*types.Block) { diff --git a/core/block_processor.go b/core/block_processor.go index e30ced312..2fd8c04c0 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -57,8 +57,8 @@ func NewBlockProcessor(db, extra common.Database, pow pow.PoW, chainManager *Cha } func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) { - coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase) - coinbase.SetGasLimit(block.Header().GasLimit) + coinbase := statedb.GetOrNewStateObject(block.Coinbase()) + coinbase.SetGasLimit(block.GasLimit()) // Process the transactions on to parent state receipts, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess) @@ -69,11 +69,11 @@ func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block return receipts, nil } -func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) { +func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) { // If we are mining this block and validating we want to set the logs back to 0 cb := statedb.GetStateObject(coinbase.Address()) - _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, block), tx, cb) + _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, cb) if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { return nil, nil, err } @@ -108,12 +108,13 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state totalUsedGas = big.NewInt(0) err error cumulativeSum = new(big.Int) + header = block.Header() ) for i, tx := range txs { statedb.StartRecord(tx.Hash(), block.Hash(), i) - receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess) + receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, header, tx, totalUsedGas, transientProcess) if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { return nil, err } @@ -142,11 +143,10 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err sm.mutex.Lock() defer sm.mutex.Unlock() - header := block.Header() - if !sm.bc.HasBlock(header.ParentHash) { - return nil, ParentError(header.ParentHash) + if !sm.bc.HasBlock(block.ParentHash()) { + return nil, ParentError(block.ParentHash()) } - parent := sm.bc.GetBlock(header.ParentHash) + parent := sm.bc.GetBlock(block.ParentHash()) // FIXME Change to full header validation. See #1225 errch := make(chan bool) @@ -168,30 +168,32 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, err erro sm.mutex.Lock() defer sm.mutex.Unlock() - header := block.Header() - if sm.bc.HasBlock(header.Hash()) { - return nil, &KnownBlockError{header.Number, header.Hash()} + if sm.bc.HasBlock(block.Hash()) { + return nil, &KnownBlockError{block.Number(), block.Hash()} } - if !sm.bc.HasBlock(header.ParentHash) { - return nil, ParentError(header.ParentHash) + if !sm.bc.HasBlock(block.ParentHash()) { + return nil, ParentError(block.ParentHash()) } - parent := sm.bc.GetBlock(header.ParentHash) + parent := sm.bc.GetBlock(block.ParentHash()) return sm.processWithParent(block, parent) } func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, err error) { // Create a new state based on the parent's root (e.g., create copy) state := state.New(parent.Root(), sm.db) + header := block.Header() + uncles := block.Uncles() + txs := block.Transactions() // Block validation - if err = ValidateHeader(sm.Pow, block.Header(), parent.Header(), false); err != nil { + if err = ValidateHeader(sm.Pow, header, parent.Header(), false); err != nil { return } // There can be at most two uncles - if len(block.Uncles()) > 2 { - return nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles())) + if len(uncles) > 2 { + return nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(uncles)) } receipts, err := sm.TransitionState(state, parent, block, false) @@ -199,8 +201,6 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st return } - header := block.Header() - // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. rbloom := types.CreateBloom(receipts) @@ -211,7 +211,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]])) // can be used by light clients to make sure they've received the correct Txs - txSha := types.DeriveSha(block.Transactions()) + txSha := types.DeriveSha(txs) if txSha != header.TxHash { err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha) return @@ -225,7 +225,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st } // Verify UncleHash before running other uncle validations - unclesSha := block.CalculateUnclesHash() + unclesSha := types.CalcUncleHash(uncles) if unclesSha != header.UncleHash { err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha) return @@ -236,7 +236,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st return } // Accumulate static rewards; block reward, uncle's and uncle inclusion. - AccumulateRewards(state, block) + AccumulateRewards(state, header, uncles) // Commit state objects/accounts to a temporary trie (does not save) // used to calculate the state root. @@ -260,11 +260,34 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st return state.Logs(), nil } +// AccumulateRewards credits the coinbase of the given block with the +// mining reward. The total reward consists of the static block reward +// and rewards for included uncles. +func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) { + reward := new(big.Int).Set(BlockReward) + + for _, uncle := range uncles { + num := new(big.Int).Add(big.NewInt(8), uncle.Number) + num.Sub(num, header.Number) + + r := new(big.Int) + r.Mul(BlockReward, num) + r.Div(r, big.NewInt(8)) + + statedb.AddBalance(uncle.Coinbase, r) + + reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32))) + } + + // Get the account associated with the coinbase + statedb.AddBalance(header.Coinbase, reward) +} + func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error { ancestors := set.New() uncles := set.New() ancestorHeaders := make(map[common.Hash]*types.Header) - for _, ancestor := range sm.bc.GetAncestors(block, 7) { + for _, ancestor := range sm.bc.GetBlocksFromHash(block.ParentHash(), 7) { ancestorHeaders[ancestor.Hash()] = ancestor.Header() ancestors.Add(ancestor.Hash()) // Include ancestors uncles in the uncle set. Uncles must be unique. @@ -325,7 +348,7 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro // TODO: remove backward compatibility var ( - parent = sm.bc.GetBlock(block.Header().ParentHash) + parent = sm.bc.GetBlock(block.ParentHash()) state = state.New(parent.Root(), sm.db) ) @@ -341,7 +364,7 @@ func ValidateHeader(pow pow.PoW, block, parent *types.Header, checkPow bool) err return fmt.Errorf("Block extra data too long (%d)", len(block.Extra)) } - expd := CalcDifficulty(block, parent) + expd := CalcDifficulty(int64(block.Time), int64(parent.Time), parent.Difficulty) if expd.Cmp(block.Difficulty) != 0 { return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) } @@ -375,26 +398,6 @@ func ValidateHeader(pow pow.PoW, block, parent *types.Header, checkPow bool) err return nil } -func AccumulateRewards(statedb *state.StateDB, block *types.Block) { - reward := new(big.Int).Set(BlockReward) - - for _, uncle := range block.Uncles() { - num := new(big.Int).Add(big.NewInt(8), uncle.Number) - num.Sub(num, block.Number()) - - r := new(big.Int) - r.Mul(BlockReward, num) - r.Div(r, big.NewInt(8)) - - statedb.AddBalance(uncle.Coinbase, r) - - reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32))) - } - - // Get the account associated with the coinbase - statedb.AddBalance(block.Header().Coinbase, reward) -} - func getBlockReceipts(db common.Database, bhash common.Hash) (receipts types.Receipts, err error) { var rdata []byte rdata, err = db.Get(append(receiptsPre, bhash[:]...)) diff --git a/core/block_processor_test.go b/core/block_processor_test.go index e38c815ef..bd77a2fc2 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -26,20 +26,18 @@ func proc() (*BlockProcessor, *ChainManager) { } func TestNumber(t *testing.T) { - _, chain := proc() - block1 := chain.NewBlock(common.Address{}) - block1.Header().Number = big.NewInt(3) - block1.Header().Time-- - pow := ezp.New() - err := ValidateHeader(pow, block1.Header(), chain.Genesis().Header(), false) + bp, chain := proc() + header := makeHeader(chain.Genesis(), 0, bp.db, 0) + header.Number = big.NewInt(3) + err := ValidateHeader(pow, header, chain.Genesis().Header(), false) if err != BlockNumberErr { - t.Errorf("expected block number error %v", err) + t.Errorf("expected block number error, got %q", err) } - block1 = chain.NewBlock(common.Address{}) - err = ValidateHeader(pow, block1.Header(), chain.Genesis().Header(), false) + header = makeHeader(chain.Genesis(), 0, bp.db, 0) + err = ValidateHeader(pow, header, chain.Genesis().Header(), false) if err == BlockNumberErr { t.Errorf("didn't expect block number error") } diff --git a/core/chain_makers.go b/core/chain_makers.go index 76acfd6ca..e704fd088 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -29,12 +29,8 @@ var ( // Utility functions for making chains on the fly // Exposed for sake of testing from other packages (eg. go-ethash) -func NewBlockFromParent(addr common.Address, parent *types.Block) *types.Block { - return newBlockFromParent(addr, parent) -} - func MakeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Database, seed int) *types.Block { - return makeBlock(bman, parent, i, db, seed) + return types.NewBlock(makeHeader(parent, i, db, seed), nil, nil, nil) } func MakeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks { @@ -53,46 +49,38 @@ func NewCanonical(n int, db common.Database) (*BlockProcessor, error) { return newCanonical(n, db) } -// block time is fixed at 10 seconds -func newBlockFromParent(addr common.Address, parent *types.Block) *types.Block { - block := types.NewBlock(parent.Hash(), addr, parent.Root(), common.BigPow(2, 32), 0, nil) - block.SetUncles(nil) - block.SetTransactions(nil) - block.SetReceipts(nil) - - header := block.Header() - header.Difficulty = CalcDifficulty(block.Header(), parent.Header()) - header.Number = new(big.Int).Add(parent.Header().Number, common.Big1) - header.Time = parent.Header().Time + 10 - header.GasLimit = CalcGasLimit(parent) - - block.Td = parent.Td - - return block -} - -// Actually make a block by simulating what miner would do -// we seed chains by the first byte of the coinbase -func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Database, seed int) *types.Block { +// makeHeader creates the header for a new empty block, simulating +// what miner would do. We seed chains by the first byte of the coinbase. +func makeHeader(parent *types.Block, i int, db common.Database, seed int) *types.Header { var addr common.Address - addr[0], addr[19] = byte(seed), byte(i) - block := newBlockFromParent(addr, parent) - state := state.New(block.Root(), db) + addr[0], addr[19] = byte(seed), byte(i) // 'random' coinbase + time := parent.Time() + 10 // block time is fixed at 10 seconds + + // ensure that the block's coinbase has the block reward in the state. + state := state.New(parent.Root(), db) cbase := state.GetOrNewStateObject(addr) cbase.SetGasLimit(CalcGasLimit(parent)) cbase.AddBalance(BlockReward) state.Update() - block.SetRoot(state.Root()) - return block + + return &types.Header{ + Root: state.Root(), + ParentHash: parent.Hash(), + Coinbase: addr, + Difficulty: CalcDifficulty(time, parent.Time(), parent.Difficulty()), + Number: new(big.Int).Add(parent.Number(), common.Big1), + Time: uint64(time), + GasLimit: CalcGasLimit(parent), + } } -// Make a chain with real blocks -// Runs ProcessWithParent to get proper state roots +// makeChain creates a valid chain of empty blocks. func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks { bman.bc.currentBlock = parent blocks := make(types.Blocks, max) for i := 0; i < max; i++ { - block := makeBlock(bman, parent, i, db, seed) + block := types.NewBlock(makeHeader(parent, i, db, seed), nil, nil, nil) + // Use ProcessWithParent to verify that we have produced a valid block. _, err := bman.processWithParent(block, parent) if err != nil { fmt.Println("process with parent failed", err) @@ -129,7 +117,7 @@ func newBlockProcessor(db common.Database, cman *ChainManager, eventMux *event.T } // Make a new, deterministic canonical chain by running InsertChain -// on result of makeChain +// on result of makeChain. func newCanonical(n int, db common.Database) (*BlockProcessor, error) { eventMux := &event.TypeMux{} diff --git a/core/chain_manager.go b/core/chain_manager.go index 3b9b7517b..8a79e3e8a 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -38,23 +38,24 @@ const ( maxTimeFutureBlocks = 30 ) -func CalcDifficulty(block, parent *types.Header) *big.Int { +// CalcDifficulty is the difficulty adjustment algorithm. It returns +// the difficulty that a new block b should have when created at time +// given the parent block's time and difficulty. +func CalcDifficulty(time int64, parentTime int64, parentDiff *big.Int) *big.Int { diff := new(big.Int) - - adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor) - if big.NewInt(int64(block.Time)-int64(parent.Time)).Cmp(params.DurationLimit) < 0 { - diff.Add(parent.Difficulty, adjust) + adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor) + if big.NewInt(time-parentTime).Cmp(params.DurationLimit) < 0 { + diff.Add(parentDiff, adjust) } else { - diff.Sub(parent.Difficulty, adjust) + diff.Sub(parentDiff, adjust) } - if diff.Cmp(params.MinimumDifficulty) < 0 { return params.MinimumDifficulty } - return diff } +// CalcTD computes the total difficulty of block. func CalcTD(block, parent *types.Block) *big.Int { if parent == nil { return block.Difficulty() @@ -62,6 +63,8 @@ func CalcTD(block, parent *types.Block) *big.Int { return new(big.Int).Add(parent.Td, block.Header().Difficulty) } +// CalcGasLimit computes the gas limit of the next block after parent. +// The result may be modified by the caller. func CalcGasLimit(parent *types.Block) *big.Int { decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor) contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3)) @@ -71,11 +74,11 @@ func CalcGasLimit(parent *types.Block) *big.Int { gl := new(big.Int).Sub(parent.GasLimit(), decay) gl = gl.Add(gl, contrib) gl = gl.Add(gl, big.NewInt(1)) - gl = common.BigMax(gl, params.MinGasLimit) + gl.Set(common.BigMax(gl, params.MinGasLimit)) if gl.Cmp(params.GenesisGasLimit) < 0 { - gl2 := new(big.Int).Add(parent.GasLimit(), decay) - return common.BigMin(params.GenesisGasLimit, gl2) + gl.Add(parent.GasLimit(), decay) + gl.Set(common.BigMin(gl, params.GenesisGasLimit)) } return gl } @@ -255,50 +258,11 @@ func (bc *ChainManager) makeCache() { 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 { + for _, block := range bc.GetBlocksFromHash(bc.currentBlock.Hash(), blockCacheLimit) { bc.cache.Push(block) } } -// Block creation & chain handling -func (bc *ChainManager) NewBlock(coinbase common.Address) *types.Block { - bc.mu.RLock() - defer bc.mu.RUnlock() - - var ( - root common.Hash - parentHash common.Hash - ) - - if bc.currentBlock != nil { - root = bc.currentBlock.Header().Root - parentHash = bc.lastBlockHash - } - - block := types.NewBlock( - parentHash, - coinbase, - root, - common.BigPow(2, 32), - 0, - nil) - block.SetUncles(nil) - block.SetTransactions(nil) - block.SetReceipts(nil) - - parent := bc.currentBlock - if parent != nil { - header := block.Header() - header.Difficulty = CalcDifficulty(block.Header(), parent.Header()) - header.Number = new(big.Int).Add(parent.Header().Number, common.Big1) - header.GasLimit = CalcGasLimit(parent) - } - - return block -} - func (bc *ChainManager) Reset() { bc.mu.Lock() defer bc.mu.Unlock() @@ -463,6 +427,19 @@ func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block { } +// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. +func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { + for i := 0; i < n; i++ { + block := self.GetBlock(hash) + if block == nil { + break + } + blocks = append(blocks, block) + hash = block.ParentHash() + } + return +} + // non blocking version func (self *ChainManager) getBlockByNumber(num uint64) *types.Block { key, _ := self.blockDb.Get(append(blockNumPre, big.NewInt(int64(num)).Bytes()...)) @@ -482,19 +459,6 @@ func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncl return } -func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks []*types.Block) { - for i := 0; i < length; i++ { - block = self.GetBlock(block.ParentHash()) - if block == nil { - break - } - - blocks = append(blocks, block) - } - - return -} - // setTotalDifficulty updates the TD of the chain manager. Note, this function // assumes that the `mu` mutex is held! func (bc *ChainManager) setTotalDifficulty(td *big.Int) { @@ -621,14 +585,12 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max) } - block.SetQueued(true) self.futureBlocks.Push(block) stats.queued++ continue } if IsParentErr(err) && self.futureBlocks.Has(block.ParentHash()) { - block.SetQueued(true) self.futureBlocks.Push(block) stats.queued++ continue diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index c56a3b3e1..afec1d0f1 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -117,7 +117,7 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) { } func loadChain(fn string, t *testing.T) (types.Blocks, error) { - fh, err := os.OpenFile(filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm) + fh, err := os.OpenFile(filepath.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm) if err != nil { return nil, err } @@ -265,7 +265,7 @@ func TestBrokenChain(t *testing.T) { } func TestChainInsertions(t *testing.T) { - t.Skip() // travil fails. + t.Skip("Skipped: outdated test files") db, _ := ethdb.NewMemDatabase() @@ -303,7 +303,7 @@ func TestChainInsertions(t *testing.T) { } func TestChainMultipleInsertions(t *testing.T) { - t.Skip() // travil fails. + t.Skip("Skipped: outdated test files") db, _ := ethdb.NewMemDatabase() @@ -346,8 +346,8 @@ func TestChainMultipleInsertions(t *testing.T) { } } -func TestGetAncestors(t *testing.T) { - t.Skip() // travil fails. +func TestGetBlocksFromHash(t *testing.T) { + t.Skip("Skipped: outdated test files") db, _ := ethdb.NewMemDatabase() chainMan := theChainManager(db, t) @@ -361,8 +361,8 @@ func TestGetAncestors(t *testing.T) { chainMan.write(block) } - ancestors := chainMan.GetAncestors(chain[len(chain)-1], 4) - fmt.Println(ancestors) + blocks := chainMan.GetBlocksFromHash(chain[len(chain)-1].Hash(), 4) + fmt.Println(blocks) } type bproc struct{} @@ -372,15 +372,17 @@ func (bproc) Process(*types.Block) (state.Logs, error) { return nil, nil } func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block { var chain []*types.Block for i, difficulty := range d { - header := &types.Header{Number: big.NewInt(int64(i + 1)), Difficulty: big.NewInt(int64(difficulty))} - block := types.NewBlockWithHeader(header) - copy(block.HeaderHash[:2], []byte{byte(i + 1), seed}) - if i == 0 { - block.ParentHeaderHash = genesis.Hash() - } else { - copy(block.ParentHeaderHash[:2], []byte{byte(i), seed}) + header := &types.Header{ + Coinbase: common.Address{seed}, + Number: big.NewInt(int64(i + 1)), + Difficulty: big.NewInt(int64(difficulty)), } - + if i == 0 { + header.ParentHash = genesis.Hash() + } else { + header.ParentHash = chain[i-1].Hash() + } + block := types.NewBlockWithHeader(header) chain = append(chain, block) } return chain @@ -399,7 +401,6 @@ func chm(genesis *types.Block, db common.Database) *ChainManager { } func TestReorgLongest(t *testing.T) { - t.Skip("skipped while cache is removed") db, _ := ethdb.NewMemDatabase() genesis := GenesisBlock(0, db) bc := chm(genesis, db) @@ -419,7 +420,6 @@ func TestReorgLongest(t *testing.T) { } func TestReorgShortest(t *testing.T) { - t.Skip("skipped while cache is removed") db, _ := ethdb.NewMemDatabase() genesis := GenesisBlock(0, db) bc := chm(genesis, db) diff --git a/core/genesis.go b/core/genesis.go index dd894e0b0..de2eee9db 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -11,38 +11,18 @@ import ( "github.com/ethereum/go-ethereum/params" ) -/* - * This is the special genesis block. - */ - -var ZeroHash256 = make([]byte, 32) -var ZeroHash160 = make([]byte, 20) -var ZeroHash512 = make([]byte, 64) - +// GenesisBlock creates a genesis block with the given nonce. func GenesisBlock(nonce uint64, db common.Database) *types.Block { - genesis := types.NewBlock(common.Hash{}, common.Address{}, common.Hash{}, params.GenesisDifficulty, nonce, nil) - genesis.Header().Number = common.Big0 - genesis.Header().GasLimit = params.GenesisGasLimit - genesis.Header().GasUsed = common.Big0 - genesis.Header().Time = 0 - - genesis.Td = common.Big0 - - genesis.SetUncles([]*types.Header{}) - genesis.SetTransactions(types.Transactions{}) - genesis.SetReceipts(types.Receipts{}) - var accounts map[string]struct { Balance string Code string } err := json.Unmarshal(GenesisAccounts, &accounts) if err != nil { - fmt.Println("enable to decode genesis json data:", err) + fmt.Println("unable to decode genesis json data:", err) os.Exit(1) } - - statedb := state.New(genesis.Root(), db) + statedb := state.New(common.Hash{}, db) for addr, account := range accounts { codedAddr := common.Hex2Bytes(addr) accountState := statedb.CreateAccount(common.BytesToAddress(codedAddr)) @@ -51,10 +31,15 @@ func GenesisBlock(nonce uint64, db common.Database) *types.Block { statedb.UpdateStateObject(accountState) } statedb.Sync() - genesis.Header().Root = statedb.Root() - genesis.Td = params.GenesisDifficulty - return genesis + block := types.NewBlock(&types.Header{ + Difficulty: params.GenesisDifficulty, + GasLimit: params.GenesisGasLimit, + Nonce: types.EncodeNonce(nonce), + Root: statedb.Root(), + }, nil, nil, nil) + block.Td = params.GenesisDifficulty + return block } var GenesisAccounts = []byte(`{ diff --git a/core/state_transition.go b/core/state_transition.go index 915dd466b..2c8770cbe 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -73,16 +73,17 @@ func MessageGasValue(msg Message) *big.Int { return new(big.Int).Mul(msg.Gas(), msg.GasPrice()) } -func IntrinsicGas(msg Message) *big.Int { +// IntrinsicGas computes the 'intrisic gas' for a message +// with the given data. +func IntrinsicGas(data []byte) *big.Int { igas := new(big.Int).Set(params.TxGas) - for _, byt := range msg.Data() { + for _, byt := range data { if byt != 0 { igas.Add(igas, params.TxDataNonZeroGas) } else { igas.Add(igas, params.TxDataZeroGas) } } - return igas } @@ -195,7 +196,7 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er sender, _ := self.From() // err checked in preCheck // Pay intrinsic gas - if err = self.UseGas(IntrinsicGas(self.msg)); err != nil { + if err = self.UseGas(IntrinsicGas(self.msg.Data())); err != nil { return nil, nil, InvalidTxError(err) } diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 45db04eef..bf28647c3 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -180,7 +180,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { } // Should supply enough intrinsic gas - if tx.Gas().Cmp(IntrinsicGas(tx)) < 0 { + if tx.Gas().Cmp(IntrinsicGas(tx.Data())) < 0 { return ErrIntrinsicGas } diff --git a/core/types/block.go b/core/types/block.go index d7963981e..31ea478d4 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -15,71 +15,59 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +// A BlockNonce is a 64-bit hash which proves (combined with the +// mix-hash) that a suffcient amount of computation has been carried +// out on a block. +type BlockNonce [8]byte + +func EncodeNonce(i uint64) BlockNonce { + var n BlockNonce + binary.BigEndian.PutUint64(n[:], i) + return n +} + +func (n BlockNonce) Uint64() uint64 { + return binary.BigEndian.Uint64(n[:]) +} + type Header struct { - // Hash to the previous block - ParentHash common.Hash - // Uncles of this block - UncleHash common.Hash - // The coin base address - Coinbase common.Address - // Block Trie state - Root common.Hash - // Tx sha - TxHash common.Hash - // Receipt sha - ReceiptHash common.Hash - // Bloom - Bloom Bloom - // Difficulty for the current block - Difficulty *big.Int - // The block number - Number *big.Int - // Gas limit - GasLimit *big.Int - // Gas used - GasUsed *big.Int - // Creation time - Time uint64 - // Extra data - Extra []byte - // Mix digest for quick checking to prevent DOS - MixDigest common.Hash - // Nonce - Nonce [8]byte + ParentHash common.Hash // Hash to the previous block + UncleHash common.Hash // Uncles of this block + Coinbase common.Address // The coin base address + Root common.Hash // Block Trie state + TxHash common.Hash // Tx sha + ReceiptHash common.Hash // Receipt sha + Bloom Bloom // Bloom + Difficulty *big.Int // Difficulty for the current block + Number *big.Int // The block number + GasLimit *big.Int // Gas limit + GasUsed *big.Int // Gas used + Time uint64 // Creation time + Extra []byte // Extra data + MixDigest common.Hash // for quick difficulty verification + Nonce BlockNonce } -func (self *Header) Hash() common.Hash { - return rlpHash(self.rlpData(true)) +func (h *Header) Hash() common.Hash { + return rlpHash(h) } -func (self *Header) HashNoNonce() common.Hash { - return rlpHash(self.rlpData(false)) -} - -func (self *Header) rlpData(withNonce bool) []interface{} { - fields := []interface{}{ - self.ParentHash, - self.UncleHash, - self.Coinbase, - self.Root, - self.TxHash, - self.ReceiptHash, - self.Bloom, - self.Difficulty, - self.Number, - self.GasLimit, - self.GasUsed, - self.Time, - self.Extra, - } - if withNonce { - fields = append(fields, self.MixDigest, self.Nonce) - } - return fields -} - -func (self *Header) RlpData() interface{} { - return self.rlpData(true) +func (h *Header) HashNoNonce() common.Hash { + return rlpHash([]interface{}{ + h.ParentHash, + h.UncleHash, + h.Coinbase, + h.Root, + h.TxHash, + h.ReceiptHash, + h.Bloom, + h.Difficulty, + h.Number, + h.GasLimit, + h.GasUsed, + h.Time, + h.Extra, + }) } func (h *Header) UnmarshalJSON(data []byte) error { @@ -112,20 +100,14 @@ func rlpHash(x interface{}) (h common.Hash) { } type Block struct { - // Preset Hash for mock (Tests) - HeaderHash common.Hash - ParentHeaderHash common.Hash - // ^^^^ ignore ^^^^ - header *Header uncles []*Header transactions Transactions - Td *big.Int - queued bool // flag for blockpool to skip TD check + receipts Receipts + Td *big.Int + queued bool // flag for blockpool to skip TD check ReceivedAt time.Time - - receipts Receipts } // StorageBlock defines the RLP encoding of a Block stored in the @@ -148,43 +130,90 @@ type storageblock struct { TD *big.Int } -func NewBlock(parentHash common.Hash, coinbase common.Address, root common.Hash, difficulty *big.Int, nonce uint64, extra []byte) *Block { - header := &Header{ - Root: root, - ParentHash: parentHash, - Coinbase: coinbase, - Difficulty: difficulty, - Time: uint64(time.Now().Unix()), - Extra: extra, - GasUsed: new(big.Int), - GasLimit: new(big.Int), - Number: new(big.Int), +var ( + emptyRootHash = DeriveSha(Transactions{}) + emptyUncleHash = CalcUncleHash(nil) +) + +// NewBlock creates a new block. The input data is copied, +// changes to header and to the field values will not affect the +// block. +// +// The values of TxHash, UncleHash, ReceiptHash and Bloom in header +// are ignored and set to values derived from the given txs, uncles +// and receipts. +func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { + b := &Block{header: copyHeader(header), Td: new(big.Int)} + + // TODO: panic if len(txs) != len(receipts) + if len(txs) == 0 { + b.header.TxHash = emptyRootHash + } else { + b.header.TxHash = DeriveSha(Transactions(txs)) + b.transactions = make(Transactions, len(txs)) + copy(b.transactions, txs) } - header.SetNonce(nonce) - block := &Block{header: header} - block.Td = new(big.Int) - return block -} - -func (self *Header) SetNonce(nonce uint64) { - binary.BigEndian.PutUint64(self.Nonce[:], nonce) + if len(receipts) == 0 { + b.header.ReceiptHash = emptyRootHash + } else { + b.header.ReceiptHash = DeriveSha(Receipts(receipts)) + b.header.Bloom = CreateBloom(receipts) + b.receipts = make([]*Receipt, len(receipts)) + copy(b.receipts, receipts) + } + + if len(uncles) == 0 { + b.header.UncleHash = emptyUncleHash + } else { + b.header.UncleHash = CalcUncleHash(uncles) + b.uncles = make([]*Header, len(uncles)) + for i := range uncles { + b.uncles[i] = copyHeader(uncles[i]) + } + } + + return b } +// NewBlockWithHeader creates a block with the given header data. The +// header data is copied, changes to header and to the field values +// will not affect the block. func NewBlockWithHeader(header *Header) *Block { - return &Block{header: header} + return &Block{header: copyHeader(header)} } -func (self *Block) ValidateFields() error { - if self.header == nil { +func copyHeader(h *Header) *Header { + cpy := *h + if cpy.Difficulty = new(big.Int); h.Difficulty != nil { + cpy.Difficulty.Set(h.Difficulty) + } + if cpy.Number = new(big.Int); h.Number != nil { + cpy.Number.Set(h.Number) + } + if cpy.GasLimit = new(big.Int); h.GasLimit != nil { + cpy.GasLimit.Set(h.GasLimit) + } + if cpy.GasUsed = new(big.Int); h.GasUsed != nil { + cpy.GasUsed.Set(h.GasUsed) + } + if len(h.Extra) > 0 { + cpy.Extra = make([]byte, len(h.Extra)) + copy(cpy.Extra, h.Extra) + } + return &cpy +} + +func (b *Block) ValidateFields() error { + if b.header == nil { return fmt.Errorf("header is nil") } - for i, transaction := range self.transactions { + for i, transaction := range b.transactions { if transaction == nil { return fmt.Errorf("transaction %d is nil", i) } } - for i, uncle := range self.uncles { + for i, uncle := range b.uncles { if uncle == nil { return fmt.Errorf("uncle %d is nil", i) } @@ -192,64 +221,48 @@ func (self *Block) ValidateFields() error { return nil } -func (self *Block) DecodeRLP(s *rlp.Stream) error { +func (b *Block) DecodeRLP(s *rlp.Stream) error { var eb extblock if err := s.Decode(&eb); err != nil { return err } - self.header, self.uncles, self.transactions = eb.Header, eb.Uncles, eb.Txs + b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs return nil } -func (self Block) EncodeRLP(w io.Writer) error { +func (b Block) EncodeRLP(w io.Writer) error { return rlp.Encode(w, extblock{ - Header: self.header, - Txs: self.transactions, - Uncles: self.uncles, + Header: b.header, + Txs: b.transactions, + Uncles: b.uncles, }) } -func (self *StorageBlock) DecodeRLP(s *rlp.Stream) error { +func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error { var sb storageblock if err := s.Decode(&sb); err != nil { return err } - self.header, self.uncles, self.transactions, self.Td = sb.Header, sb.Uncles, sb.Txs, sb.TD + b.header, b.uncles, b.transactions, b.Td = sb.Header, sb.Uncles, sb.Txs, sb.TD return nil } -func (self StorageBlock) EncodeRLP(w io.Writer) error { +func (b StorageBlock) EncodeRLP(w io.Writer) error { return rlp.Encode(w, storageblock{ - Header: self.header, - Txs: self.transactions, - Uncles: self.uncles, - TD: self.Td, + Header: b.header, + Txs: b.transactions, + Uncles: b.uncles, + TD: b.Td, }) } -func (self *Block) Header() *Header { - return self.header -} +// TODO: copies +func (b *Block) Uncles() []*Header { return b.uncles } +func (b *Block) Transactions() Transactions { return b.transactions } +func (b *Block) Receipts() Receipts { return b.receipts } -func (self *Block) Uncles() []*Header { - return self.uncles -} - -func (self *Block) CalculateUnclesHash() common.Hash { - return rlpHash(self.uncles) -} - -func (self *Block) SetUncles(uncleHeaders []*Header) { - self.uncles = uncleHeaders - self.header.UncleHash = rlpHash(uncleHeaders) -} - -func (self *Block) Transactions() Transactions { - return self.transactions -} - -func (self *Block) Transaction(hash common.Hash) *Transaction { - for _, transaction := range self.transactions { +func (b *Block) Transaction(hash common.Hash) *Transaction { + for _, transaction := range b.transactions { if transaction.Hash() == hash { return transaction } @@ -257,74 +270,33 @@ func (self *Block) Transaction(hash common.Hash) *Transaction { return nil } -func (self *Block) SetTransactions(transactions Transactions) { - self.transactions = transactions - self.header.TxHash = DeriveSha(transactions) -} -func (self *Block) AddTransaction(transaction *Transaction) { - self.transactions = append(self.transactions, transaction) - self.SetTransactions(self.transactions) +func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) } +func (b *Block) GasLimit() *big.Int { return new(big.Int).Set(b.header.GasLimit) } +func (b *Block) GasUsed() *big.Int { return new(big.Int).Set(b.header.GasUsed) } +func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) } + +func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } +func (b *Block) MixDigest() common.Hash { return b.header.MixDigest } +func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) } +func (b *Block) Bloom() Bloom { return b.header.Bloom } +func (b *Block) Coinbase() common.Address { return b.header.Coinbase } +func (b *Block) Time() int64 { return int64(b.header.Time) } +func (b *Block) Root() common.Hash { return b.header.Root } +func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } +func (b *Block) TxHash() common.Hash { return b.header.TxHash } +func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } +func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } +func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } + +func (b *Block) Header() *Header { return copyHeader(b.header) } + +func (b *Block) HashNoNonce() common.Hash { + return b.header.HashNoNonce() } -func (self *Block) Receipts() Receipts { - return self.receipts -} - -func (self *Block) SetReceipts(receipts Receipts) { - self.receipts = receipts - self.header.ReceiptHash = DeriveSha(receipts) - self.header.Bloom = CreateBloom(receipts) -} -func (self *Block) AddReceipt(receipt *Receipt) { - self.receipts = append(self.receipts, receipt) - self.SetReceipts(self.receipts) -} - -func (self *Block) RlpData() interface{} { - return []interface{}{self.header, self.transactions, self.uncles} -} - -func (self *Block) RlpDataForStorage() interface{} { - return []interface{}{self.header, self.transactions, self.uncles, self.Td /* TODO receipts */} -} - -// Header accessors (add as you need them) -func (self *Block) Number() *big.Int { return self.header.Number } -func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() } -func (self *Block) MixDigest() common.Hash { return self.header.MixDigest } -func (self *Block) Nonce() uint64 { - return binary.BigEndian.Uint64(self.header.Nonce[:]) -} -func (self *Block) SetNonce(nonce uint64) { - self.header.SetNonce(nonce) -} - -func (self *Block) Queued() bool { return self.queued } -func (self *Block) SetQueued(q bool) { self.queued = q } - -func (self *Block) Bloom() Bloom { return self.header.Bloom } -func (self *Block) Coinbase() common.Address { return self.header.Coinbase } -func (self *Block) Time() int64 { return int64(self.header.Time) } -func (self *Block) GasLimit() *big.Int { return self.header.GasLimit } -func (self *Block) GasUsed() *big.Int { return self.header.GasUsed } -func (self *Block) Root() common.Hash { return self.header.Root } -func (self *Block) SetRoot(root common.Hash) { self.header.Root = root } -func (self *Block) GetTransaction(i int) *Transaction { - if len(self.transactions) > i { - return self.transactions[i] - } - return nil -} -func (self *Block) GetUncle(i int) *Header { - if len(self.uncles) > i { - return self.uncles[i] - } - return nil -} - -func (self *Block) Size() common.StorageSize { +func (b *Block) Size() common.StorageSize { c := writeCounter(0) - rlp.Encode(&c, self) + rlp.Encode(&c, b) return common.StorageSize(c) } @@ -335,48 +307,32 @@ func (c *writeCounter) Write(b []byte) (int, error) { return len(b), nil } +func CalcUncleHash(uncles []*Header) common.Hash { + return rlpHash(uncles) +} + +// WithMiningResult returns a new block with the data from b +// where nonce and mix digest are set to the provided values. +func (b *Block) WithMiningResult(nonce uint64, mixDigest common.Hash) *Block { + cpy := *b.header + binary.BigEndian.PutUint64(cpy.Nonce[:], nonce) + cpy.MixDigest = mixDigest + return &Block{ + header: &cpy, + transactions: b.transactions, + receipts: b.receipts, + uncles: b.uncles, + Td: b.Td, + } +} + // Implement pow.Block -func (self *Block) Difficulty() *big.Int { return self.header.Difficulty } -func (self *Block) HashNoNonce() common.Hash { return self.header.HashNoNonce() } -func (self *Block) Hash() common.Hash { - if (self.HeaderHash != common.Hash{}) { - return self.HeaderHash - } else { - return self.header.Hash() - } +func (b *Block) Hash() common.Hash { + return b.header.Hash() } -func (self *Block) ParentHash() common.Hash { - if (self.ParentHeaderHash != common.Hash{}) { - return self.ParentHeaderHash - } else { - return self.header.ParentHash - } -} - -func (self *Block) Copy() *Block { - block := NewBlock(self.header.ParentHash, self.Coinbase(), self.Root(), new(big.Int), self.Nonce(), self.header.Extra) - block.header.Bloom = self.header.Bloom - block.header.TxHash = self.header.TxHash - block.transactions = self.transactions - block.header.UncleHash = self.header.UncleHash - block.uncles = self.uncles - block.header.GasLimit.Set(self.header.GasLimit) - block.header.GasUsed.Set(self.header.GasUsed) - block.header.ReceiptHash = self.header.ReceiptHash - block.header.Difficulty.Set(self.header.Difficulty) - block.header.Number.Set(self.header.Number) - block.header.Time = self.header.Time - block.header.MixDigest = self.header.MixDigest - if self.Td != nil { - block.Td.Set(self.Td) - } - - return block -} - -func (self *Block) String() string { +func (b *Block) String() string { str := fmt.Sprintf(`Block(#%v): Size: %v TD: %v { MinerHash: %x %v @@ -385,20 +341,11 @@ Transactions: Uncles: %v } -`, self.Number(), self.Size(), self.Td, self.header.HashNoNonce(), self.header, self.transactions, self.uncles) - - if (self.HeaderHash != common.Hash{}) { - str += fmt.Sprintf("\nFake hash = %x", self.HeaderHash) - } - - if (self.ParentHeaderHash != common.Hash{}) { - str += fmt.Sprintf("\nFake parent hash = %x", self.ParentHeaderHash) - } - +`, b.Number(), b.Size(), b.Td, b.header.HashNoNonce(), b.header, b.transactions, b.uncles) return str } -func (self *Header) String() string { +func (h *Header) String() string { return fmt.Sprintf(`Header(%x): [ ParentHash: %x @@ -414,9 +361,9 @@ func (self *Header) String() string { GasUsed: %v Time: %v Extra: %s - MixDigest: %x + MixDigest: %x Nonce: %x -]`, self.Hash(), self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.MixDigest, self.Nonce) +]`, h.Hash(), h.ParentHash, h.UncleHash, h.Coinbase, h.Root, h.TxHash, h.ReceiptHash, h.Bloom, h.Difficulty, h.Number, h.GasLimit, h.GasUsed, h.Time, h.Extra, h.MixDigest, h.Nonce) } type Blocks []*Block @@ -442,4 +389,4 @@ func (self blockSorter) Swap(i, j int) { } func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } -func Number(b1, b2 *Block) bool { return b1.Header().Number.Cmp(b2.Header().Number) < 0 } +func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 } diff --git a/core/vm_env.go b/core/vm_env.go index da862d5c8..6dd83acde 100644 --- a/core/vm_env.go +++ b/core/vm_env.go @@ -10,32 +10,32 @@ import ( ) type VMEnv struct { - state *state.StateDB - block *types.Block - msg Message - depth int - chain *ChainManager - typ vm.Type + state *state.StateDB + header *types.Header + msg Message + depth int + chain *ChainManager + typ vm.Type // structured logging logs []vm.StructLog } -func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, block *types.Block) *VMEnv { +func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, header *types.Header) *VMEnv { return &VMEnv{ - chain: chain, - state: state, - block: block, - msg: msg, - typ: vm.StdVmTy, + chain: chain, + state: state, + header: header, + msg: msg, + typ: vm.StdVmTy, } } func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f } -func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() } -func (self *VMEnv) Coinbase() common.Address { return self.block.Coinbase() } -func (self *VMEnv) Time() int64 { return self.block.Time() } -func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() } -func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() } +func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number } +func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } +func (self *VMEnv) Time() int64 { return int64(self.header.Time) } +func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } +func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } func (self *VMEnv) Value() *big.Int { return self.msg.Value() } func (self *VMEnv) State() *state.StateDB { return self.state } func (self *VMEnv) Depth() int { return self.depth } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 4fc4e1434..fb8aa7e4b 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -40,7 +40,10 @@ func createHashes(amount int, root common.Hash) (hashes []common.Hash) { // createBlock assembles a new block at the given chain height. func createBlock(i int, parent, hash common.Hash) *types.Block { - header := &types.Header{Number: big.NewInt(int64(i))} + header := &types.Header{ + Hash: hash, + Number: big.NewInt(int64(i)) + } block := types.NewBlockWithHeader(header) block.HeaderHash = hash block.ParentHeaderHash = parent diff --git a/miner/agent.go b/miner/agent.go index 3ed3ba839..a7d017aa5 100644 --- a/miner/agent.go +++ b/miner/agent.go @@ -90,15 +90,13 @@ done: } } -func (self *CpuAgent) mine(block *types.Block, stop <- chan struct{}) { +func (self *CpuAgent) mine(block *types.Block, stop <-chan struct{}) { glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index) // Mine nonce, mixDigest := self.pow.Search(block, stop) if nonce != 0 { - block.SetNonce(nonce) - block.Header().MixDigest = common.BytesToHash(mixDigest) - self.returnCh <- block + self.returnCh <- block.WithMiningResult(nonce, common.BytesToHash(mixDigest)) } else { self.returnCh <- nil } diff --git a/miner/remote_agent.go b/miner/remote_agent.go index 80cc9053e..6a44782f6 100644 --- a/miner/remote_agent.go +++ b/miner/remote_agent.go @@ -81,9 +81,7 @@ func (a *RemoteAgent) SubmitWork(nonce uint64, mixDigest, seedHash common.Hash) // Make sure the external miner was working on the right hash if a.currentWork != nil && a.work != nil { - a.currentWork.SetNonce(nonce) - a.currentWork.Header().MixDigest = mixDigest - a.returnCh <- a.currentWork + a.returnCh <- a.currentWork.WithMiningResult(nonce, mixDigest) //a.returnCh <- Work{a.currentWork.Number().Uint64(), nonce, mixDigest.Bytes(), seedHash.Bytes()} return true } diff --git a/miner/worker.go b/miner/worker.go index 55c23376c..3a2d7a41b 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -49,10 +49,8 @@ type uint64RingBuffer struct { // environment is the workers current environment and holds // all of the current state information type environment struct { - totalUsedGas *big.Int // total gas usage in the cycle state *state.StateDB // apply state changes here coinbase *state.StateObject // the miner's account - block *types.Block // the new block ancestors *set.Set // ancestor set (used for checking uncle parent validity) family *set.Set // family set (used for checking uncle invalidity) uncles *set.Set // uncle set @@ -63,22 +61,12 @@ type environment struct { ownedAccounts *set.Set lowGasTxs types.Transactions localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion) -} -// env returns a new environment for the current cycle -func env(block *types.Block, eth core.Backend) *environment { - state := state.New(block.Root(), eth.StateDb()) - env := &environment{ - totalUsedGas: new(big.Int), - state: state, - block: block, - ancestors: set.New(), - family: set.New(), - uncles: set.New(), - coinbase: state.GetOrNewStateObject(block.Coinbase()), - } + block *types.Block // the new block - return env + header *types.Header + txs []*types.Transaction + receipts []*types.Receipt } // worker is the main object which takes care of applying messages to the new state @@ -137,14 +125,20 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker { func (self *worker) pendingState() *state.StateDB { self.currentMu.Lock() defer self.currentMu.Unlock() - return self.current.state } func (self *worker) pendingBlock() *types.Block { self.currentMu.Lock() defer self.currentMu.Unlock() - + if atomic.LoadInt32(&self.mining) == 0 { + return types.NewBlock( + self.current.header, + self.current.txs, + nil, + self.current.receipts, + ) + } return self.current.block } @@ -206,7 +200,7 @@ out: // Apply transaction to the pending state if we're not mining if atomic.LoadInt32(&self.mining) == 0 { self.mu.Lock() - self.commitTransactions(types.Transactions{ev.Tx}) + self.current.commitTransactions(types.Transactions{ev.Tx}, self.gasPrice, self.proc) self.mu.Unlock() } } @@ -259,8 +253,8 @@ func (self *worker) wait() { jsonlogger.LogJson(&logger.EthMinerNewBlock{ BlockHash: block.Hash().Hex(), BlockNumber: block.Number(), - ChainHeadHash: block.ParentHeaderHash.Hex(), - BlockPrevHash: block.ParentHeaderHash.Hex(), + ChainHeadHash: block.ParentHash().Hex(), + BlockPrevHash: block.ParentHash().Hex(), }) } else { self.commitNewWork() @@ -271,14 +265,12 @@ func (self *worker) wait() { func (self *worker) push() { if atomic.LoadInt32(&self.mining) == 1 { - self.current.block.SetRoot(self.current.state.Root()) - // push new work to agents for _, agent := range self.agents { atomic.AddInt32(&self.atWork, 1) if agent.Work() != nil { - agent.Work() <- self.current.block.Copy() + agent.Work() <- self.current.block } else { common.Report(fmt.Sprintf("%v %T\n", agent, agent)) } @@ -286,22 +278,20 @@ func (self *worker) push() { } } -func (self *worker) makeCurrent() { - block := self.chain.NewBlock(self.coinbase) - parent := self.chain.GetBlock(block.ParentHash()) - // TMP fix for build server ... - if parent == nil { - return +// makeCurrent creates a new environment for the current cycle. +func (self *worker) makeCurrent(parent *types.Block, header *types.Header) { + state := state.New(parent.Root(), self.eth.StateDb()) + current := &environment{ + state: state, + ancestors: set.New(), + family: set.New(), + uncles: set.New(), + header: header, + coinbase: state.GetOrNewStateObject(self.coinbase), } - if block.Time() <= parent.Time() { - block.Header().Time = parent.Header().Time + 1 - } - block.Header().Extra = self.extra - // when 08 is processed ancestors contain 07 (quick block) - current := env(block, self.eth) - for _, ancestor := range self.chain.GetAncestors(block, 7) { + for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { for _, uncle := range ancestor.Uncles() { current.family.Add(uncle.Hash()) } @@ -309,6 +299,7 @@ func (self *worker) makeCurrent() { current.ancestors.Add(ancestor.Hash()) } accounts, _ := self.eth.AccountManager().Accounts() + // Keep track of transactions which return errors so they can be removed current.remove = set.New() current.tcount = 0 @@ -318,9 +309,6 @@ func (self *worker) makeCurrent() { if self.current != nil { current.localMinedBlocks = self.current.localMinedBlocks } - - current.coinbase.SetGasLimit(core.CalcGasLimit(parent)) - self.current = current } @@ -352,13 +340,13 @@ func (self *worker) isBlockLocallyMined(deepBlockNum uint64) bool { //Does the block at {deepBlockNum} send earnings to my coinbase? var block = self.chain.GetBlockByNumber(deepBlockNum) - return block != nil && block.Header().Coinbase == self.coinbase + return block != nil && block.Coinbase() == self.coinbase } func (self *worker) logLocalMinedBlocks(previous *environment) { if previous != nil && self.current.localMinedBlocks != nil { - nextBlockNum := self.current.block.Number().Uint64() - for checkBlockNum := previous.block.Number().Uint64(); checkBlockNum < nextBlockNum; checkBlockNum++ { + nextBlockNum := self.current.block.NumberU64() + for checkBlockNum := previous.block.NumberU64(); checkBlockNum < nextBlockNum; checkBlockNum++ { inspectBlockNum := checkBlockNum - miningLogAtDepth if self.isBlockLocallyMined(inspectBlockNum) { glog.V(logger.Info).Infof("🔨 🔗 Mined %d blocks back: block #%v", miningLogAtDepth, inspectBlockNum) @@ -376,18 +364,35 @@ func (self *worker) commitNewWork() { defer self.currentMu.Unlock() tstart := time.Now() + parent := self.chain.CurrentBlock() + tstamp := tstart.Unix() + if tstamp <= parent.Time() { + tstamp = parent.Time() + 1 + } + num := parent.Number() + header := &types.Header{ + ParentHash: parent.Hash(), + Number: num.Add(num, common.Big1), + Difficulty: core.CalcDifficulty(tstamp, parent.Time(), parent.Difficulty()), + GasLimit: core.CalcGasLimit(parent), + GasUsed: new(big.Int), + Coinbase: self.coinbase, + Extra: self.extra, + Time: uint64(tstamp), + } previous := self.current - self.makeCurrent() + self.makeCurrent(parent, header) current := self.current + // commit transactions for this run. transactions := self.eth.TxPool().GetTransactions() sort.Sort(types.TxByNonce{transactions}) - - // commit transactions for this run - self.commitTransactions(transactions) + current.coinbase.SetGasLimit(header.GasLimit) + current.commitTransactions(transactions, self.gasPrice, self.proc) self.eth.TxPool().RemoveTransactions(current.lowGasTxs) + // compute uncles for the new block. var ( uncles []*types.Header badUncles []common.Hash @@ -396,88 +401,76 @@ func (self *worker) commitNewWork() { if len(uncles) == 2 { break } - if err := self.commitUncle(uncle.Header()); err != nil { if glog.V(logger.Ridiculousness) { glog.V(logger.Detail).Infof("Bad uncle found and will be removed (%x)\n", hash[:4]) glog.V(logger.Detail).Infoln(uncle) } - badUncles = append(badUncles, hash) } else { glog.V(logger.Debug).Infof("commiting %x as uncle\n", hash[:4]) uncles = append(uncles, uncle.Header()) } } + for _, hash := range badUncles { + delete(self.possibleUncles, hash) + } - // We only care about logging if we're actually mining + // commit state root after all state transitions. + core.AccumulateRewards(self.current.state, header, uncles) + current.state.Update() + header.Root = current.state.Root() + + // create the new block whose nonce will be mined. + current.block = types.NewBlock(header, current.txs, uncles, current.receipts) + + // We only care about logging if we're actually mining. if atomic.LoadInt32(&self.mining) == 1 { glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles. Took %v\n", current.block.Number(), current.tcount, len(uncles), time.Since(tstart)) self.logLocalMinedBlocks(previous) } - for _, hash := range badUncles { - delete(self.possibleUncles, hash) - } - - self.current.block.SetUncles(uncles) - - core.AccumulateRewards(self.current.state, self.current.block) - - self.current.state.Update() - self.push() } -var ( - inclusionReward = new(big.Int).Div(core.BlockReward, big.NewInt(32)) - _uncleReward = new(big.Int).Mul(core.BlockReward, big.NewInt(15)) - uncleReward = new(big.Int).Div(_uncleReward, big.NewInt(16)) -) - func (self *worker) commitUncle(uncle *types.Header) error { - if self.current.uncles.Has(uncle.Hash()) { - // Error not unique + hash := uncle.Hash() + if self.current.uncles.Has(hash) { return core.UncleError("Uncle not unique") } - if !self.current.ancestors.Has(uncle.ParentHash) { return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) } - - if self.current.family.Has(uncle.Hash()) { - return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", uncle.Hash())) + if self.current.family.Has(hash) { + return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash)) } self.current.uncles.Add(uncle.Hash()) - return nil } -func (self *worker) commitTransactions(transactions types.Transactions) { - current := self.current - +func (env *environment) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) { for _, tx := range transactions { // We can skip err. It has already been validated in the tx pool from, _ := tx.From() // Check if it falls within margin. Txs from owned accounts are always processed. - if tx.GasPrice().Cmp(self.gasPrice) < 0 && !current.ownedAccounts.Has(from) { + if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { // ignore the transaction and transactor. We ignore the transactor // because nonce will fail after ignoring this transaction so there's // no point - current.lowGasTransactors.Add(from) + env.lowGasTransactors.Add(from) - glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(self.gasPrice), from[:4]) + glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4]) } // Continue with the next transaction if the transaction sender is included in // the low gas tx set. This will also remove the tx and all sequential transaction // from this transactor - if current.lowGasTransactors.Has(from) { + if env.lowGasTransactors.Has(from) { // add tx to the low gas set. This will be removed at the end of the run // owned accounts are ignored - if !current.ownedAccounts.Has(from) { - current.lowGasTxs = append(current.lowGasTxs, tx) + if !env.ownedAccounts.Has(from) { + env.lowGasTxs = append(env.lowGasTxs, tx) } continue } @@ -487,46 +480,41 @@ func (self *worker) commitTransactions(transactions types.Transactions) { // the transaction is processed (that could potentially be included in the block) it // will throw a nonce error because the previous transaction hasn't been processed. // Therefor we need to ignore any transaction after the ignored one. - if current.ignoredTransactors.Has(from) { + if env.ignoredTransactors.Has(from) { continue } - self.current.state.StartRecord(tx.Hash(), common.Hash{}, 0) + env.state.StartRecord(tx.Hash(), common.Hash{}, 0) - err := self.commitTransaction(tx) + err := env.commitTransaction(tx, proc) switch { case core.IsNonceErr(err) || core.IsInvalidTxErr(err): - current.remove.Add(tx.Hash()) + env.remove.Add(tx.Hash()) if glog.V(logger.Detail) { glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) } case state.IsGasLimitErr(err): - from, _ := tx.From() // ignore the transactor so no nonce errors will be thrown for this account // next time the worker is run, they'll be picked up again. - current.ignoredTransactors.Add(from) + env.ignoredTransactors.Add(from) glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) default: - current.tcount++ + env.tcount++ } } - - self.current.block.Header().GasUsed = self.current.totalUsedGas } -func (self *worker) commitTransaction(tx *types.Transaction) error { - snap := self.current.state.Copy() - receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true) +func (env *environment) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor) error { + snap := env.state.Copy() + receipt, _, err := proc.ApplyTransaction(env.coinbase, env.state, env.header, tx, env.header.GasUsed, true) if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err) || core.IsInvalidTxErr(err)) { - self.current.state.Set(snap) + env.state.Set(snap) return err } - - self.current.block.AddTransaction(tx) - self.current.block.AddReceipt(receipt) - + env.txs = append(env.txs, tx) + env.receipts = append(env.receipts, receipt) return nil } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 5fdc6402e..450ef86a1 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -245,7 +245,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error { if b.BlockHeader == nil { continue // OK - block is supposed to be invalid, continue with next block } else { - return fmt.Errorf("Block RLP decoding failed when expected to succeed: ", err) + return fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err) } } // RLP decoding worked, try to insert into chain: @@ -254,7 +254,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error { if b.BlockHeader == nil { continue // OK - block is supposed to be invalid, continue with next block } else { - return fmt.Errorf("Block insertion into chain failed: ", err) + return fmt.Errorf("Block insertion into chain failed: %v", err) } } if b.BlockHeader == nil { @@ -262,7 +262,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error { } err = t.validateBlockHeader(b.BlockHeader, cb.Header()) if err != nil { - return fmt.Errorf("Block header validation failed: ", err) + return fmt.Errorf("Block header validation failed: %v", err) } } return nil @@ -286,7 +286,7 @@ func (s *BlockTest) validateBlockHeader(h *btHeader, h2 *types.Header) error { expectedNonce := mustConvertBytes(h.Nonce) if !bytes.Equal(expectedNonce, h2.Nonce[:]) { - return fmt.Errorf("Nonce: expected: %v, decoded: %v", expectedNonce, h2.Nonce[:]) + return fmt.Errorf("Nonce: expected: %v, decoded: %v", expectedNonce, h2.Nonce) } expectedNumber := mustConvertBigInt(h.Number, 16) @@ -423,9 +423,8 @@ func mustConvertHeader(in btHeader) *types.Header { GasLimit: mustConvertBigInt(in.GasLimit, 16), Difficulty: mustConvertBigInt(in.Difficulty, 16), Time: mustConvertUint(in.Timestamp, 16), + Nonce: types.EncodeNonce(mustConvertUint(in.Nonce, 16)), } - // XXX cheats? :-) - header.SetNonce(mustConvertUint(in.Nonce, 16)) return header } diff --git a/xeth/xeth.go b/xeth/xeth.go index 11d30ebac..0dbedff43 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -209,8 +209,8 @@ func (self *XEth) AtStateNum(num int64) *XEth { // - could be removed in favour of mining on testdag (natspec e2e + networking) // + filters func (self *XEth) ApplyTestTxs(statedb *state.StateDB, address common.Address, txc uint64) (uint64, *XEth) { - - block := self.backend.ChainManager().NewBlock(address) + chain := self.backend.ChainManager() + header := chain.CurrentBlock().Header() coinbase := statedb.GetStateObject(address) coinbase.SetGasLimit(big.NewInt(10000000)) txs := self.backend.TxPool().GetQueuedTransactions() @@ -218,7 +218,7 @@ func (self *XEth) ApplyTestTxs(statedb *state.StateDB, address common.Address, t for i := 0; i < len(txs); i++ { for _, tx := range txs { if tx.Nonce() == txc { - _, _, err := core.ApplyMessage(core.NewEnv(statedb, self.backend.ChainManager(), tx, block), tx, coinbase) + _, _, err := core.ApplyMessage(core.NewEnv(statedb, self.backend.ChainManager(), tx, header), tx, coinbase) if err != nil { panic(err) } @@ -845,8 +845,8 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st msg.gasPrice = self.DefaultGasPrice() } - block := self.CurrentBlock() - vmenv := core.NewEnv(statedb, self.backend.ChainManager(), msg, block) + header := self.CurrentBlock().Header() + vmenv := core.NewEnv(statedb, self.backend.ChainManager(), msg, header) res, gas, err := core.ApplyMessage(vmenv, msg, from) return common.ToHex(res), gas.String(), err