diff --git a/consensus/consensus_engine.go b/consensus/consensus_engine.go
new file mode 100644
index 000000000..d14b8fb67
--- /dev/null
+++ b/consensus/consensus_engine.go
@@ -0,0 +1,91 @@
+package consensus
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// ChainReader defines a small collection of methods needed to access the local
+// blockchain during header and/or uncle verification.
+type ChainReader interface {
+ // Config retrieves the blockchain's chain configuration.
+ Config() *params.ChainConfig
+
+ // CurrentHeader retrieves the current header from the local chain.
+ CurrentHeader() *types.Header
+
+ // GetHeader retrieves a block header from the database by hash and number.
+ GetHeader(hash common.Hash, number uint64) *types.Header
+
+ // GetHeaderByNumber retrieves a block header from the database by number.
+ GetHeaderByNumber(number uint64) *types.Header
+
+ // GetHeaderByHash retrieves a block header from the database by its hash.
+ GetHeaderByHash(hash common.Hash) *types.Header
+
+ // GetBlock retrieves a block from the database by hash and number.
+ GetBlock(hash common.Hash, number uint64) *types.Block
+}
+
+// Engine is an algorithm agnostic consensus engine.
+type Engine interface {
+ // Author retrieves the Ethereum address of the account that minted the given
+ // block, which may be different from the header's coinbase if a consensus
+ // engine is based on signatures.
+ Author(header *types.Header) (common.Address, error)
+
+ // VerifyHeader checks whether a header conforms to the consensus rules of a
+ // given engine. Verifying the seal may be done optionally here, or explicitly
+ // via the VerifySeal method.
+ VerifyHeader(chain ChainReader, header *types.Header, seal bool) error
+
+ // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
+ // concurrently. The method returns a quit channel to abort the operations and
+ // a results channel to retrieve the async verifications (the order is that of
+ // the input slice).
+ VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
+
+ // VerifyUncles verifies that the given block's uncles conform to the consensus
+ // rules of a given engine.
+ VerifyUncles(chain ChainReader, block *types.Block) error
+
+ // VerifySeal checks whether the crypto seal on a header is valid according to
+ // the consensus rules of the given engine.
+ VerifySeal(chain ChainReader, header *types.Header) error
+
+ // Prepare initializes the consensus fields of a block header according to the
+ // rules of a particular engine. The changes are executed inline.
+ Prepare(chain ChainReader, header *types.Header) error
+
+ // Finalize runs any post-transaction state modifications (e.g. block rewards)
+ // and assembles the final block.
+ // Note: The block header and state database might be updated to reflect any
+ // consensus rules that happen at finalization (e.g. block rewards).
+ Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
+ uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
+
+ // Seal generates a new sealing request for the given input block and pushes
+ // the result into the given channel.
+ //
+ // Note, the method returns immediately and will send the result async. More
+ // than one result may also be returned depending on the consensus algorithm.
+ Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
+
+ // SealHash returns the hash of a block prior to it being sealed.
+ SealHash(header *types.Header) common.Hash
+
+ // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
+ // that a new block should have.
+ CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int
+
+ // APIs returns the RPC APIs this consensus engine provides.
+ APIs(chain ChainReader) []rpc.API
+
+ // Close terminates any background threads maintained by the consensus engine.
+ Close() error
+}
diff --git a/consensus/errors.go b/consensus/errors.go
new file mode 100644
index 000000000..a005c5f63
--- /dev/null
+++ b/consensus/errors.go
@@ -0,0 +1,37 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package consensus
+
+import "errors"
+
+var (
+ // ErrUnknownAncestor is returned when validating a block requires an ancestor
+ // that is unknown.
+ ErrUnknownAncestor = errors.New("unknown ancestor")
+
+ // ErrPrunedAncestor is returned when validating a block requires an ancestor
+ // that is known, but the state of which is not available.
+ ErrPrunedAncestor = errors.New("pruned ancestor")
+
+ // ErrFutureBlock is returned when a block's timestamp is in the future according
+ // to the current node.
+ ErrFutureBlock = errors.New("block in the future")
+
+ // ErrInvalidNumber is returned if a block's number doesn't equal it's parent's
+ // plus one.
+ ErrInvalidNumber = errors.New("invalid block number")
+)
diff --git a/core/block_validator.go b/core/block_validator.go
new file mode 100644
index 000000000..b35834504
--- /dev/null
+++ b/core/block_validator.go
@@ -0,0 +1,139 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// BlockValidator is responsible for validating block headers, uncles and
+// processed state.
+//
+// BlockValidator implements Validator.
+type BlockValidator struct {
+ config *params.ChainConfig // Chain configuration options
+ bc *BlockChain // Canonical block chain
+ engine consensus.Engine // Consensus engine used for validating
+}
+
+// NewBlockValidator returns a new block validator which is safe for re-use
+func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine) *BlockValidator {
+ validator := &BlockValidator{
+ config: config,
+ engine: engine,
+ bc: blockchain,
+ }
+ return validator
+}
+
+// ValidateBody validates the given block's uncles and verifies the block
+// header's transaction and uncle roots. The headers are assumed to be already
+// validated at this point.
+func (v *BlockValidator) ValidateBody(block *types.Block) error {
+ // Check whether the block's known, and if not, that it's linkable
+ if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
+ return ErrKnownBlock
+ }
+ if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
+ if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
+ return consensus.ErrUnknownAncestor
+ }
+ return consensus.ErrPrunedAncestor
+ }
+ // Header validity is known at this point, check the uncles and transactions
+ header := block.Header()
+ //if err := v.engine.VerifyUncles(v.bc, block); err != nil {
+ // return err
+ //}
+ if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
+ return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash)
+ }
+ if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash {
+ return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
+ }
+ return nil
+}
+
+// ValidateState validates the various changes that happen after a state
+// transition, such as amount of used gas, the receipt roots and the state root
+// itself. ValidateState returns a database batch if the validation was a success
+// otherwise nil and an error is returned.
+func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
+ header := block.Header()
+ if block.GasUsed() != usedGas {
+ return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
+ }
+ // 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)
+ if rbloom != header.Bloom {
+ return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
+ }
+ // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
+ receiptSha := types.DeriveSha(receipts)
+ if receiptSha != header.ReceiptHash {
+ return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
+ }
+ // Validate the state root against the received state root and throw
+ // an error if they don't match.
+ if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
+ return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root)
+ }
+ return nil
+}
+
+// CalcGasLimit computes the gas limit of the next block after parent. It aims
+// to keep the baseline gas above the provided floor, and increase it towards the
+// ceil if the blocks are full. If the ceil is exceeded, it will always decrease
+// the gas allowance.
+func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
+ // contrib = (parentGasUsed * 3 / 2) / 1024
+ contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor
+
+ // decay = parentGasLimit / 1024 -1
+ decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1
+
+ /*
+ strategy: gasLimit of block-to-mine is set based on parent's
+ gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we
+ increase it, otherwise lower it (or leave it unchanged if it's right
+ at that usage) the amount increased/decreased depends on how far away
+ from parentGasLimit * (2/3) parentGasUsed is.
+ */
+ limit := parent.GasLimit() - decay + contrib
+ if limit < params.MinGasLimit {
+ limit = params.MinGasLimit
+ }
+ // If we're outside our allowed gas range, we try to hone towards them
+ if limit < gasFloor {
+ limit = parent.GasLimit() + decay
+ if limit > gasFloor {
+ limit = gasFloor
+ }
+ } else if limit > gasCeil {
+ limit = parent.GasLimit() - decay
+ if limit < gasCeil {
+ limit = gasCeil
+ }
+ }
+ return limit
+}
diff --git a/core/blockchain.go b/core/blockchain.go
new file mode 100644
index 000000000..c9517ace8
--- /dev/null
+++ b/core/blockchain.go
@@ -0,0 +1,1269 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package core implements the Ethereum consensus protocol.
+package core
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ mrand "math/rand"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/mclock"
+ "github.com/ethereum/go-ethereum/common/prque"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
+ "github.com/hashicorp/golang-lru"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/rawdb"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+var (
+ blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
+
+ ErrNoGenesis = errors.New("Genesis not found in chain")
+)
+
+const (
+ bodyCacheLimit = 256
+ blockCacheLimit = 256
+ receiptsCacheLimit = 32
+ maxFutureBlocks = 256
+ maxTimeFutureBlocks = 30
+ badBlockLimit = 10
+ triesInMemory = 128
+
+ // BlockChainVersion ensures that an incompatible database forces a resync from scratch.
+ BlockChainVersion = 3
+)
+
+// CacheConfig contains the configuration values for the trie caching/pruning
+// that's resident in a blockchain.
+type CacheConfig struct {
+ Disabled bool // Whether to disable trie write caching (archive node)
+ TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk
+ TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
+}
+
+// BlockChain represents the canonical chain given a database with a genesis
+// block. The Blockchain manages chain imports, reverts, chain reorganisations.
+//
+// Importing blocks in to the block chain happens according to the set of rules
+// defined by the two stage Validator. Processing of blocks is done using the
+// Processor which processes the included transaction. The validation of the state
+// is done in the second part of the Validator. Failing results in aborting of
+// the import.
+//
+// The BlockChain also helps in returning blocks from **any** chain included
+// in the database as well as blocks that represents the canonical chain. It's
+// important to note that GetBlock can return any block and does not need to be
+// included in the canonical one where as GetBlockByNumber always represents the
+// canonical chain.
+type BlockChain struct {
+ chainConfig *params.ChainConfig // Chain & network configuration
+ cacheConfig *CacheConfig // Cache configuration for pruning
+
+ db ethdb.Database // Low level persistent database to store final content in
+ triegc *prque.Prque // Priority queue mapping block numbers to tries to gc
+ gcproc time.Duration // Accumulates canonical block processing for trie dumping
+
+ hc *HeaderChain
+ rmLogsFeed event.Feed
+ chainFeed event.Feed
+ chainSideFeed event.Feed
+ chainHeadFeed event.Feed
+ logsFeed event.Feed
+ scope event.SubscriptionScope
+ genesisBlock *types.Block
+
+ mu sync.RWMutex // global mutex for locking chain operations
+ chainmu sync.RWMutex // blockchain insertion lock
+ procmu sync.RWMutex // block processor lock
+
+ checkpoint int // checkpoint counts towards the new checkpoint
+ currentBlock atomic.Value // Current head of the block chain
+ currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
+
+ stateCache state.Database // State database to reuse between imports (contains state cache)
+ bodyCache *lru.Cache // Cache for the most recent block bodies
+ bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
+ receiptsCache *lru.Cache // Cache for the most recent receipts per block
+ blockCache *lru.Cache // Cache for the most recent entire blocks
+ futureBlocks *lru.Cache // future blocks are blocks added for later processing
+
+ quit chan struct{} // blockchain quit channel
+ running int32 // running must be called atomically
+ // procInterrupt must be atomically called
+ procInterrupt int32 // interrupt signaler for block processing
+ wg sync.WaitGroup // chain processing wait group for shutting down
+
+ engine consensus.Engine
+ processor Processor // block processor interface
+ validator Validator // block and state validator interface
+ vmConfig vm.Config
+
+ badBlocks *lru.Cache // Bad block cache
+ shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
+}
+
+// NewBlockChain returns a fully initialised block chain using information
+// available in the database. It initialises the default Ethereum Validator and
+// Processor.
+func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool) (*BlockChain, error) {
+ if cacheConfig == nil {
+ cacheConfig = &CacheConfig{
+ TrieNodeLimit: 256 * 1024 * 1024,
+ TrieTimeLimit: 5 * time.Minute,
+ }
+ }
+ bodyCache, _ := lru.New(bodyCacheLimit)
+ bodyRLPCache, _ := lru.New(bodyCacheLimit)
+ receiptsCache, _ := lru.New(receiptsCacheLimit)
+ blockCache, _ := lru.New(blockCacheLimit)
+ futureBlocks, _ := lru.New(maxFutureBlocks)
+ badBlocks, _ := lru.New(badBlockLimit)
+
+ bc := &BlockChain{
+ chainConfig: chainConfig,
+ cacheConfig: cacheConfig,
+ db: db,
+ triegc: prque.New(nil),
+ stateCache: state.NewDatabase(db),
+ quit: make(chan struct{}),
+ shouldPreserve: shouldPreserve,
+ bodyCache: bodyCache,
+ bodyRLPCache: bodyRLPCache,
+ receiptsCache: receiptsCache,
+ blockCache: blockCache,
+ futureBlocks: futureBlocks,
+ engine: engine,
+ vmConfig: vmConfig,
+ badBlocks: badBlocks,
+ }
+ bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
+ bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
+
+ var err error
+ bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.getProcInterrupt)
+ if err != nil {
+ return nil, err
+ }
+ bc.genesisBlock = bc.GetBlockByNumber(0)
+ if bc.genesisBlock == nil {
+ return nil, ErrNoGenesis
+ }
+ if err := bc.loadLastState(); err != nil {
+ return nil, err
+ }
+
+ // Take ownership of this particular state
+ go bc.update()
+ return bc, nil
+}
+
+func (bc *BlockChain) getProcInterrupt() bool {
+ return atomic.LoadInt32(&bc.procInterrupt) == 1
+}
+
+// loadLastState loads the last known chain state from the database. This method
+// assumes that the chain manager mutex is held.
+func (bc *BlockChain) loadLastState() error {
+ // Restore the last known head block
+ head := rawdb.ReadHeadBlockHash(bc.db)
+ if head == (common.Hash{}) {
+ // Corrupt or empty database, init from scratch
+ log.Warn("Empty database, resetting chain")
+ return bc.Reset()
+ }
+ // Make sure the entire head block is available
+ currentBlock := bc.GetBlockByHash(head)
+ if currentBlock == nil {
+ // Corrupt or empty database, init from scratch
+ log.Warn("Head block missing, resetting chain", "hash", head)
+ return bc.Reset()
+ }
+ // Make sure the state associated with the block is available
+ if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
+ // Dangling block without a state associated, init from scratch
+ log.Warn("Head state missing, repairing chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
+ if err := bc.repair(¤tBlock); err != nil {
+ return err
+ }
+ }
+ // Everything seems to be fine, set as the head block
+ bc.currentBlock.Store(currentBlock)
+
+ // Restore the last known head header
+ currentHeader := currentBlock.Header()
+ if head := rawdb.ReadHeadHeaderHash(bc.db); head != (common.Hash{}) {
+ if header := bc.GetHeaderByHash(head); header != nil {
+ currentHeader = header
+ }
+ }
+ bc.hc.SetCurrentHeader(currentHeader)
+
+ // Restore the last known head fast block
+ bc.currentFastBlock.Store(currentBlock)
+ if head := rawdb.ReadHeadFastBlockHash(bc.db); head != (common.Hash{}) {
+ if block := bc.GetBlockByHash(head); block != nil {
+ bc.currentFastBlock.Store(block)
+ }
+ }
+
+ // Issue a status log for the user
+ currentFastBlock := bc.CurrentFastBlock()
+
+ headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
+ blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
+ fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64())
+
+ log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd, "age", common.PrettyAge(time.Unix(currentHeader.Time.Int64(), 0)))
+ log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd, "age", common.PrettyAge(time.Unix(currentBlock.Time().Int64(), 0)))
+ log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd, "age", common.PrettyAge(time.Unix(currentFastBlock.Time().Int64(), 0)))
+
+ return nil
+}
+
+// SetHead rewinds the local chain to a new head. In the case of headers, everything
+// above the new head will be deleted and the new one set. In the case of blocks
+// though, the head may be further rewound if block bodies are missing (non-archive
+// nodes after a fast sync).
+func (bc *BlockChain) SetHead(head uint64) error {
+ log.Warn("Rewinding blockchain", "target", head)
+
+ bc.mu.Lock()
+ defer bc.mu.Unlock()
+
+ // Rewind the header chain, deleting all block bodies until then
+ delFn := func(db rawdb.DatabaseDeleter, hash common.Hash, num uint64) {
+ rawdb.DeleteBody(db, hash, num)
+ }
+ bc.hc.SetHead(head, delFn)
+ currentHeader := bc.hc.CurrentHeader()
+
+ // Clear out any stale content from the caches
+ bc.bodyCache.Purge()
+ bc.bodyRLPCache.Purge()
+ bc.receiptsCache.Purge()
+ bc.blockCache.Purge()
+ bc.futureBlocks.Purge()
+
+ // Rewind the block chain, ensuring we don't end up with a stateless head block
+ if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() {
+ bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
+ }
+ if currentBlock := bc.CurrentBlock(); currentBlock != nil {
+ if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
+ // Rewound state missing, rolled back to before pivot, reset to genesis
+ bc.currentBlock.Store(bc.genesisBlock)
+ }
+ }
+ // Rewind the fast block in a simpleton way to the target head
+ if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && currentHeader.Number.Uint64() < currentFastBlock.NumberU64() {
+ bc.currentFastBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
+ }
+ // If either blocks reached nil, reset to the genesis state
+ if currentBlock := bc.CurrentBlock(); currentBlock == nil {
+ bc.currentBlock.Store(bc.genesisBlock)
+ }
+ if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil {
+ bc.currentFastBlock.Store(bc.genesisBlock)
+ }
+ currentBlock := bc.CurrentBlock()
+ currentFastBlock := bc.CurrentFastBlock()
+
+ rawdb.WriteHeadBlockHash(bc.db, currentBlock.Hash())
+ rawdb.WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash())
+
+ return bc.loadLastState()
+}
+
+// FastSyncCommitHead sets the current head block to the one defined by the hash
+// irrelevant what the chain contents were prior.
+func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
+ // Make sure that both the block as well at its state trie exists
+ block := bc.GetBlockByHash(hash)
+ if block == nil {
+ return fmt.Errorf("non existent block [%x…]", hash[:4])
+ }
+ if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB(), 0); err != nil {
+ return err
+ }
+ // If all checks out, manually set the head block
+ bc.mu.Lock()
+ bc.currentBlock.Store(block)
+ bc.mu.Unlock()
+
+ log.Info("Committed new head block", "number", block.Number(), "hash", hash)
+ return nil
+}
+
+// GasLimit returns the gas limit of the current HEAD block.
+func (bc *BlockChain) GasLimit() uint64 {
+ return bc.CurrentBlock().GasLimit()
+}
+
+// CurrentBlock retrieves the current head block of the canonical chain. The
+// block is retrieved from the blockchain's internal cache.
+func (bc *BlockChain) CurrentBlock() *types.Block {
+ return bc.currentBlock.Load().(*types.Block)
+}
+
+// CurrentFastBlock retrieves the current fast-sync head block of the canonical
+// chain. The block is retrieved from the blockchain's internal cache.
+func (bc *BlockChain) CurrentFastBlock() *types.Block {
+ return bc.currentFastBlock.Load().(*types.Block)
+}
+
+// SetProcessor sets the processor required for making state modifications.
+func (bc *BlockChain) SetProcessor(processor Processor) {
+ bc.procmu.Lock()
+ defer bc.procmu.Unlock()
+ bc.processor = processor
+}
+
+// SetValidator sets the validator which is used to validate incoming blocks.
+func (bc *BlockChain) SetValidator(validator Validator) {
+ bc.procmu.Lock()
+ defer bc.procmu.Unlock()
+ bc.validator = validator
+}
+
+// Validator returns the current validator.
+func (bc *BlockChain) Validator() Validator {
+ bc.procmu.RLock()
+ defer bc.procmu.RUnlock()
+ return bc.validator
+}
+
+// Processor returns the current processor.
+func (bc *BlockChain) Processor() Processor {
+ bc.procmu.RLock()
+ defer bc.procmu.RUnlock()
+ return bc.processor
+}
+
+// State returns a new mutable state based on the current HEAD block.
+func (bc *BlockChain) State() (*state.StateDB, error) {
+ return bc.StateAt(bc.CurrentBlock().Root())
+}
+
+// StateAt returns a new mutable state based on a particular point in time.
+func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
+ return state.New(root, bc.stateCache)
+}
+
+// Reset purges the entire blockchain, restoring it to its genesis state.
+func (bc *BlockChain) Reset() error {
+ return bc.ResetWithGenesisBlock(bc.genesisBlock)
+}
+
+// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
+// specified genesis state.
+func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
+ // Dump the entire block chain and purge the caches
+ if err := bc.SetHead(0); err != nil {
+ return err
+ }
+ bc.mu.Lock()
+ defer bc.mu.Unlock()
+
+ // Prepare the genesis block and reinitialise the chain
+ if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
+ log.Crit("Failed to write genesis block TD", "err", err)
+ }
+ rawdb.WriteBlock(bc.db, genesis)
+
+ bc.genesisBlock = genesis
+ bc.insert(bc.genesisBlock)
+ bc.currentBlock.Store(bc.genesisBlock)
+ bc.hc.SetGenesis(bc.genesisBlock.Header())
+ bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
+ bc.currentFastBlock.Store(bc.genesisBlock)
+
+ return nil
+}
+
+// repair tries to repair the current blockchain by rolling back the current block
+// until one with associated state is found. This is needed to fix incomplete db
+// writes caused either by crashes/power outages, or simply non-committed tries.
+//
+// This method only rolls back the current block. The current header and current
+// fast block are left intact.
+func (bc *BlockChain) repair(head **types.Block) error {
+ for {
+ // Abort if we've rewound to a head block that does have associated state
+ if _, err := state.New((*head).Root(), bc.stateCache); err == nil {
+ log.Info("Rewound blockchain to past state", "number", (*head).Number(), "hash", (*head).Hash())
+ return nil
+ }
+ // Otherwise rewind one block and recheck state availability there
+ (*head) = bc.GetBlock((*head).ParentHash(), (*head).NumberU64()-1)
+ }
+}
+
+// Export writes the active chain to the given writer.
+func (bc *BlockChain) Export(w io.Writer) error {
+ return bc.ExportN(w, uint64(0), bc.CurrentBlock().NumberU64())
+}
+
+// ExportN writes a subset of the active chain to the given writer.
+func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
+ bc.mu.RLock()
+ defer bc.mu.RUnlock()
+
+ if first > last {
+ return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
+ }
+ log.Info("Exporting batch of blocks", "count", last-first+1)
+
+ start, reported := time.Now(), time.Now()
+ for nr := first; nr <= last; nr++ {
+ block := bc.GetBlockByNumber(nr)
+ if block == nil {
+ return fmt.Errorf("export failed on #%d: not found", nr)
+ }
+ if err := block.EncodeRLP(w); err != nil {
+ return err
+ }
+ if time.Since(reported) >= statsReportLimit {
+ log.Info("Exporting blocks", "exported", block.NumberU64()-first, "elapsed", common.PrettyDuration(time.Since(start)))
+ reported = time.Now()
+ }
+ }
+
+ return nil
+}
+
+// insert injects a new head block into the current block chain. This method
+// assumes that the block is indeed a true head. It will also reset the head
+// header and the head fast sync block to this very same block if they are older
+// or if they are on a different side chain.
+//
+// Note, this function assumes that the `mu` mutex is held!
+func (bc *BlockChain) insert(block *types.Block) {
+ // If the block is on a side chain or an unknown one, force other heads onto it too
+ updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash()
+
+ // Add the block to the canonical chain number scheme and mark as the head
+ rawdb.WriteCanonicalHash(bc.db, block.Hash(), block.NumberU64())
+ rawdb.WriteHeadBlockHash(bc.db, block.Hash())
+
+ bc.currentBlock.Store(block)
+
+ // If the block is better than our head or is on a different chain, force update heads
+ if updateHeads {
+ bc.hc.SetCurrentHeader(block.Header())
+ rawdb.WriteHeadFastBlockHash(bc.db, block.Hash())
+
+ bc.currentFastBlock.Store(block)
+ }
+}
+
+// Genesis retrieves the chain's genesis block.
+func (bc *BlockChain) Genesis() *types.Block {
+ return bc.genesisBlock
+}
+
+// GetBody retrieves a block body (transactions and uncles) from the database by
+// hash, caching it if found.
+func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
+ // Short circuit if the body's already in the cache, retrieve otherwise
+ if cached, ok := bc.bodyCache.Get(hash); ok {
+ body := cached.(*types.Body)
+ return body
+ }
+ number := bc.hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ body := rawdb.ReadBody(bc.db, hash, *number)
+ if body == nil {
+ return nil
+ }
+ // Cache the found body for next time and return
+ bc.bodyCache.Add(hash, body)
+ return body
+}
+
+// GetBodyRLP retrieves a block body in RLP encoding from the database by hash,
+// caching it if found.
+func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
+ // Short circuit if the body's already in the cache, retrieve otherwise
+ if cached, ok := bc.bodyRLPCache.Get(hash); ok {
+ return cached.(rlp.RawValue)
+ }
+ number := bc.hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ body := rawdb.ReadBodyRLP(bc.db, hash, *number)
+ if len(body) == 0 {
+ return nil
+ }
+ // Cache the found body for next time and return
+ bc.bodyRLPCache.Add(hash, body)
+ return body
+}
+
+// HasBlock checks if a block is fully present in the database or not.
+func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
+ if bc.blockCache.Contains(hash) {
+ return true
+ }
+ return rawdb.HasBody(bc.db, hash, number)
+}
+
+// HasState checks if state trie is fully present in the database or not.
+func (bc *BlockChain) HasState(hash common.Hash) bool {
+ _, err := bc.stateCache.OpenTrie(hash)
+ return err == nil
+}
+
+// HasBlockAndState checks if a block and associated state trie is fully present
+// in the database or not, caching it if present.
+func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
+ // Check first that the block itself is known
+ block := bc.GetBlock(hash, number)
+ if block == nil {
+ return false
+ }
+ return bc.HasState(block.Root())
+}
+
+// GetBlock retrieves a block from the database by hash and number,
+// caching it if found.
+func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
+ // Short circuit if the block's already in the cache, retrieve otherwise
+ if block, ok := bc.blockCache.Get(hash); ok {
+ return block.(*types.Block)
+ }
+ block := rawdb.ReadBlock(bc.db, hash, number)
+ if block == nil {
+ return nil
+ }
+ // Cache the found block for next time and return
+ bc.blockCache.Add(block.Hash(), block)
+ return block
+}
+
+// GetBlockByHash retrieves a block from the database by hash, caching it if found.
+func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
+ number := bc.hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ return bc.GetBlock(hash, *number)
+}
+
+// GetBlockByNumber retrieves a block from the database by number, caching it
+// (associated with its hash) if found.
+func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
+ hash := rawdb.ReadCanonicalHash(bc.db, number)
+ if hash == (common.Hash{}) {
+ return nil
+ }
+ return bc.GetBlock(hash, number)
+}
+
+// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
+func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
+ if receipts, ok := bc.receiptsCache.Get(hash); ok {
+ return receipts.(types.Receipts)
+ }
+
+ number := rawdb.ReadHeaderNumber(bc.db, hash)
+ if number == nil {
+ return nil
+ }
+
+ receipts := rawdb.ReadReceipts(bc.db, hash, *number)
+ bc.receiptsCache.Add(hash, receipts)
+ return receipts
+}
+
+// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
+// [deprecated by eth/62]
+func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
+ number := bc.hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ for i := 0; i < n; i++ {
+ block := bc.GetBlock(hash, *number)
+ if block == nil {
+ break
+ }
+ blocks = append(blocks, block)
+ hash = block.ParentHash()
+ *number--
+ }
+ return
+}
+
+// GetUnclesInChain retrieves all the uncles from a given block backwards until
+// a specific distance is reached.
+func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
+ uncles := []*types.Header{}
+ for i := 0; block != nil && i < length; i++ {
+ uncles = append(uncles, block.Uncles()...)
+ block = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
+ }
+ return uncles
+}
+
+// TrieNode retrieves a blob of data associated with a trie node (or code hash)
+// either from ephemeral in-memory cache, or from persistent storage.
+func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
+ return bc.stateCache.TrieDB().Node(hash)
+}
+
+// Stop stops the blockchain service. If any imports are currently in progress
+// it will abort them using the procInterrupt.
+func (bc *BlockChain) Stop() {
+ if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
+ return
+ }
+ // Unsubscribe all subscriptions registered from blockchain
+ bc.scope.Close()
+ close(bc.quit)
+ atomic.StoreInt32(&bc.procInterrupt, 1)
+
+ bc.wg.Wait()
+
+ // Ensure the state of a recent block is also stored to disk before exiting.
+ // We're writing three different states to catch different restart scenarios:
+ // - HEAD: So we don't need to reprocess any blocks in the general case
+ // - HEAD-1: So we don't do large reorgs if our HEAD becomes an uncle
+ // - HEAD-127: So we have a hard limit on the number of blocks reexecuted
+ if !bc.cacheConfig.Disabled {
+ triedb := bc.stateCache.TrieDB()
+
+ for _, offset := range []uint64{0, 1, triesInMemory - 1} {
+ if number := bc.CurrentBlock().NumberU64(); number > offset {
+ recent := bc.GetBlockByNumber(number - offset)
+
+ log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
+ if err := triedb.Commit(recent.Root(), true); err != nil {
+ log.Error("Failed to commit recent state trie", "err", err)
+ }
+ }
+ }
+ for !bc.triegc.Empty() {
+ triedb.Dereference(bc.triegc.PopItem().(common.Hash))
+ }
+ if size, _ := triedb.Size(); size != 0 {
+ log.Error("Dangling trie nodes after full cleanup")
+ }
+ }
+ log.Info("Blockchain manager stopped")
+}
+
+func (bc *BlockChain) procFutureBlocks() {
+ blocks := make([]*types.Block, 0, bc.futureBlocks.Len())
+ for _, hash := range bc.futureBlocks.Keys() {
+ if block, exist := bc.futureBlocks.Peek(hash); exist {
+ blocks = append(blocks, block.(*types.Block))
+ }
+ }
+ if len(blocks) > 0 {
+ types.BlockBy(types.Number).Sort(blocks)
+
+ // Insert one by one as chain insertion needs contiguous ancestry between blocks
+ //for i := range blocks {
+ // bc.InsertChain(blocks[i : i+1])
+ //}
+ }
+}
+
+// WriteStatus status of write
+type WriteStatus byte
+
+const (
+ NonStatTy WriteStatus = iota
+ CanonStatTy
+ SideStatTy
+)
+
+// Rollback is designed to remove a chain of links from the database that aren't
+// certain enough to be valid.
+func (bc *BlockChain) Rollback(chain []common.Hash) {
+ bc.mu.Lock()
+ defer bc.mu.Unlock()
+
+ for i := len(chain) - 1; i >= 0; i-- {
+ hash := chain[i]
+
+ currentHeader := bc.hc.CurrentHeader()
+ if currentHeader.Hash() == hash {
+ bc.hc.SetCurrentHeader(bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1))
+ }
+ if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash {
+ newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1)
+ bc.currentFastBlock.Store(newFastBlock)
+ rawdb.WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
+ }
+ if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash {
+ newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1)
+ bc.currentBlock.Store(newBlock)
+ rawdb.WriteHeadBlockHash(bc.db, newBlock.Hash())
+ }
+ }
+}
+
+// SetReceiptsData computes all the non-consensus fields of the receipts
+func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error {
+ signer := types.MakeSigner(config, block.Number())
+
+ transactions, logIndex := block.Transactions(), uint(0)
+ if len(transactions) != len(receipts) {
+ return errors.New("transaction and receipt count mismatch")
+ }
+
+ for j := 0; j < len(receipts); j++ {
+ // The transaction hash can be retrieved from the transaction itself
+ receipts[j].TxHash = transactions[j].Hash()
+
+ // The contract address can be derived from the transaction itself
+ if transactions[j].To() == nil {
+ // Deriving the signer is expensive, only do if it's actually needed
+ from, _ := types.Sender(signer, transactions[j])
+ receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce())
+ }
+ // The used gas can be calculated based on previous receipts
+ if j == 0 {
+ receipts[j].GasUsed = receipts[j].CumulativeGasUsed
+ } else {
+ receipts[j].GasUsed = receipts[j].CumulativeGasUsed - receipts[j-1].CumulativeGasUsed
+ }
+ // The derived log fields can simply be set from the block and transaction
+ for k := 0; k < len(receipts[j].Logs); k++ {
+ receipts[j].Logs[k].BlockNumber = block.NumberU64()
+ receipts[j].Logs[k].BlockHash = block.Hash()
+ receipts[j].Logs[k].TxHash = receipts[j].TxHash
+ receipts[j].Logs[k].TxIndex = uint(j)
+ receipts[j].Logs[k].Index = logIndex
+ logIndex++
+ }
+ }
+ return nil
+}
+
+// InsertReceiptChain attempts to complete an already existing header chain with
+// transaction and receipt data.
+func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
+ bc.wg.Add(1)
+ defer bc.wg.Done()
+
+ // Do a sanity check that the provided chain is actually ordered and linked
+ for i := 1; i < len(blockChain); i++ {
+ if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() {
+ log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(),
+ "prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash())
+ return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, blockChain[i-1].NumberU64(),
+ blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4])
+ }
+ }
+
+ var (
+ stats = struct{ processed, ignored int32 }{}
+ start = time.Now()
+ bytes = 0
+ batch = bc.db.NewBatch()
+ )
+ for i, block := range blockChain {
+ receipts := receiptChain[i]
+ // Short circuit insertion if shutting down or processing failed
+ if atomic.LoadInt32(&bc.procInterrupt) == 1 {
+ return 0, nil
+ }
+ // Short circuit if the owner header is unknown
+ if !bc.HasHeader(block.Hash(), block.NumberU64()) {
+ return i, fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4])
+ }
+ // Skip if the entire data is already known
+ if bc.HasBlock(block.Hash(), block.NumberU64()) {
+ stats.ignored++
+ continue
+ }
+ // Compute all the non-consensus fields of the receipts
+ if err := SetReceiptsData(bc.chainConfig, block, receipts); err != nil {
+ return i, fmt.Errorf("failed to set receipts data: %v", err)
+ }
+ // Write all the data out into the database
+ rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
+ rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
+ rawdb.WriteTxLookupEntries(batch, block)
+
+ stats.processed++
+
+ if batch.ValueSize() >= ethdb.IdealBatchSize {
+ if err := batch.Write(); err != nil {
+ return 0, err
+ }
+ bytes += batch.ValueSize()
+ batch.Reset()
+ }
+ }
+ if batch.ValueSize() > 0 {
+ bytes += batch.ValueSize()
+ if err := batch.Write(); err != nil {
+ return 0, err
+ }
+ }
+
+ // Update the head fast sync block if better
+ bc.mu.Lock()
+ head := blockChain[len(blockChain)-1]
+ if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
+ currentFastBlock := bc.CurrentFastBlock()
+ if bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()).Cmp(td) < 0 {
+ rawdb.WriteHeadFastBlockHash(bc.db, head.Hash())
+ bc.currentFastBlock.Store(head)
+ }
+ }
+ bc.mu.Unlock()
+
+ context := []interface{}{
+ "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
+ "number", head.Number(), "hash", head.Hash(), "age", common.PrettyAge(time.Unix(head.Time().Int64(), 0)),
+ "size", common.StorageSize(bytes),
+ }
+ if stats.ignored > 0 {
+ context = append(context, []interface{}{"ignored", stats.ignored}...)
+ }
+ log.Info("Imported new block receipts", context...)
+
+ return 0, nil
+}
+
+var lastWrite uint64
+
+// WriteBlockWithoutState writes only the block and its metadata to the database,
+// but does not write any state. This is used to construct competing side forks
+// up to the point where they exceed the canonical total difficulty.
+func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (err error) {
+ bc.wg.Add(1)
+ defer bc.wg.Done()
+
+ if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), td); err != nil {
+ return err
+ }
+ rawdb.WriteBlock(bc.db, block)
+
+ return nil
+}
+
+// WriteBlockWithState writes the block and all associated state to the database.
+func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
+ bc.wg.Add(1)
+ defer bc.wg.Done()
+
+ // Calculate the total difficulty of the block
+ ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
+ if ptd == nil {
+ return NonStatTy, consensus.ErrUnknownAncestor
+ }
+ // Make sure no inconsistent state is leaked during insertion
+ bc.mu.Lock()
+ defer bc.mu.Unlock()
+
+ currentBlock := bc.CurrentBlock()
+ localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
+ externTd := new(big.Int).Add(block.Difficulty(), ptd)
+
+ // Irrelevant of the canonical status, write the block itself to the database
+ if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), externTd); err != nil {
+ return NonStatTy, err
+ }
+ rawdb.WriteBlock(bc.db, block)
+
+ root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
+ if err != nil {
+ return NonStatTy, err
+ }
+ triedb := bc.stateCache.TrieDB()
+
+ // If we're running an archive node, always flush
+ if bc.cacheConfig.Disabled {
+ if err := triedb.Commit(root, false); err != nil {
+ return NonStatTy, err
+ }
+ } else {
+ // Full but not archive node, do proper garbage collection
+ triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
+ bc.triegc.Push(root, -int64(block.NumberU64()))
+
+ if current := block.NumberU64(); current > triesInMemory {
+ // If we exceeded our memory allowance, flush matured singleton nodes to disk
+ var (
+ nodes, imgs = triedb.Size()
+ limit = common.StorageSize(bc.cacheConfig.TrieNodeLimit) * 1024 * 1024
+ )
+ if nodes > limit || imgs > 4*1024*1024 {
+ triedb.Cap(limit - ethdb.IdealBatchSize)
+ }
+ // Find the next state trie we need to commit
+ header := bc.GetHeaderByNumber(current - triesInMemory)
+ chosen := header.Number.Uint64()
+
+ // If we exceeded out time allowance, flush an entire trie to disk
+ if bc.gcproc > bc.cacheConfig.TrieTimeLimit {
+ // If we're exceeding limits but haven't reached a large enough memory gap,
+ // warn the user that the system is becoming unstable.
+ if chosen < lastWrite+triesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
+ log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/triesInMemory)
+ }
+ // Flush an entire trie and restart the counters
+ triedb.Commit(header.Root, true)
+ lastWrite = chosen
+ bc.gcproc = 0
+ }
+ // Garbage collect anything below our required write retention
+ for !bc.triegc.Empty() {
+ root, number := bc.triegc.Pop()
+ if uint64(-number) > chosen {
+ bc.triegc.Push(root, number)
+ break
+ }
+ triedb.Dereference(root.(common.Hash))
+ }
+ }
+ }
+
+ // Write other block data using a batch.
+ batch := bc.db.NewBatch()
+ rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
+
+ // If the total difficulty is higher than our known, add it to the canonical chain
+ // Second clause in the if statement reduces the vulnerability to selfish mining.
+ // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
+ reorg := externTd.Cmp(localTd) > 0
+ currentBlock = bc.CurrentBlock()
+ if !reorg && externTd.Cmp(localTd) == 0 {
+ // Split same-difficulty blocks by number, then preferentially select
+ // the block generated by the local miner as the canonical block.
+ if block.NumberU64() < currentBlock.NumberU64() {
+ reorg = true
+ } else if block.NumberU64() == currentBlock.NumberU64() {
+ var currentPreserve, blockPreserve bool
+ if bc.shouldPreserve != nil {
+ currentPreserve, blockPreserve = bc.shouldPreserve(currentBlock), bc.shouldPreserve(block)
+ }
+ reorg = !currentPreserve && (blockPreserve || mrand.Float64() < 0.5)
+ }
+ }
+ if reorg {
+ } else {
+ status = SideStatTy
+ }
+ if err := batch.Write(); err != nil {
+ return NonStatTy, err
+ }
+
+ // Set new head.
+ if status == CanonStatTy {
+ bc.insert(block)
+ }
+ bc.futureBlocks.Remove(block.Hash())
+ return status, nil
+}
+
+// insertStats tracks and reports on block insertion.
+type insertStats struct {
+ queued, processed, ignored int
+ usedGas uint64
+ lastIndex int
+ startTime mclock.AbsTime
+}
+
+// statsReportLimit is the time limit during import and export after which we
+// always print out progress. This avoids the user wondering what's going on.
+const statsReportLimit = 8 * time.Second
+
+// report prints statistics if some number of blocks have been processed
+// or more than a few seconds have passed since the last message.
+func (st *insertStats) report(chain []*types.Block, index int, cache common.StorageSize) {
+ // Fetch the timings for the batch
+ var (
+ now = mclock.Now()
+ elapsed = time.Duration(now) - time.Duration(st.startTime)
+ )
+ // If we're at the last block of the batch or report period reached, log
+ if index == len(chain)-1 || elapsed >= statsReportLimit {
+ var (
+ end = chain[index]
+ txs = countTransactions(chain[st.lastIndex : index+1])
+ )
+ context := []interface{}{
+ "blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
+ "elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
+ "number", end.Number(), "hash", end.Hash(),
+ }
+ if timestamp := time.Unix(end.Time().Int64(), 0); time.Since(timestamp) > time.Minute {
+ context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
+ }
+ context = append(context, []interface{}{"cache", cache}...)
+
+ if st.queued > 0 {
+ context = append(context, []interface{}{"queued", st.queued}...)
+ }
+ if st.ignored > 0 {
+ context = append(context, []interface{}{"ignored", st.ignored}...)
+ }
+ log.Info("Imported new chain segment", context...)
+
+ *st = insertStats{startTime: now, lastIndex: index + 1}
+ }
+}
+
+func countTransactions(chain []*types.Block) (c int) {
+ for _, b := range chain {
+ c += len(b.Transactions())
+ }
+ return c
+}
+
+// PostChainEvents iterates over the events generated by a chain insertion and
+// posts them into the event feed.
+// TODO: Should not expose PostChainEvents. The chain events should be posted in WriteBlock.
+func (bc *BlockChain) PostChainEvents(events []interface{}, logs []*types.Log) {
+ // post event logs for further processing
+ if logs != nil {
+ bc.logsFeed.Send(logs)
+ }
+ for _, event := range events {
+ switch ev := event.(type) {
+ case ChainEvent:
+ bc.chainFeed.Send(ev)
+
+ case ChainHeadEvent:
+ bc.chainHeadFeed.Send(ev)
+
+ case ChainSideEvent:
+ bc.chainSideFeed.Send(ev)
+ }
+ }
+}
+
+func (bc *BlockChain) update() {
+ futureTimer := time.NewTicker(5 * time.Second)
+ defer futureTimer.Stop()
+ for {
+ select {
+ case <-futureTimer.C:
+ bc.procFutureBlocks()
+ case <-bc.quit:
+ return
+ }
+ }
+}
+
+// BadBlocks returns a list of the last 'bad blocks' that the client has seen on the network
+func (bc *BlockChain) BadBlocks() []*types.Block {
+ blocks := make([]*types.Block, 0, bc.badBlocks.Len())
+ for _, hash := range bc.badBlocks.Keys() {
+ if blk, exist := bc.badBlocks.Peek(hash); exist {
+ block := blk.(*types.Block)
+ blocks = append(blocks, block)
+ }
+ }
+ return blocks
+}
+
+// addBadBlock adds a bad block to the bad-block LRU cache
+func (bc *BlockChain) addBadBlock(block *types.Block) {
+ bc.badBlocks.Add(block.Hash(), block)
+}
+
+// reportBlock logs a bad block error.
+func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
+ bc.addBadBlock(block)
+
+ var receiptString string
+ for _, receipt := range receipts {
+ receiptString += fmt.Sprintf("\t%v\n", receipt)
+ }
+ log.Error(fmt.Sprintf(`
+########## BAD BLOCK #########
+Chain config: %v
+
+Number: %v
+Hash: 0x%x
+%v
+
+Error: %v
+##############################
+`, bc.chainConfig, block.Number(), block.Hash(), receiptString, err))
+}
+
+// InsertHeaderChain attempts to insert the given header chain in to the local
+// chain, possibly creating a reorg. If an error is returned, it will return the
+// index number of the failing header as well an error describing what went wrong.
+//
+// The verify parameter can be used to fine tune whether nonce verification
+// should be done or not. The reason behind the optional check is because some
+// of the header retrieval mechanisms already need to verify nonces, as well as
+// because nonces can be verified sparsely, not needing to check each.
+func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
+ start := time.Now()
+ if i, err := bc.hc.ValidateHeaderChain(chain, checkFreq); err != nil {
+ return i, err
+ }
+
+ // Make sure only one thread manipulates the chain at once
+ bc.chainmu.Lock()
+ defer bc.chainmu.Unlock()
+
+ bc.wg.Add(1)
+ defer bc.wg.Done()
+
+ whFunc := func(header *types.Header) error {
+ bc.mu.Lock()
+ defer bc.mu.Unlock()
+
+ _, err := bc.hc.WriteHeader(header)
+ return err
+ }
+
+ return bc.hc.InsertHeaderChain(chain, whFunc, start)
+}
+
+// writeHeader writes a header into the local chain, given that its parent is
+// already known. If the total difficulty of the newly inserted header becomes
+// greater than the current known TD, the canonical chain is re-routed.
+//
+// Note: This method is not concurrent-safe with inserting blocks simultaneously
+// into the chain, as side effects caused by reorganisations cannot be emulated
+// without the real blocks. Hence, writing headers directly should only be done
+// in two scenarios: pure-header mode of operation (light clients), or properly
+// separated header/block phases (non-archive clients).
+func (bc *BlockChain) writeHeader(header *types.Header) error {
+ bc.wg.Add(1)
+ defer bc.wg.Done()
+
+ bc.mu.Lock()
+ defer bc.mu.Unlock()
+
+ _, err := bc.hc.WriteHeader(header)
+ return err
+}
+
+// CurrentHeader retrieves the current head header of the canonical chain. The
+// header is retrieved from the HeaderChain's internal cache.
+func (bc *BlockChain) CurrentHeader() *types.Header {
+ return bc.hc.CurrentHeader()
+}
+
+// GetTd retrieves a block's total difficulty in the canonical chain from the
+// database by hash and number, caching it if found.
+func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int {
+ return bc.hc.GetTd(hash, number)
+}
+
+// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
+// database by hash, caching it if found.
+func (bc *BlockChain) GetTdByHash(hash common.Hash) *big.Int {
+ return bc.hc.GetTdByHash(hash)
+}
+
+// GetHeader retrieves a block header from the database by hash and number,
+// caching it if found.
+func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header {
+ return bc.hc.GetHeader(hash, number)
+}
+
+// GetHeaderByHash retrieves a block header from the database by hash, caching it if
+// found.
+func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header {
+ return bc.hc.GetHeaderByHash(hash)
+}
+
+// HasHeader checks if a block header is present in the database or not, caching
+// it if present.
+func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {
+ return bc.hc.HasHeader(hash, number)
+}
+
+// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
+// hash, fetching towards the genesis block.
+func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
+ return bc.hc.GetBlockHashesFromHash(hash, max)
+}
+
+// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
+// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
+// number of blocks to be individually checked before we reach the canonical chain.
+//
+// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
+func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
+ bc.chainmu.Lock()
+ defer bc.chainmu.Unlock()
+
+ return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
+}
+
+// GetHeaderByNumber retrieves a block header from the database by number,
+// caching it (associated with its hash) if found.
+func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
+ return bc.hc.GetHeaderByNumber(number)
+}
+
+// Config retrieves the blockchain's chain configuration.
+func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }
+
+// Engine retrieves the blockchain's consensus engine.
+func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }
+
+// SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent.
+func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription {
+ return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch))
+}
+
+// SubscribeChainEvent registers a subscription of ChainEvent.
+func (bc *BlockChain) SubscribeChainEvent(ch chan<- ChainEvent) event.Subscription {
+ return bc.scope.Track(bc.chainFeed.Subscribe(ch))
+}
+
+// SubscribeChainHeadEvent registers a subscription of ChainHeadEvent.
+func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription {
+ return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch))
+}
+
+// SubscribeChainSideEvent registers a subscription of ChainSideEvent.
+func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Subscription {
+ return bc.scope.Track(bc.chainSideFeed.Subscribe(ch))
+}
+
+// SubscribeLogsEvent registers a subscription of []*types.Log.
+func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
+ return bc.scope.Track(bc.logsFeed.Subscribe(ch))
+}
diff --git a/core/error.go b/core/error.go
new file mode 100644
index 000000000..410eca1e1
--- /dev/null
+++ b/core/error.go
@@ -0,0 +1,35 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import "errors"
+
+var (
+ // ErrKnownBlock is returned when a block to import is already known locally.
+ ErrKnownBlock = errors.New("block already known")
+
+ // ErrGasLimitReached is returned by the gas pool if the amount of gas required
+ // by a transaction is higher than what's left in the block.
+ ErrGasLimitReached = errors.New("gas limit reached")
+
+ // ErrBlacklistedHash is returned if a block to import is on the blacklist.
+ ErrBlacklistedHash = errors.New("blacklisted hash")
+
+ // ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
+ // next one expected based on the local chain.
+ ErrNonceTooHigh = errors.New("nonce too high")
+)
diff --git a/core/events.go b/core/events.go
new file mode 100644
index 000000000..e91c0844d
--- /dev/null
+++ b/core/events.go
@@ -0,0 +1,48 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
+type NewTxsEvent struct{ Txs []*types.Transaction }
+
+// PendingLogsEvent is posted pre mining and notifies of pending logs.
+type PendingLogsEvent struct {
+ Logs []*types.Log
+}
+
+// NewMinedBlockEvent is posted when a block has been imported.
+type NewMinedBlockEvent struct{ Block *types.Block }
+
+// RemovedLogsEvent is posted when a reorg happens
+type RemovedLogsEvent struct{ Logs []*types.Log }
+
+type ChainEvent struct {
+ Block *types.Block
+ Hash common.Hash
+ Logs []*types.Log
+}
+
+type ChainSideEvent struct {
+ Block *types.Block
+}
+
+type ChainHeadEvent struct{ Block *types.Block }
diff --git a/core/evm.go b/core/evm.go
new file mode 100644
index 000000000..051b4340d
--- /dev/null
+++ b/core/evm.go
@@ -0,0 +1,97 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// ChainContext supports retrieving headers and consensus parameters from the
+// current blockchain to be used during transaction processing.
+type ChainContext interface {
+ // Engine retrieves the chain's consensus engine.
+ Engine() consensus.Engine
+
+ // GetHeader returns the hash corresponding to their hash.
+ GetHeader(common.Hash, uint64) *types.Header
+}
+
+// NewEVMContext creates a new context for use in the EVM.
+func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author *common.Address) vm.Context {
+ // If we don't have an explicit author (i.e. not mining), extract from the header
+ var beneficiary common.Address
+ if author == nil {
+ beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation
+ } else {
+ beneficiary = *author
+ }
+ return vm.Context{
+ CanTransfer: CanTransfer,
+ Transfer: Transfer,
+ GetHash: GetHashFn(header, chain),
+ Origin: msg.From(),
+ Coinbase: beneficiary,
+ BlockNumber: new(big.Int).Set(header.Number),
+ Time: new(big.Int).Set(header.Time),
+ Difficulty: new(big.Int).Set(header.Difficulty),
+ GasLimit: header.GasLimit,
+ GasPrice: new(big.Int).Set(msg.GasPrice()),
+ }
+}
+
+// GetHashFn returns a GetHashFunc which retrieves header hashes by number
+func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash {
+ var cache map[uint64]common.Hash
+
+ return func(n uint64) common.Hash {
+ // If there's no hash cache yet, make one
+ if cache == nil {
+ cache = map[uint64]common.Hash{
+ ref.Number.Uint64() - 1: ref.ParentHash,
+ }
+ }
+ // Try to fulfill the request from the cache
+ if hash, ok := cache[n]; ok {
+ return hash
+ }
+ // Not cached, iterate the blocks and cache the hashes
+ for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
+ cache[header.Number.Uint64()-1] = header.ParentHash
+ if n == header.Number.Uint64()-1 {
+ return header.ParentHash
+ }
+ }
+ return common.Hash{}
+ }
+}
+
+// CanTransfer checks whether there are enough funds in the address' account to make a transfer.
+// This does not take the necessary gas in to account to make the transfer valid.
+func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
+ return db.GetBalance(addr).Cmp(amount) >= 0
+}
+
+// Transfer subtracts amount from sender and adds amount to recipient using the given Db
+func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
+ db.SubBalance(sender, amount)
+ db.AddBalance(recipient, amount)
+}
diff --git a/core/gaspool.go b/core/gaspool.go
new file mode 100644
index 000000000..e3795c1ee
--- /dev/null
+++ b/core/gaspool.go
@@ -0,0 +1,54 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "fmt"
+ "math"
+)
+
+// GasPool tracks the amount of gas available during execution of the transactions
+// in a block. The zero value is a pool with zero gas available.
+type GasPool uint64
+
+// AddGas makes gas available for execution.
+func (gp *GasPool) AddGas(amount uint64) *GasPool {
+ if uint64(*gp) > math.MaxUint64-amount {
+ panic("gas pool pushed above uint64")
+ }
+ *(*uint64)(gp) += amount
+ return gp
+}
+
+// SubGas deducts the given amount from the pool if enough gas is
+// available and returns an error otherwise.
+func (gp *GasPool) SubGas(amount uint64) error {
+ if uint64(*gp) < amount {
+ return ErrGasLimitReached
+ }
+ *(*uint64)(gp) -= amount
+ return nil
+}
+
+// Gas returns the amount of gas remaining in the pool.
+func (gp *GasPool) Gas() uint64 {
+ return uint64(*gp)
+}
+
+func (gp *GasPool) String() string {
+ return fmt.Sprintf("%d", *gp)
+}
diff --git a/core/genesis.go b/core/genesis.go
new file mode 100644
index 000000000..a2c356b98
--- /dev/null
+++ b/core/genesis.go
@@ -0,0 +1,351 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "bytes"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+//go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
+//go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go
+
+var errGenesisNoConfig = errors.New("genesis has no chain configuration")
+
+// Genesis specifies the header fields, state of a genesis block. It also defines hard
+// fork switch-over blocks through the chain configuration.
+type Genesis struct {
+ Config *params.ChainConfig `json:"config"`
+ Nonce uint64 `json:"nonce"`
+ Timestamp uint64 `json:"timestamp"`
+ ExtraData []byte `json:"extraData"`
+ GasLimit uint64 `json:"gasLimit" gencodec:"required"`
+ Difficulty *big.Int `json:"difficulty" gencodec:"required"`
+ Mixhash common.Hash `json:"mixHash"`
+ Coinbase common.Address `json:"coinbase"`
+ Alloc GenesisAlloc `json:"alloc" gencodec:"required"`
+
+ // These fields are used for consensus tests. Please don't use them
+ // in actual genesis blocks.
+ Number uint64 `json:"number"`
+ GasUsed uint64 `json:"gasUsed"`
+ ParentHash common.Hash `json:"parentHash"`
+}
+
+// GenesisAlloc specifies the initial state that is part of the genesis block.
+type GenesisAlloc map[common.Address]GenesisAccount
+
+func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
+ m := make(map[common.UnprefixedAddress]GenesisAccount)
+ if err := json.Unmarshal(data, &m); err != nil {
+ return err
+ }
+ *ga = make(GenesisAlloc)
+ for addr, a := range m {
+ (*ga)[common.Address(addr)] = a
+ }
+ return nil
+}
+
+// GenesisAccount is an account in the state of the genesis block.
+type GenesisAccount struct {
+ Code []byte `json:"code,omitempty"`
+ Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
+ Balance *big.Int `json:"balance" gencodec:"required"`
+ Nonce uint64 `json:"nonce,omitempty"`
+ PrivateKey []byte `json:"secretKey,omitempty"` // for tests
+}
+
+// field type overrides for gencodec
+type genesisSpecMarshaling struct {
+ Nonce math.HexOrDecimal64
+ Timestamp math.HexOrDecimal64
+ ExtraData hexutil.Bytes
+ GasLimit math.HexOrDecimal64
+ GasUsed math.HexOrDecimal64
+ Number math.HexOrDecimal64
+ Difficulty *math.HexOrDecimal256
+ Alloc map[common.UnprefixedAddress]GenesisAccount
+}
+
+type genesisAccountMarshaling struct {
+ Code hexutil.Bytes
+ Balance *math.HexOrDecimal256
+ Nonce math.HexOrDecimal64
+ Storage map[storageJSON]storageJSON
+ PrivateKey hexutil.Bytes
+}
+
+// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
+// unmarshaling from hex.
+type storageJSON common.Hash
+
+func (h *storageJSON) UnmarshalText(text []byte) error {
+ text = bytes.TrimPrefix(text, []byte("0x"))
+ if len(text) > 64 {
+ return fmt.Errorf("too many hex characters in storage key/value %q", text)
+ }
+ offset := len(h) - len(text)/2 // pad on the left
+ if _, err := hex.Decode(h[offset:], text); err != nil {
+ fmt.Println(err)
+ return fmt.Errorf("invalid hex storage key/value %q", text)
+ }
+ return nil
+}
+
+func (h storageJSON) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(h[:]).MarshalText()
+}
+
+// GenesisMismatchError is raised when trying to overwrite an existing
+// genesis block with an incompatible one.
+type GenesisMismatchError struct {
+ Stored, New common.Hash
+}
+
+func (e *GenesisMismatchError) Error() string {
+ return fmt.Sprintf("database already contains an incompatible genesis block (have %x, new %x)", e.Stored[:8], e.New[:8])
+}
+
+// SetupGenesisBlock writes or updates the genesis block in db.
+// The block that will be used is:
+//
+// genesis == nil genesis != nil
+// +------------------------------------------
+// db has no genesis | main-net default | genesis
+// db has genesis | from DB | genesis (if compatible)
+//
+// The stored chain configuration will be updated if it is compatible (i.e. does not
+// specify a fork block below the local head block). In case of a conflict, the
+// error is a *params.ConfigCompatError and the new, unwritten config is returned.
+//
+// The returned chain configuration is never nil.
+func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
+ if genesis != nil && genesis.Config == nil {
+ return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
+ }
+
+ // Just commit the new block if there is no stored genesis block.
+ stored := rawdb.ReadCanonicalHash(db, 0)
+ if (stored == common.Hash{}) {
+ if genesis == nil {
+ log.Info("Writing default main-net genesis block")
+ genesis = DefaultGenesisBlock()
+ } else {
+ log.Info("Writing custom genesis block")
+ }
+ block, err := genesis.Commit(db)
+ return genesis.Config, block.Hash(), err
+ }
+
+ // Check whether the genesis block is already written.
+ if genesis != nil {
+ hash := genesis.ToBlock(nil).Hash()
+ if hash != stored {
+ return genesis.Config, hash, &GenesisMismatchError{stored, hash}
+ }
+ }
+
+ // Get the existing chain configuration.
+ newcfg := genesis.configOrDefault(stored)
+ storedcfg := rawdb.ReadChainConfig(db, stored)
+ if storedcfg == nil {
+ log.Warn("Found genesis block without chain config")
+ rawdb.WriteChainConfig(db, stored, newcfg)
+ return newcfg, stored, nil
+ }
+ // Special case: don't change the existing config of a non-mainnet chain if no new
+ // config is supplied. These chains would get AllProtocolChanges (and a compat error)
+ // if we just continued here.
+ if genesis == nil && stored != params.MainnetGenesisHash {
+ return storedcfg, stored, nil
+ }
+
+ // Check config compatibility and write the config. Compatibility errors
+ // are returned to the caller unless we're already at block zero.
+ height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db))
+ if height == nil {
+ return newcfg, stored, fmt.Errorf("missing block number for head header hash")
+ }
+ compatErr := storedcfg.CheckCompatible(newcfg, *height)
+ if compatErr != nil && *height != 0 && compatErr.RewindTo != 0 {
+ return newcfg, stored, compatErr
+ }
+ rawdb.WriteChainConfig(db, stored, newcfg)
+ return newcfg, stored, nil
+}
+
+func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
+ switch {
+ case g != nil:
+ return g.Config
+ case ghash == params.MainnetGenesisHash:
+ return params.MainnetChainConfig
+ case ghash == params.TestnetGenesisHash:
+ return params.TestnetChainConfig
+ default:
+ return params.AllEthashProtocolChanges
+ }
+}
+
+// ToBlock creates the genesis block and writes state of a genesis specification
+// to the given database (or discards it if nil).
+func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
+ if db == nil {
+ db = ethdb.NewMemDatabase()
+ }
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
+ for addr, account := range g.Alloc {
+ statedb.AddBalance(addr, account.Balance)
+ statedb.SetCode(addr, account.Code)
+ statedb.SetNonce(addr, account.Nonce)
+ for key, value := range account.Storage {
+ statedb.SetState(addr, key, value)
+ }
+ }
+ root := statedb.IntermediateRoot(false)
+ head := &types.Header{
+ Number: new(big.Int).SetUint64(g.Number),
+ Nonce: types.EncodeNonce(g.Nonce),
+ Time: new(big.Int).SetUint64(g.Timestamp),
+ ParentHash: g.ParentHash,
+ Extra: g.ExtraData,
+ GasLimit: g.GasLimit,
+ GasUsed: g.GasUsed,
+ Difficulty: g.Difficulty,
+ MixDigest: g.Mixhash,
+ Coinbase: g.Coinbase,
+ Root: root,
+ }
+ if g.GasLimit == 0 {
+ head.GasLimit = params.GenesisGasLimit
+ }
+ if g.Difficulty == nil {
+ head.Difficulty = params.GenesisDifficulty
+ }
+ statedb.Commit(false)
+ statedb.Database().TrieDB().Commit(root, true)
+
+ return types.NewBlock(head, nil, nil, nil)
+}
+
+// Commit writes the block and state of a genesis specification to the database.
+// The block is committed as the canonical head block.
+func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
+ block := g.ToBlock(db)
+ if block.Number().Sign() != 0 {
+ return nil, fmt.Errorf("can't commit genesis block with number > 0")
+ }
+ rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty)
+ rawdb.WriteBlock(db, block)
+ rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
+ rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
+ rawdb.WriteHeadBlockHash(db, block.Hash())
+ rawdb.WriteHeadHeaderHash(db, block.Hash())
+
+ config := g.Config
+ if config == nil {
+ config = params.AllEthashProtocolChanges
+ }
+ rawdb.WriteChainConfig(db, block.Hash(), config)
+ return block, nil
+}
+
+// MustCommit writes the genesis block and state to db, panicking on error.
+// The block is committed as the canonical head block.
+func (g *Genesis) MustCommit(db ethdb.Database) *types.Block {
+ block, err := g.Commit(db)
+ if err != nil {
+ panic(err)
+ }
+ return block
+}
+
+// GenesisBlockForTesting creates and writes a block in which addr has the given wei balance.
+func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
+ g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}}
+ return g.MustCommit(db)
+}
+
+// DefaultGenesisBlock returns the Ethereum main net genesis block.
+func DefaultGenesisBlock() *Genesis {
+ return &Genesis{
+ Config: params.MainnetChainConfig,
+ Nonce: 66,
+ ExtraData: hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"),
+ GasLimit: 5000,
+ Difficulty: big.NewInt(17179869184),
+ Alloc: decodePrealloc("empty"),
+ }
+}
+
+// DeveloperGenesisBlock returns the 'geth --dev' genesis block. Note, this must
+// be seeded with the
+func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
+ // Override the default period to the user requested one
+ config := *params.AllCliqueProtocolChanges
+ config.Clique.Period = period
+
+ // Assemble and return the genesis with the precompiles and faucet pre-funded
+ return &Genesis{
+ Config: &config,
+ ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...),
+ GasLimit: 6283185,
+ Difficulty: big.NewInt(1),
+ Alloc: map[common.Address]GenesisAccount{
+ common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
+ common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
+ common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD
+ common.BytesToAddress([]byte{4}): {Balance: big.NewInt(1)}, // Identity
+ common.BytesToAddress([]byte{5}): {Balance: big.NewInt(1)}, // ModExp
+ common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd
+ common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
+ common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
+ faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))},
+ },
+ }
+}
+
+func decodePrealloc(data string) GenesisAlloc {
+ var p []struct{ Addr, Balance *big.Int }
+
+ // Create empty allocation for now
+ // TODO: create genesis block with actual content
+ //if err := rlp.NewStream(strings.NewReader(data), 0).Decode(&p); err != nil {
+ // panic(err)
+ //}
+ _ = data
+ ga := make(GenesisAlloc, len(p))
+ for _, account := range p {
+ ga[common.BigToAddress(account.Addr)] = GenesisAccount{Balance: account.Balance}
+ }
+ return ga
+}
diff --git a/core/headerchain.go b/core/headerchain.go
new file mode 100644
index 000000000..73d05c4cb
--- /dev/null
+++ b/core/headerchain.go
@@ -0,0 +1,507 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ crand "crypto/rand"
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+ mrand "math/rand"
+ "sync/atomic"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/hashicorp/golang-lru"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/rawdb"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+const (
+ headerCacheLimit = 512
+ tdCacheLimit = 1024
+ numberCacheLimit = 2048
+)
+
+// HeaderChain implements the basic block header chain logic that is shared by
+// core.BlockChain and light.LightChain. It is not usable in itself, only as
+// a part of either structure.
+// It is not thread safe either, the encapsulating chain structures should do
+// the necessary mutex locking/unlocking.
+type HeaderChain struct {
+ config *params.ChainConfig
+
+ chainDb ethdb.Database
+ genesisHeader *types.Header
+
+ currentHeader atomic.Value // Current head of the header chain (may be above the block chain!)
+ currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
+
+ headerCache *lru.Cache // Cache for the most recent block headers
+ tdCache *lru.Cache // Cache for the most recent block total difficulties
+ numberCache *lru.Cache // Cache for the most recent block numbers
+
+ procInterrupt func() bool
+
+ rand *mrand.Rand
+ engine consensus.Engine
+}
+
+// NewHeaderChain creates a new HeaderChain structure.
+// getValidator should return the parent's validator
+// procInterrupt points to the parent's interrupt semaphore
+// wg points to the parent's shutdown wait group
+func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) {
+ headerCache, _ := lru.New(headerCacheLimit)
+ tdCache, _ := lru.New(tdCacheLimit)
+ numberCache, _ := lru.New(numberCacheLimit)
+
+ // Seed a fast but crypto originating random generator
+ seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
+ if err != nil {
+ return nil, err
+ }
+
+ hc := &HeaderChain{
+ config: config,
+ chainDb: chainDb,
+ headerCache: headerCache,
+ tdCache: tdCache,
+ numberCache: numberCache,
+ procInterrupt: procInterrupt,
+ rand: mrand.New(mrand.NewSource(seed.Int64())),
+ engine: engine,
+ }
+
+ hc.genesisHeader = hc.GetHeaderByNumber(0)
+ if hc.genesisHeader == nil {
+ return nil, ErrNoGenesis
+ }
+
+ hc.currentHeader.Store(hc.genesisHeader)
+ if head := rawdb.ReadHeadBlockHash(chainDb); head != (common.Hash{}) {
+ if chead := hc.GetHeaderByHash(head); chead != nil {
+ hc.currentHeader.Store(chead)
+ }
+ }
+ hc.currentHeaderHash = hc.CurrentHeader().Hash()
+
+ return hc, nil
+}
+
+// GetBlockNumber retrieves the block number belonging to the given hash
+// from the cache or database
+func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 {
+ if cached, ok := hc.numberCache.Get(hash); ok {
+ number := cached.(uint64)
+ return &number
+ }
+ number := rawdb.ReadHeaderNumber(hc.chainDb, hash)
+ if number != nil {
+ hc.numberCache.Add(hash, *number)
+ }
+ return number
+}
+
+// WriteHeader writes a header into the local chain, given that its parent is
+// already known. If the total difficulty of the newly inserted header becomes
+// greater than the current known TD, the canonical chain is re-routed.
+//
+// Note: This method is not concurrent-safe with inserting blocks simultaneously
+// into the chain, as side effects caused by reorganisations cannot be emulated
+// without the real blocks. Hence, writing headers directly should only be done
+// in two scenarios: pure-header mode of operation (light clients), or properly
+// separated header/block phases (non-archive clients).
+func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, err error) {
+ // Cache some values to prevent constant recalculation
+ var (
+ hash = header.Hash()
+ number = header.Number.Uint64()
+ )
+ // Calculate the total difficulty of the header
+ ptd := hc.GetTd(header.ParentHash, number-1)
+ if ptd == nil {
+ return NonStatTy, consensus.ErrUnknownAncestor
+ }
+ localTd := hc.GetTd(hc.currentHeaderHash, hc.CurrentHeader().Number.Uint64())
+ externTd := new(big.Int).Add(header.Difficulty, ptd)
+
+ // Irrelevant of the canonical status, write the td and header to the database
+ if err := hc.WriteTd(hash, number, externTd); err != nil {
+ log.Crit("Failed to write header total difficulty", "err", err)
+ }
+ //rawdb.WriteHeader(hc.chainDb, header)
+
+ // If the total difficulty is higher than our known, add it to the canonical chain
+ // Second clause in the if statement reduces the vulnerability to selfish mining.
+ // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
+ if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) {
+ // Delete any canonical number assignments above the new head
+ batch := hc.chainDb.NewBatch()
+ for i := number + 1; ; i++ {
+ hash := rawdb.ReadCanonicalHash(hc.chainDb, i)
+ if hash == (common.Hash{}) {
+ break
+ }
+ rawdb.DeleteCanonicalHash(batch, i)
+ }
+ batch.Write()
+
+ // Overwrite any stale canonical number assignments
+ var (
+ headHash = header.ParentHash
+ headNumber = header.Number.Uint64() - 1
+ headHeader = hc.GetHeader(headHash, headNumber)
+ )
+ for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash {
+ rawdb.WriteCanonicalHash(hc.chainDb, headHash, headNumber)
+
+ headHash = headHeader.ParentHash
+ headNumber = headHeader.Number.Uint64() - 1
+ headHeader = hc.GetHeader(headHash, headNumber)
+ }
+ // Extend the canonical chain with the new header
+ rawdb.WriteCanonicalHash(hc.chainDb, hash, number)
+ rawdb.WriteHeadHeaderHash(hc.chainDb, hash)
+
+ hc.currentHeaderHash = hash
+ hc.currentHeader.Store(types.CopyHeader(header))
+
+ status = CanonStatTy
+ } else {
+ status = SideStatTy
+ }
+
+ hc.headerCache.Add(hash, header)
+ hc.numberCache.Add(hash, number)
+
+ return
+}
+
+// WhCallback is a callback function for inserting individual headers.
+// A callback is used for two reasons: first, in a LightChain, status should be
+// processed and light chain events sent, while in a BlockChain this is not
+// necessary since chain events are sent after inserting blocks. Second, the
+// header writes should be protected by the parent chain mutex individually.
+type WhCallback func(*types.Header) error
+
+func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
+ // Do a sanity check that the provided chain is actually ordered and linked
+ for i := 1; i < len(chain); i++ {
+ if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() {
+ // Chain broke ancestry, log a message (programming error) and skip insertion
+ log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", chain[i].Hash(),
+ "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", chain[i-1].Hash())
+
+ return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].Number,
+ chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4])
+ }
+ }
+
+ // Generate the list of seal verification requests, and start the parallel verifier
+ seals := make([]bool, len(chain))
+ for i := 0; i < len(seals)/checkFreq; i++ {
+ index := i*checkFreq + hc.rand.Intn(checkFreq)
+ if index >= len(seals) {
+ index = len(seals) - 1
+ }
+ seals[index] = true
+ }
+ seals[len(seals)-1] = true // Last should always be verified to avoid junk
+
+ //abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
+ //defer close(abort)
+ //
+ //// Iterate over the headers and ensure they all check out
+ //for i, _ := range chain {
+ // // If the chain is terminating, stop processing blocks
+ // if hc.procInterrupt() {
+ // log.Debug("Premature abort during headers verification")
+ // return 0, errors.New("aborted")
+ // }
+ //
+ // // Otherwise wait for headers checks and ensure they pass
+ // if err := <-results; err != nil {
+ // return i, err
+ // }
+ //}
+
+ return 0, nil
+}
+
+// InsertHeaderChain attempts to insert the given header chain in to the local
+// chain, possibly creating a reorg. If an error is returned, it will return the
+// index number of the failing header as well an error describing what went wrong.
+//
+// The verify parameter can be used to fine tune whether nonce verification
+// should be done or not. The reason behind the optional check is because some
+// of the header retrieval mechanisms already need to verfy nonces, as well as
+// because nonces can be verified sparsely, not needing to check each.
+func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, writeHeader WhCallback, start time.Time) (int, error) {
+ // Collect some import statistics to report on
+ stats := struct{ processed, ignored int }{}
+ // All headers passed verification, import them into the database
+ for i, header := range chain {
+ // Short circuit insertion if shutting down
+ if hc.procInterrupt() {
+ log.Debug("Premature abort during headers import")
+ return i, errors.New("aborted")
+ }
+ // If the header's already known, skip it, otherwise store
+ if hc.HasHeader(header.Hash(), header.Number.Uint64()) {
+ stats.ignored++
+ continue
+ }
+ if err := writeHeader(header); err != nil {
+ return i, err
+ }
+ stats.processed++
+ }
+ // Report some public statistics so the user has a clue what's going on
+ last := chain[len(chain)-1]
+
+ context := []interface{}{
+ "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
+ "number", last.Number, "hash", last.Hash(),
+ }
+ if timestamp := time.Unix(last.Time.Int64(), 0); time.Since(timestamp) > time.Minute {
+ context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
+ }
+ if stats.ignored > 0 {
+ context = append(context, []interface{}{"ignored", stats.ignored}...)
+ }
+ log.Info("Imported new block headers", context...)
+
+ return 0, nil
+}
+
+// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
+// hash, fetching towards the genesis block.
+func (hc *HeaderChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
+ // Get the origin header from which to fetch
+ header := hc.GetHeaderByHash(hash)
+ if header == nil {
+ return nil
+ }
+ // Iterate the headers until enough is collected or the genesis reached
+ chain := make([]common.Hash, 0, max)
+ for i := uint64(0); i < max; i++ {
+ next := header.ParentHash
+ if header = hc.GetHeader(next, header.Number.Uint64()-1); header == nil {
+ break
+ }
+ chain = append(chain, next)
+ if header.Number.Sign() == 0 {
+ break
+ }
+ }
+ return chain
+}
+
+// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
+// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
+// number of blocks to be individually checked before we reach the canonical chain.
+//
+// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
+func (hc *HeaderChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
+ if ancestor > number {
+ return common.Hash{}, 0
+ }
+ if ancestor == 1 {
+ // in this case it is cheaper to just read the header
+ if header := hc.GetHeader(hash, number); header != nil {
+ return header.ParentHash, number - 1
+ } else {
+ return common.Hash{}, 0
+ }
+ }
+ for ancestor != 0 {
+ if rawdb.ReadCanonicalHash(hc.chainDb, number) == hash {
+ number -= ancestor
+ return rawdb.ReadCanonicalHash(hc.chainDb, number), number
+ }
+ if *maxNonCanonical == 0 {
+ return common.Hash{}, 0
+ }
+ *maxNonCanonical--
+ ancestor--
+ header := hc.GetHeader(hash, number)
+ if header == nil {
+ return common.Hash{}, 0
+ }
+ hash = header.ParentHash
+ number--
+ }
+ return hash, number
+}
+
+// GetTd retrieves a block's total difficulty in the canonical chain from the
+// database by hash and number, caching it if found.
+func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
+ // Short circuit if the td's already in the cache, retrieve otherwise
+ if cached, ok := hc.tdCache.Get(hash); ok {
+ return cached.(*big.Int)
+ }
+ td := rawdb.ReadTd(hc.chainDb, hash, number)
+ if td == nil {
+ return nil
+ }
+ // Cache the found body for next time and return
+ hc.tdCache.Add(hash, td)
+ return td
+}
+
+// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
+// database by hash, caching it if found.
+func (hc *HeaderChain) GetTdByHash(hash common.Hash) *big.Int {
+ number := hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ return hc.GetTd(hash, *number)
+}
+
+// WriteTd stores a block's total difficulty into the database, also caching it
+// along the way.
+func (hc *HeaderChain) WriteTd(hash common.Hash, number uint64, td *big.Int) error {
+ rawdb.WriteTd(hc.chainDb, hash, number, td)
+ hc.tdCache.Add(hash, new(big.Int).Set(td))
+ return nil
+}
+
+// GetHeader retrieves a block header from the database by hash and number,
+// caching it if found.
+func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header {
+ // Short circuit if the header's already in the cache, retrieve otherwise
+ if header, ok := hc.headerCache.Get(hash); ok {
+ return header.(*types.Header)
+ }
+ header := rawdb.ReadHeader(hc.chainDb, hash, number)
+ if header == nil {
+ return nil
+ }
+ // Cache the found header for next time and return
+ hc.headerCache.Add(hash, header)
+ return header
+}
+
+// GetHeaderByHash retrieves a block header from the database by hash, caching it if
+// found.
+func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header {
+ number := hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ return hc.GetHeader(hash, *number)
+}
+
+// HasHeader checks if a block header is present in the database or not.
+func (hc *HeaderChain) HasHeader(hash common.Hash, number uint64) bool {
+ if hc.numberCache.Contains(hash) || hc.headerCache.Contains(hash) {
+ return true
+ }
+ return rawdb.HasHeader(hc.chainDb, hash, number)
+}
+
+// GetHeaderByNumber retrieves a block header from the database by number,
+// caching it (associated with its hash) if found.
+func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header {
+ hash := rawdb.ReadCanonicalHash(hc.chainDb, number)
+ if hash == (common.Hash{}) {
+ return nil
+ }
+ return hc.GetHeader(hash, number)
+}
+
+// CurrentHeader retrieves the current head header of the canonical chain. The
+// header is retrieved from the HeaderChain's internal cache.
+func (hc *HeaderChain) CurrentHeader() *types.Header {
+ return hc.currentHeader.Load().(*types.Header)
+}
+
+// SetCurrentHeader sets the current head header of the canonical chain.
+func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
+ rawdb.WriteHeadHeaderHash(hc.chainDb, head.Hash())
+
+ hc.currentHeader.Store(head)
+ hc.currentHeaderHash = head.Hash()
+}
+
+// DeleteCallback is a callback function that is called by SetHead before
+// each header is deleted.
+type DeleteCallback func(rawdb.DatabaseDeleter, common.Hash, uint64)
+
+// SetHead rewinds the local chain to a new head. Everything above the new head
+// will be deleted and the new one set.
+func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
+ height := uint64(0)
+
+ if hdr := hc.CurrentHeader(); hdr != nil {
+ height = hdr.Number.Uint64()
+ }
+ batch := hc.chainDb.NewBatch()
+ for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() {
+ hash := hdr.Hash()
+ num := hdr.Number.Uint64()
+ if delFn != nil {
+ delFn(batch, hash, num)
+ }
+ rawdb.DeleteHeader(batch, hash, num)
+ rawdb.DeleteTd(batch, hash, num)
+
+ hc.currentHeader.Store(hc.GetHeader(hdr.ParentHash, hdr.Number.Uint64()-1))
+ }
+ // Roll back the canonical chain numbering
+ for i := height; i > head; i-- {
+ rawdb.DeleteCanonicalHash(batch, i)
+ }
+ batch.Write()
+
+ // Clear out any stale content from the caches
+ hc.headerCache.Purge()
+ hc.tdCache.Purge()
+ hc.numberCache.Purge()
+
+ if hc.CurrentHeader() == nil {
+ hc.currentHeader.Store(hc.genesisHeader)
+ }
+ hc.currentHeaderHash = hc.CurrentHeader().Hash()
+
+ rawdb.WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash)
+}
+
+// SetGenesis sets a new genesis block header for the chain
+func (hc *HeaderChain) SetGenesis(head *types.Header) {
+ hc.genesisHeader = head
+}
+
+// Config retrieves the header chain's chain configuration.
+func (hc *HeaderChain) Config() *params.ChainConfig { return hc.config }
+
+// Engine retrieves the header chain's consensus engine.
+func (hc *HeaderChain) Engine() consensus.Engine { return hc.engine }
+
+// GetBlock implements consensus.ChainReader, and returns nil for every input as
+// a header chain does not have blocks available for retrieval.
+func (hc *HeaderChain) GetBlock(hash common.Hash, number uint64) *types.Block {
+ return nil
+}
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
new file mode 100644
index 000000000..fae60af2e
--- /dev/null
+++ b/core/rawdb/accessors_chain.go
@@ -0,0 +1,375 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rawdb
+
+import (
+ "bytes"
+ "encoding/binary"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
+func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash {
+ data, _ := db.Get(headerHashKey(number))
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteCanonicalHash stores the hash assigned to a canonical block number.
+func WriteCanonicalHash(db DatabaseWriter, hash common.Hash, number uint64) {
+ if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil {
+ log.Crit("Failed to store number to hash mapping", "err", err)
+ }
+}
+
+// DeleteCanonicalHash removes the number to hash canonical mapping.
+func DeleteCanonicalHash(db DatabaseDeleter, number uint64) {
+ if err := db.Delete(headerHashKey(number)); err != nil {
+ log.Crit("Failed to delete number to hash mapping", "err", err)
+ }
+}
+
+// ReadHeaderNumber returns the header number assigned to a hash.
+func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 {
+ data, _ := db.Get(headerNumberKey(hash))
+ if len(data) != 8 {
+ return nil
+ }
+ number := binary.BigEndian.Uint64(data)
+ return &number
+}
+
+// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
+func ReadHeadHeaderHash(db DatabaseReader) common.Hash {
+ data, _ := db.Get(headHeaderKey)
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteHeadHeaderHash stores the hash of the current canonical head header.
+func WriteHeadHeaderHash(db DatabaseWriter, hash common.Hash) {
+ if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
+ log.Crit("Failed to store last header's hash", "err", err)
+ }
+}
+
+// ReadHeadBlockHash retrieves the hash of the current canonical head block.
+func ReadHeadBlockHash(db DatabaseReader) common.Hash {
+ data, _ := db.Get(headBlockKey)
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteHeadBlockHash stores the head block's hash.
+func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) {
+ if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
+ log.Crit("Failed to store last block's hash", "err", err)
+ }
+}
+
+// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
+func ReadHeadFastBlockHash(db DatabaseReader) common.Hash {
+ data, _ := db.Get(headFastBlockKey)
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteHeadFastBlockHash stores the hash of the current fast-sync head block.
+func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) {
+ if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil {
+ log.Crit("Failed to store last fast block's hash", "err", err)
+ }
+}
+
+// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
+// reporting correct numbers across restarts.
+func ReadFastTrieProgress(db DatabaseReader) uint64 {
+ data, _ := db.Get(fastTrieProgressKey)
+ if len(data) == 0 {
+ return 0
+ }
+ return new(big.Int).SetBytes(data).Uint64()
+}
+
+// WriteFastTrieProgress stores the fast sync trie process counter to support
+// retrieving it across restarts.
+func WriteFastTrieProgress(db DatabaseWriter, count uint64) {
+ if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
+ log.Crit("Failed to store fast sync trie progress", "err", err)
+ }
+}
+
+// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
+func ReadHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
+ data, _ := db.Get(headerKey(number, hash))
+ return data
+}
+
+// HasHeader verifies the existence of a block header corresponding to the hash.
+func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool {
+ if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
+ return false
+ }
+ return true
+}
+
+// ReadHeader retrieves the block header corresponding to the hash.
+func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Header {
+ data := ReadHeaderRLP(db, hash, number)
+ if len(data) == 0 {
+ return nil
+ }
+ header := new(types.Header)
+ if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
+ log.Error("Invalid block header RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return header
+}
+
+// WriteHeader stores a block header into the database and also stores the hash-
+// to-number mapping.
+func WriteHeader(db DatabaseWriter, header *types.Header) {
+ // Write the hash -> number mapping
+ var (
+ hash = header.Hash()
+ number = header.Number.Uint64()
+ encoded = encodeBlockNumber(number)
+ )
+ key := headerNumberKey(hash)
+ if err := db.Put(key, encoded); err != nil {
+ log.Crit("Failed to store hash to number mapping", "err", err)
+ }
+ // Write the encoded header
+ data, err := rlp.EncodeToBytes(header)
+ if err != nil {
+ log.Crit("Failed to RLP encode header", "err", err)
+ }
+ key = headerKey(number, hash)
+ if err := db.Put(key, data); err != nil {
+ log.Crit("Failed to store header", "err", err)
+ }
+}
+
+// DeleteHeader removes all block header data associated with a hash.
+func DeleteHeader(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(headerKey(number, hash)); err != nil {
+ log.Crit("Failed to delete header", "err", err)
+ }
+ if err := db.Delete(headerNumberKey(hash)); err != nil {
+ log.Crit("Failed to delete hash to number mapping", "err", err)
+ }
+}
+
+// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
+func ReadBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
+ data, _ := db.Get(blockBodyKey(number, hash))
+ return data
+}
+
+// WriteBodyRLP stores an RLP encoded block body into the database.
+func WriteBodyRLP(db DatabaseWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
+ if err := db.Put(blockBodyKey(number, hash), rlp); err != nil {
+ log.Crit("Failed to store block body", "err", err)
+ }
+}
+
+// HasBody verifies the existence of a block body corresponding to the hash.
+func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool {
+ if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
+ return false
+ }
+ return true
+}
+
+// ReadBody retrieves the block body corresponding to the hash.
+func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body {
+ data := ReadBodyRLP(db, hash, number)
+ if len(data) == 0 {
+ return nil
+ }
+ body := new(types.Body)
+ if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
+ log.Error("Invalid block body RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return body
+}
+
+// WriteBody storea a block body into the database.
+func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.Body) {
+ data, err := rlp.EncodeToBytes(body)
+ if err != nil {
+ log.Crit("Failed to RLP encode body", "err", err)
+ }
+ WriteBodyRLP(db, hash, number, data)
+}
+
+// DeleteBody removes all block body data associated with a hash.
+func DeleteBody(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(blockBodyKey(number, hash)); err != nil {
+ log.Crit("Failed to delete block body", "err", err)
+ }
+}
+
+// ReadTd retrieves a block's total difficulty corresponding to the hash.
+func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int {
+ data, _ := db.Get(headerTDKey(number, hash))
+ if len(data) == 0 {
+ return nil
+ }
+ td := new(big.Int)
+ if err := rlp.Decode(bytes.NewReader(data), td); err != nil {
+ log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return td
+}
+
+// WriteTd stores the total difficulty of a block into the database.
+func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) {
+ data, err := rlp.EncodeToBytes(td)
+ if err != nil {
+ log.Crit("Failed to RLP encode block total difficulty", "err", err)
+ }
+ if err := db.Put(headerTDKey(number, hash), data); err != nil {
+ log.Crit("Failed to store block total difficulty", "err", err)
+ }
+}
+
+// DeleteTd removes all block total difficulty data associated with a hash.
+func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(headerTDKey(number, hash)); err != nil {
+ log.Crit("Failed to delete block total difficulty", "err", err)
+ }
+}
+
+// ReadReceipts retrieves all the transaction receipts belonging to a block.
+func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Receipts {
+ // Retrieve the flattened receipt slice
+ data, _ := db.Get(blockReceiptsKey(number, hash))
+ if len(data) == 0 {
+ return nil
+ }
+ // Convert the receipts from their storage form to their internal representation
+ storageReceipts := []*types.ReceiptForStorage{}
+ if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
+ log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
+ return nil
+ }
+ receipts := make(types.Receipts, len(storageReceipts))
+ for i, receipt := range storageReceipts {
+ receipts[i] = (*types.Receipt)(receipt)
+ }
+ return receipts
+}
+
+// WriteReceipts stores all the transaction receipts belonging to a block.
+func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts types.Receipts) {
+ // Convert the receipts into their storage form and serialize them
+ storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
+ for i, receipt := range receipts {
+ storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
+ }
+ bytes, err := rlp.EncodeToBytes(storageReceipts)
+ if err != nil {
+ log.Crit("Failed to encode block receipts", "err", err)
+ }
+ // Store the flattened receipt slice
+ if err := db.Put(blockReceiptsKey(number, hash), bytes); err != nil {
+ log.Crit("Failed to store block receipts", "err", err)
+ }
+}
+
+// DeleteReceipts removes all receipt data associated with a block hash.
+func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
+ log.Crit("Failed to delete block receipts", "err", err)
+ }
+}
+
+// ReadBlock retrieves an entire block corresponding to the hash, assembling it
+// back from the stored header and body. If either the header or body could not
+// be retrieved nil is returned.
+//
+// Note, due to concurrent download of header and block body the header and thus
+// canonical hash can be stored in the database but the body data not (yet).
+func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block {
+ header := ReadHeader(db, hash, number)
+ if header == nil {
+ return nil
+ }
+ body := ReadBody(db, hash, number)
+ if body == nil {
+ return nil
+ }
+ return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
+}
+
+// WriteBlock serializes a block into the database, header and body separately.
+func WriteBlock(db DatabaseWriter, block *types.Block) {
+ WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
+ WriteHeader(db, block.Header())
+}
+
+// DeleteBlock removes all block data associated with a hash.
+func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) {
+ DeleteReceipts(db, hash, number)
+ DeleteHeader(db, hash, number)
+ DeleteBody(db, hash, number)
+ DeleteTd(db, hash, number)
+}
+
+// FindCommonAncestor returns the last common ancestor of two block headers
+func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header {
+ for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
+ a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
+ if a == nil {
+ return nil
+ }
+ }
+ for an := a.Number.Uint64(); an < b.Number.Uint64(); {
+ b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
+ if b == nil {
+ return nil
+ }
+ }
+ for a.Hash() != b.Hash() {
+ a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
+ if a == nil {
+ return nil
+ }
+ b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
+ if b == nil {
+ return nil
+ }
+ }
+ return a
+}
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
new file mode 100644
index 000000000..7cc6991e0
--- /dev/null
+++ b/core/rawdb/accessors_chain_test.go
@@ -0,0 +1,319 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rawdb
+
+import (
+ "bytes"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto/sha3"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// Tests block header storage and retrieval operations.
+func TestHeaderStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ // Create a test header to move around the database and make sure it's really new
+ header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
+ if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
+ t.Fatalf("Non existent header returned: %v", entry)
+ }
+ // Write and verify the header in the database
+ WriteHeader(db, header)
+ if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
+ t.Fatalf("Stored header not found")
+ } else if entry.Hash() != header.Hash() {
+ t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header)
+ }
+ if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
+ t.Fatalf("Stored header RLP not found")
+ } else {
+ hasher := sha3.NewKeccak256()
+ hasher.Write(entry)
+
+ if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
+ t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header)
+ }
+ }
+ // Delete the header and verify the execution
+ DeleteHeader(db, header.Hash(), header.Number.Uint64())
+ if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
+ t.Fatalf("Deleted header returned: %v", entry)
+ }
+}
+
+// Tests block body storage and retrieval operations.
+func TestBodyStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ // Create a test body to move around the database and make sure it's really new
+ body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
+
+ hasher := sha3.NewKeccak256()
+ rlp.Encode(hasher, body)
+ hash := common.BytesToHash(hasher.Sum(nil))
+
+ if entry := ReadBody(db, hash, 0); entry != nil {
+ t.Fatalf("Non existent body returned: %v", entry)
+ }
+ // Write and verify the body in the database
+ WriteBody(db, hash, 0, body)
+ if entry := ReadBody(db, hash, 0); entry == nil {
+ t.Fatalf("Stored body not found")
+ } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
+ t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
+ }
+ if entry := ReadBodyRLP(db, hash, 0); entry == nil {
+ t.Fatalf("Stored body RLP not found")
+ } else {
+ hasher := sha3.NewKeccak256()
+ hasher.Write(entry)
+
+ if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
+ t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body)
+ }
+ }
+ // Delete the body and verify the execution
+ DeleteBody(db, hash, 0)
+ if entry := ReadBody(db, hash, 0); entry != nil {
+ t.Fatalf("Deleted body returned: %v", entry)
+ }
+}
+
+// Tests block storage and retrieval operations.
+func TestBlockStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ // Create a test block to move around the database and make sure it's really new
+ block := types.NewBlockWithHeader(&types.Header{
+ Extra: []byte("test block"),
+ UncleHash: types.EmptyUncleHash,
+ TxHash: types.EmptyRootHash,
+ ReceiptHash: types.EmptyRootHash,
+ })
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent block returned: %v", entry)
+ }
+ if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent header returned: %v", entry)
+ }
+ if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent body returned: %v", entry)
+ }
+ // Write and verify the block in the database
+ WriteBlock(db, block)
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
+ t.Fatalf("Stored block not found")
+ } else if entry.Hash() != block.Hash() {
+ t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
+ }
+ if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry == nil {
+ t.Fatalf("Stored header not found")
+ } else if entry.Hash() != block.Header().Hash() {
+ t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
+ }
+ if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil {
+ t.Fatalf("Stored body not found")
+ } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
+ t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
+ }
+ // Delete the block and verify the execution
+ DeleteBlock(db, block.Hash(), block.NumberU64())
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Deleted block returned: %v", entry)
+ }
+ if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Deleted header returned: %v", entry)
+ }
+ if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Deleted body returned: %v", entry)
+ }
+}
+
+// Tests that partial block contents don't get reassembled into full blocks.
+func TestPartialBlockStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+ block := types.NewBlockWithHeader(&types.Header{
+ Extra: []byte("test block"),
+ UncleHash: types.EmptyUncleHash,
+ TxHash: types.EmptyRootHash,
+ ReceiptHash: types.EmptyRootHash,
+ })
+ // Store a header and check that it's not recognized as a block
+ WriteHeader(db, block.Header())
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent block returned: %v", entry)
+ }
+ DeleteHeader(db, block.Hash(), block.NumberU64())
+
+ // Store a body and check that it's not recognized as a block
+ WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent block returned: %v", entry)
+ }
+ DeleteBody(db, block.Hash(), block.NumberU64())
+
+ // Store a header and a body separately and check reassembly
+ WriteHeader(db, block.Header())
+ WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
+
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
+ t.Fatalf("Stored block not found")
+ } else if entry.Hash() != block.Hash() {
+ t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
+ }
+}
+
+// Tests block total difficulty storage and retrieval operations.
+func TestTdStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ // Create a test TD to move around the database and make sure it's really new
+ hash, td := common.Hash{}, big.NewInt(314)
+ if entry := ReadTd(db, hash, 0); entry != nil {
+ t.Fatalf("Non existent TD returned: %v", entry)
+ }
+ // Write and verify the TD in the database
+ WriteTd(db, hash, 0, td)
+ if entry := ReadTd(db, hash, 0); entry == nil {
+ t.Fatalf("Stored TD not found")
+ } else if entry.Cmp(td) != 0 {
+ t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td)
+ }
+ // Delete the TD and verify the execution
+ DeleteTd(db, hash, 0)
+ if entry := ReadTd(db, hash, 0); entry != nil {
+ t.Fatalf("Deleted TD returned: %v", entry)
+ }
+}
+
+// Tests that canonical numbers can be mapped to hashes and retrieved.
+func TestCanonicalMappingStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ // Create a test canonical number and assinged hash to move around
+ hash, number := common.Hash{0: 0xff}, uint64(314)
+ if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) {
+ t.Fatalf("Non existent canonical mapping returned: %v", entry)
+ }
+ // Write and verify the TD in the database
+ WriteCanonicalHash(db, hash, number)
+ if entry := ReadCanonicalHash(db, number); entry == (common.Hash{}) {
+ t.Fatalf("Stored canonical mapping not found")
+ } else if entry != hash {
+ t.Fatalf("Retrieved canonical mapping mismatch: have %v, want %v", entry, hash)
+ }
+ // Delete the TD and verify the execution
+ DeleteCanonicalHash(db, number)
+ if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) {
+ t.Fatalf("Deleted canonical mapping returned: %v", entry)
+ }
+}
+
+// Tests that head headers and head blocks can be assigned, individually.
+func TestHeadStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
+ blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
+ blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")})
+
+ // Check that no head entries are in a pristine database
+ if entry := ReadHeadHeaderHash(db); entry != (common.Hash{}) {
+ t.Fatalf("Non head header entry returned: %v", entry)
+ }
+ if entry := ReadHeadBlockHash(db); entry != (common.Hash{}) {
+ t.Fatalf("Non head block entry returned: %v", entry)
+ }
+ if entry := ReadHeadFastBlockHash(db); entry != (common.Hash{}) {
+ t.Fatalf("Non fast head block entry returned: %v", entry)
+ }
+ // Assign separate entries for the head header and block
+ WriteHeadHeaderHash(db, blockHead.Hash())
+ WriteHeadBlockHash(db, blockFull.Hash())
+ WriteHeadFastBlockHash(db, blockFast.Hash())
+
+ // Check that both heads are present, and different (i.e. two heads maintained)
+ if entry := ReadHeadHeaderHash(db); entry != blockHead.Hash() {
+ t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash())
+ }
+ if entry := ReadHeadBlockHash(db); entry != blockFull.Hash() {
+ t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash())
+ }
+ if entry := ReadHeadFastBlockHash(db); entry != blockFast.Hash() {
+ t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash())
+ }
+}
+
+// Tests that receipts associated with a single block can be stored and retrieved.
+func TestBlockReceiptStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ receipt1 := &types.Receipt{
+ Status: types.ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x11})},
+ {Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ },
+ TxHash: common.BytesToHash([]byte{0x11, 0x11}),
+ ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
+ GasUsed: 111111,
+ }
+ receipt2 := &types.Receipt{
+ PostState: common.Hash{2}.Bytes(),
+ CumulativeGasUsed: 2,
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x22})},
+ {Address: common.BytesToAddress([]byte{0x02, 0x22})},
+ },
+ TxHash: common.BytesToHash([]byte{0x22, 0x22}),
+ ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
+ GasUsed: 222222,
+ }
+ receipts := []*types.Receipt{receipt1, receipt2}
+
+ // Check that no receipt entries are in a pristine database
+ hash := common.BytesToHash([]byte{0x03, 0x14})
+ if rs := ReadReceipts(db, hash, 0); len(rs) != 0 {
+ t.Fatalf("non existent receipts returned: %v", rs)
+ }
+ // Insert the receipt slice into the database and check presence
+ WriteReceipts(db, hash, 0, receipts)
+ if rs := ReadReceipts(db, hash, 0); len(rs) == 0 {
+ t.Fatalf("no receipts returned")
+ } else {
+ for i := 0; i < len(receipts); i++ {
+ rlpHave, _ := rlp.EncodeToBytes(rs[i])
+ rlpWant, _ := rlp.EncodeToBytes(receipts[i])
+
+ if !bytes.Equal(rlpHave, rlpWant) {
+ t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i])
+ }
+ }
+ }
+ // Delete the receipt slice and check purge
+ DeleteReceipts(db, hash, 0)
+ if rs := ReadReceipts(db, hash, 0); len(rs) != 0 {
+ t.Fatalf("deleted receipts returned: %v", rs)
+ }
+}
diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go
new file mode 100644
index 000000000..b664bf21b
--- /dev/null
+++ b/core/rawdb/accessors_indexes.go
@@ -0,0 +1,107 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rawdb
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
+// hash to allow retrieving the transaction or receipt by hash.
+func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) (common.Hash, uint64, uint64) {
+ data, _ := db.Get(txLookupKey(hash))
+ if len(data) == 0 {
+ return common.Hash{}, 0, 0
+ }
+ var entry TxLookupEntry
+ if err := rlp.DecodeBytes(data, &entry); err != nil {
+ log.Error("Invalid transaction lookup entry RLP", "hash", hash, "err", err)
+ return common.Hash{}, 0, 0
+ }
+ return entry.BlockHash, entry.BlockIndex, entry.Index
+}
+
+// WriteTxLookupEntries stores a positional metadata for every transaction from
+// a block, enabling hash based transaction and receipt lookups.
+func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) {
+ for i, tx := range block.Transactions() {
+ entry := TxLookupEntry{
+ BlockHash: block.Hash(),
+ BlockIndex: block.NumberU64(),
+ Index: uint64(i),
+ }
+ data, err := rlp.EncodeToBytes(entry)
+ if err != nil {
+ log.Crit("Failed to encode transaction lookup entry", "err", err)
+ }
+ if err := db.Put(txLookupKey(tx.Hash()), data); err != nil {
+ log.Crit("Failed to store transaction lookup entry", "err", err)
+ }
+ }
+}
+
+// DeleteTxLookupEntry removes all transaction data associated with a hash.
+func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) {
+ db.Delete(txLookupKey(hash))
+}
+
+// ReadTransaction retrieves a specific transaction from the database, along with
+// its added positional metadata.
+func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
+ blockHash, blockNumber, txIndex := ReadTxLookupEntry(db, hash)
+ if blockHash == (common.Hash{}) {
+ return nil, common.Hash{}, 0, 0
+ }
+ body := ReadBody(db, blockHash, blockNumber)
+ if body == nil || len(body.Transactions) <= int(txIndex) {
+ log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash, "index", txIndex)
+ return nil, common.Hash{}, 0, 0
+ }
+ return body.Transactions[txIndex], blockHash, blockNumber, txIndex
+}
+
+// ReadReceipt retrieves a specific transaction receipt from the database, along with
+// its added positional metadata.
+func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
+ blockHash, blockNumber, receiptIndex := ReadTxLookupEntry(db, hash)
+ if blockHash == (common.Hash{}) {
+ return nil, common.Hash{}, 0, 0
+ }
+ receipts := ReadReceipts(db, blockHash, blockNumber)
+ if len(receipts) <= int(receiptIndex) {
+ log.Error("Receipt refereced missing", "number", blockNumber, "hash", blockHash, "index", receiptIndex)
+ return nil, common.Hash{}, 0, 0
+ }
+ return receipts[receiptIndex], blockHash, blockNumber, receiptIndex
+}
+
+// ReadBloomBits retrieves the compressed bloom bit vector belonging to the given
+// section and bit index from the.
+func ReadBloomBits(db DatabaseReader, bit uint, section uint64, head common.Hash) ([]byte, error) {
+ return db.Get(bloomBitsKey(bit, section, head))
+}
+
+// WriteBloomBits stores the compressed bloom bits vector belonging to the given
+// section and bit index.
+func WriteBloomBits(db DatabaseWriter, bit uint, section uint64, head common.Hash, bits []byte) {
+ if err := db.Put(bloomBitsKey(bit, section, head), bits); err != nil {
+ log.Crit("Failed to store bloom bits", "err", err)
+ }
+}
diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go
new file mode 100644
index 000000000..d9c4c2f6d
--- /dev/null
+++ b/core/rawdb/accessors_indexes_test.go
@@ -0,0 +1,68 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rawdb
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// Tests that positional lookup metadata can be stored and retrieved.
+func TestLookupStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
+ tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
+ tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
+ txs := []*types.Transaction{tx1, tx2, tx3}
+
+ block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
+
+ // Check that no transactions entries are in a pristine database
+ for i, tx := range txs {
+ if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
+ t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
+ }
+ }
+ // Insert all the transactions into the database, and verify contents
+ WriteBlock(db, block)
+ WriteTxLookupEntries(db, block)
+
+ for i, tx := range txs {
+ if txn, hash, number, index := ReadTransaction(db, tx.Hash()); txn == nil {
+ t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
+ } else {
+ if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
+ t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
+ }
+ if tx.Hash() != txn.Hash() {
+ t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
+ }
+ }
+ }
+ // Delete the transactions and check purge
+ for i, tx := range txs {
+ DeleteTxLookupEntry(db, tx.Hash())
+ if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
+ t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
+ }
+ }
+}
diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go
new file mode 100644
index 000000000..514328e87
--- /dev/null
+++ b/core/rawdb/accessors_metadata.go
@@ -0,0 +1,90 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rawdb
+
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// ReadDatabaseVersion retrieves the version number of the database.
+func ReadDatabaseVersion(db DatabaseReader) int {
+ var version int
+
+ enc, _ := db.Get(databaseVerisionKey)
+ rlp.DecodeBytes(enc, &version)
+
+ return version
+}
+
+// WriteDatabaseVersion stores the version number of the database
+func WriteDatabaseVersion(db DatabaseWriter, version int) {
+ enc, _ := rlp.EncodeToBytes(version)
+ if err := db.Put(databaseVerisionKey, enc); err != nil {
+ log.Crit("Failed to store the database version", "err", err)
+ }
+}
+
+// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
+func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig {
+ data, _ := db.Get(configKey(hash))
+ if len(data) == 0 {
+ return nil
+ }
+ var config params.ChainConfig
+ if err := json.Unmarshal(data, &config); err != nil {
+ log.Error("Invalid chain config JSON", "hash", hash, "err", err)
+ return nil
+ }
+ return &config
+}
+
+// WriteChainConfig writes the chain config settings to the database.
+func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConfig) {
+ if cfg == nil {
+ return
+ }
+ data, err := json.Marshal(cfg)
+ if err != nil {
+ log.Crit("Failed to JSON encode chain config", "err", err)
+ }
+ if err := db.Put(configKey(hash), data); err != nil {
+ log.Crit("Failed to store chain config", "err", err)
+ }
+}
+
+// ReadPreimage retrieves a single preimage of the provided hash.
+func ReadPreimage(db DatabaseReader, hash common.Hash) []byte {
+ data, _ := db.Get(preimageKey(hash))
+ return data
+}
+
+// WritePreimages writes the provided set of preimages to the database. `number` is the
+// current block number, and is used for debug messages only.
+func WritePreimages(db DatabaseWriter, number uint64, preimages map[common.Hash][]byte) {
+ for hash, preimage := range preimages {
+ if err := db.Put(preimageKey(hash), preimage); err != nil {
+ log.Crit("Failed to store trie preimage", "err", err)
+ }
+ }
+ preimageCounter.Inc(int64(len(preimages)))
+ preimageHitCounter.Inc(int64(len(preimages)))
+}
diff --git a/core/rawdb/interfaces.go b/core/rawdb/interfaces.go
new file mode 100644
index 000000000..3bdf55124
--- /dev/null
+++ b/core/rawdb/interfaces.go
@@ -0,0 +1,33 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rawdb
+
+// DatabaseReader wraps the Has and Get method of a backing data store.
+type DatabaseReader interface {
+ Has(key []byte) (bool, error)
+ Get(key []byte) ([]byte, error)
+}
+
+// DatabaseWriter wraps the Put method of a backing data store.
+type DatabaseWriter interface {
+ Put(key []byte, value []byte) error
+}
+
+// DatabaseDeleter wraps the Delete method of a backing data store.
+type DatabaseDeleter interface {
+ Delete(key []byte) error
+}
diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go
new file mode 100644
index 000000000..ef597ef30
--- /dev/null
+++ b/core/rawdb/schema.go
@@ -0,0 +1,134 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package rawdb contains a collection of low level database accessors.
+package rawdb
+
+import (
+ "encoding/binary"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/metrics"
+)
+
+// The fields below define the low level database schema prefixing.
+var (
+ // databaseVerisionKey tracks the current database version.
+ databaseVerisionKey = []byte("DatabaseVersion")
+
+ // headHeaderKey tracks the latest know header's hash.
+ headHeaderKey = []byte("LastHeader")
+
+ // headBlockKey tracks the latest know full block's hash.
+ headBlockKey = []byte("LastBlock")
+
+ // headFastBlockKey tracks the latest known incomplete block's hash duirng fast sync.
+ headFastBlockKey = []byte("LastFast")
+
+ // fastTrieProgressKey tracks the number of trie entries imported during fast sync.
+ fastTrieProgressKey = []byte("TrieSync")
+
+ // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
+ headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
+ headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
+ headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
+ headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
+
+ blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
+ blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
+
+ txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
+ bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
+
+ preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
+ configPrefix = []byte("ethereum-config-") // config prefix for the db
+
+ // Chain index prefixes (use `i` + single byte to avoid mixing data types).
+ BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
+
+ preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
+ preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
+)
+
+// TxLookupEntry is a positional metadata to help looking up the data content of
+// a transaction or receipt given only its hash.
+type TxLookupEntry struct {
+ BlockHash common.Hash
+ BlockIndex uint64
+ Index uint64
+}
+
+// encodeBlockNumber encodes a block number as big endian uint64
+func encodeBlockNumber(number uint64) []byte {
+ enc := make([]byte, 8)
+ binary.BigEndian.PutUint64(enc, number)
+ return enc
+}
+
+// headerKey = headerPrefix + num (uint64 big endian) + hash
+func headerKey(number uint64, hash common.Hash) []byte {
+ return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+}
+
+// headerTDKey = headerPrefix + num (uint64 big endian) + hash + headerTDSuffix
+func headerTDKey(number uint64, hash common.Hash) []byte {
+ return append(headerKey(number, hash), headerTDSuffix...)
+}
+
+// headerHashKey = headerPrefix + num (uint64 big endian) + headerHashSuffix
+func headerHashKey(number uint64) []byte {
+ return append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)
+}
+
+// headerNumberKey = headerNumberPrefix + hash
+func headerNumberKey(hash common.Hash) []byte {
+ return append(headerNumberPrefix, hash.Bytes()...)
+}
+
+// blockBodyKey = blockBodyPrefix + num (uint64 big endian) + hash
+func blockBodyKey(number uint64, hash common.Hash) []byte {
+ return append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+}
+
+// blockReceiptsKey = blockReceiptsPrefix + num (uint64 big endian) + hash
+func blockReceiptsKey(number uint64, hash common.Hash) []byte {
+ return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+}
+
+// txLookupKey = txLookupPrefix + hash
+func txLookupKey(hash common.Hash) []byte {
+ return append(txLookupPrefix, hash.Bytes()...)
+}
+
+// bloomBitsKey = bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash
+func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte {
+ key := append(append(bloomBitsPrefix, make([]byte, 10)...), hash.Bytes()...)
+
+ binary.BigEndian.PutUint16(key[1:], uint16(bit))
+ binary.BigEndian.PutUint64(key[3:], section)
+
+ return key
+}
+
+// preimageKey = preimagePrefix + hash
+func preimageKey(hash common.Hash) []byte {
+ return append(preimagePrefix, hash.Bytes()...)
+}
+
+// configKey = configPrefix + hash
+func configKey(hash common.Hash) []byte {
+ return append(configPrefix, hash.Bytes()...)
+}
diff --git a/core/state_processor.go b/core/state_processor.go
new file mode 100644
index 000000000..290cdfee2
--- /dev/null
+++ b/core/state_processor.go
@@ -0,0 +1,126 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/misc"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// StateProcessor is a basic Processor, which takes care of transitioning
+// state from one point to another.
+//
+// StateProcessor implements Processor.
+type StateProcessor struct {
+ config *params.ChainConfig // Chain configuration options
+ bc *BlockChain // Canonical block chain
+ engine consensus.Engine // Consensus engine used for block rewards
+}
+
+// NewStateProcessor initialises a new StateProcessor.
+func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor {
+ return &StateProcessor{
+ config: config,
+ bc: bc,
+ engine: engine,
+ }
+}
+
+// Process processes the state changes according to the Ethereum rules by running
+// the transaction messages using the statedb and applying any rewards to both
+// the processor (coinbase) and any included uncles.
+//
+// Process returns the receipts and logs accumulated during the process and
+// returns the amount of gas that was used in the process. If any of the
+// transactions failed to execute due to insufficient gas it will return an error.
+func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
+ var (
+ receipts types.Receipts
+ usedGas = new(uint64)
+ header = block.Header()
+ allLogs []*types.Log
+ gp = new(GasPool).AddGas(block.GasLimit())
+ )
+ // Mutate the block and state according to any hard-fork specs
+ if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
+ misc.ApplyDAOHardFork(statedb)
+ }
+ // Iterate over and process the individual transactions
+ for i, tx := range block.Transactions() {
+ statedb.Prepare(tx.Hash(), block.Hash(), i)
+ receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg)
+ if err != nil {
+ return nil, nil, 0, err
+ }
+ receipts = append(receipts, receipt)
+ allLogs = append(allLogs, receipt.Logs...)
+ }
+ // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
+ p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts)
+
+ return receipts, allLogs, *usedGas, nil
+}
+
+// ApplyTransaction attempts to apply a transaction to the given state database
+// and uses the input parameters for its environment. It returns the receipt
+// for the transaction, gas used and an error if the transaction failed,
+// indicating the block was invalid.
+func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) {
+ msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
+ if err != nil {
+ return nil, 0, err
+ }
+ // Create a new context to be used in the EVM environment
+ context := NewEVMContext(msg, header, bc, author)
+ // Create a new environment which holds all relevant information
+ // about the transaction and calling mechanisms.
+ vmenv := vm.NewEVM(context, statedb, config, cfg)
+ // Apply the transaction to the current state (included in the env)
+ _, gas, failed, err := ApplyMessage(vmenv, msg, gp)
+ if err != nil {
+ return nil, 0, err
+ }
+ // Update the state with pending changes
+ var root []byte
+ if config.IsByzantium(header.Number) {
+ statedb.Finalise(true)
+ } else {
+ root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
+ }
+ *usedGas += gas
+
+ // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
+ // based on the eip phase, we're passing whether the root touch-delete accounts.
+ receipt := types.NewReceipt(root, failed, *usedGas)
+ receipt.TxHash = tx.Hash()
+ receipt.GasUsed = gas
+ // if the transaction created a contract, store the creation address in the receipt.
+ if msg.To() == nil {
+ receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
+ }
+ // Set the receipt logs and create a bloom for filtering
+ //receipt.Logs = statedb.GetLogs(tx.Hash())
+ receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
+
+ return receipt, gas, err
+}
diff --git a/core/state_transition.go b/core/state_transition.go
new file mode 100644
index 000000000..fda081b7d
--- /dev/null
+++ b/core/state_transition.go
@@ -0,0 +1,250 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "errors"
+ "math"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+var (
+ errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
+)
+
+/*
+The State Transitioning Model
+
+A state transition is a change made when a transaction is applied to the current world state
+The state transitioning model does all the necessary work to work out a valid new state root.
+
+1) Nonce handling
+2) Pre pay gas
+3) Create a new state object if the recipient is \0*32
+4) Value transfer
+== If contract creation ==
+ 4a) Attempt to run transaction data
+ 4b) If valid, use result as code for the new state object
+== end ==
+5) Run Script section
+6) Derive new state root
+*/
+type StateTransition struct {
+ gp *GasPool
+ msg Message
+ gas uint64
+ gasPrice *big.Int
+ initialGas uint64
+ value *big.Int
+ data []byte
+ state vm.StateDB
+ evm *vm.EVM
+}
+
+// Message represents a message sent to a contract.
+type Message interface {
+ From() common.Address
+ //FromFrontier() (common.Address, error)
+ To() *common.Address
+
+ GasPrice() *big.Int
+ Gas() uint64
+ Value() *big.Int
+
+ Nonce() uint64
+ CheckNonce() bool
+ Data() []byte
+}
+
+// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
+func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) {
+ // Set the starting gas for the raw transaction
+ var gas uint64
+ if contractCreation && homestead {
+ gas = params.TxGasContractCreation
+ } else {
+ gas = params.TxGas
+ }
+ // Bump the required gas by the amount of transactional data
+ if len(data) > 0 {
+ // Zero and non-zero bytes are priced differently
+ var nz uint64
+ for _, byt := range data {
+ if byt != 0 {
+ nz++
+ }
+ }
+ // Make sure we don't exceed uint64 for all data combinations
+ if (math.MaxUint64-gas)/params.TxDataNonZeroGas < nz {
+ return 0, vm.ErrOutOfGas
+ }
+ gas += nz * params.TxDataNonZeroGas
+
+ z := uint64(len(data)) - nz
+ if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
+ return 0, vm.ErrOutOfGas
+ }
+ gas += z * params.TxDataZeroGas
+ }
+ return gas, nil
+}
+
+// NewStateTransition initialises and returns a new state transition object.
+func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
+ return &StateTransition{
+ gp: gp,
+ evm: evm,
+ msg: msg,
+ gasPrice: msg.GasPrice(),
+ value: msg.Value(),
+ data: msg.Data(),
+ state: evm.StateDB,
+ }
+}
+
+// ApplyMessage computes the new state by applying the given message
+// against the old state within the environment.
+//
+// ApplyMessage returns the bytes returned by any EVM execution (if it took place),
+// the gas used (which includes gas refunds) and an error if it failed. An error always
+// indicates a core error meaning that the message would always fail for that particular
+// state and would never be accepted within a block.
+func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) {
+ return NewStateTransition(evm, msg, gp).TransitionDb()
+}
+
+// to returns the recipient of the message.
+func (st *StateTransition) to() common.Address {
+ if st.msg == nil || st.msg.To() == nil /* contract creation */ {
+ return common.Address{}
+ }
+ return *st.msg.To()
+}
+
+func (st *StateTransition) useGas(amount uint64) error {
+ if st.gas < amount {
+ return vm.ErrOutOfGas
+ }
+ st.gas -= amount
+
+ return nil
+}
+
+func (st *StateTransition) buyGas() error {
+ mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
+ if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
+ return errInsufficientBalanceForGas
+ }
+ if err := st.gp.SubGas(st.msg.Gas()); err != nil {
+ return err
+ }
+ st.gas += st.msg.Gas()
+
+ st.initialGas = st.msg.Gas()
+ st.state.SubBalance(st.msg.From(), mgval)
+ return nil
+}
+
+func (st *StateTransition) preCheck() error {
+ // Make sure this transaction's nonce is correct.
+ if st.msg.CheckNonce() {
+ nonce := st.state.GetNonce(st.msg.From())
+ if nonce < st.msg.Nonce() {
+ return ErrNonceTooHigh
+ } else if nonce > st.msg.Nonce() {
+ return ErrNonceTooLow
+ }
+ }
+ return st.buyGas()
+}
+
+// TransitionDb will transition the state by applying the current message and
+// returning the result including the used gas. It returns an error if failed.
+// An error indicates a consensus issue.
+func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
+ if err = st.preCheck(); err != nil {
+ return
+ }
+ msg := st.msg
+ sender := vm.AccountRef(msg.From())
+ homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
+ contractCreation := msg.To() == nil
+
+ // Pay intrinsic gas
+ gas, err := IntrinsicGas(st.data, contractCreation, homestead)
+ if err != nil {
+ return nil, 0, false, err
+ }
+ if err = st.useGas(gas); err != nil {
+ return nil, 0, false, err
+ }
+
+ var (
+ evm = st.evm
+ // vm errors do not effect consensus and are therefor
+ // not assigned to err, except for insufficient balance
+ // error.
+ vmerr error
+ )
+ if contractCreation {
+ ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
+ } else {
+ // Increment the nonce for the next transaction
+ st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
+ ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value)
+ }
+ if vmerr != nil {
+ log.Debug("VM returned with error", "err", vmerr)
+ // The only possible consensus-error would be if there wasn't
+ // sufficient balance to make the transfer happen. The first
+ // balance transfer may never fail.
+ if vmerr == vm.ErrInsufficientBalance {
+ return nil, 0, false, vmerr
+ }
+ }
+ st.refundGas()
+ st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
+
+ return ret, st.gasUsed(), vmerr != nil, err
+}
+
+func (st *StateTransition) refundGas() {
+ // Apply refund counter, capped to half of the used gas.
+ refund := st.gasUsed() / 2
+ if refund > st.state.GetRefund() {
+ refund = st.state.GetRefund()
+ }
+ st.gas += refund
+
+ // Return ETH for remaining gas, exchanged at the original rate.
+ remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
+ st.state.AddBalance(st.msg.From(), remaining)
+
+ // Also return remaining gas to the block gas counter so it is
+ // available for the next transaction.
+ st.gp.AddGas(st.gas)
+}
+
+// gasUsed returns the amount of gas used up by the state transition.
+func (st *StateTransition) gasUsed() uint64 {
+ return st.initialGas - st.gas
+}
diff --git a/core/tx_cacher.go b/core/tx_cacher.go
new file mode 100644
index 000000000..bcaa5ead3
--- /dev/null
+++ b/core/tx_cacher.go
@@ -0,0 +1,105 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "runtime"
+
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+// senderCacher is a concurrent transaction sender recoverer anc cacher.
+var senderCacher = newTxSenderCacher(runtime.NumCPU())
+
+// txSenderCacherRequest is a request for recovering transaction senders with a
+// specific signature scheme and caching it into the transactions themselves.
+//
+// The inc field defines the number of transactions to skip after each recovery,
+// which is used to feed the same underlying input array to different threads but
+// ensure they process the early transactions fast.
+type txSenderCacherRequest struct {
+ signer types.Signer
+ txs []*types.Transaction
+ inc int
+}
+
+// txSenderCacher is a helper structure to concurrently ecrecover transaction
+// senders from digital signatures on background threads.
+type txSenderCacher struct {
+ threads int
+ tasks chan *txSenderCacherRequest
+}
+
+// newTxSenderCacher creates a new transaction sender background cacher and starts
+// as many processing goroutines as allowed by the GOMAXPROCS on construction.
+func newTxSenderCacher(threads int) *txSenderCacher {
+ cacher := &txSenderCacher{
+ tasks: make(chan *txSenderCacherRequest, threads),
+ threads: threads,
+ }
+ for i := 0; i < threads; i++ {
+ go cacher.cache()
+ }
+ return cacher
+}
+
+// cache is an infinite loop, caching transaction senders from various forms of
+// data structures.
+func (cacher *txSenderCacher) cache() {
+ for task := range cacher.tasks {
+ for i := 0; i < len(task.txs); i += task.inc {
+ types.Sender(task.signer, task.txs[i])
+ }
+ }
+}
+
+// recover recovers the senders from a batch of transactions and caches them
+// back into the same data structures. There is no validation being done, nor
+// any reaction to invalid signatures. That is up to calling code later.
+func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transaction) {
+ // If there's nothing to recover, abort
+ if len(txs) == 0 {
+ return
+ }
+ // Ensure we have meaningful task sizes and schedule the recoveries
+ tasks := cacher.threads
+ if len(txs) < tasks*4 {
+ tasks = (len(txs) + 3) / 4
+ }
+ for i := 0; i < tasks; i++ {
+ cacher.tasks <- &txSenderCacherRequest{
+ signer: signer,
+ txs: txs[i:],
+ inc: tasks,
+ }
+ }
+}
+
+// recoverFromBlocks recovers the senders from a batch of blocks and caches them
+// back into the same data structures. There is no validation being done, nor
+// any reaction to invalid signatures. That is up to calling code later.
+func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*types.Block) {
+ count := 0
+ for _, block := range blocks {
+ count += len(block.Transactions())
+ }
+ txs := make([]*types.Transaction, 0, count)
+ for _, block := range blocks {
+ txs = append(txs, block.Transactions()...)
+ }
+ cacher.recover(signer, txs)
+}
diff --git a/core/tx_journal.go b/core/tx_journal.go
new file mode 100644
index 000000000..3d6c1fac5
--- /dev/null
+++ b/core/tx_journal.go
@@ -0,0 +1,180 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "errors"
+ "io"
+ "os"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// errNoActiveJournal is returned if a transaction is attempted to be inserted
+// into the journal, but no such file is currently open.
+var errNoActiveJournal = errors.New("no active journal")
+
+// devNull is a WriteCloser that just discards anything written into it. Its
+// goal is to allow the transaction journal to write into a fake journal when
+// loading transactions on startup without printing warnings due to no file
+// being read for write.
+type devNull struct{}
+
+func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil }
+func (*devNull) Close() error { return nil }
+
+// txJournal is a rotating log of transactions with the aim of storing locally
+// created transactions to allow non-executed ones to survive node restarts.
+type txJournal struct {
+ path string // Filesystem path to store the transactions at
+ writer io.WriteCloser // Output stream to write new transactions into
+}
+
+// newTxJournal creates a new transaction journal to
+func newTxJournal(path string) *txJournal {
+ return &txJournal{
+ path: path,
+ }
+}
+
+// load parses a transaction journal dump from disk, loading its contents into
+// the specified pool.
+func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
+ // Skip the parsing if the journal file doesn't exist at all
+ if _, err := os.Stat(journal.path); os.IsNotExist(err) {
+ return nil
+ }
+ // Open the journal for loading any past transactions
+ input, err := os.Open(journal.path)
+ if err != nil {
+ return err
+ }
+ defer input.Close()
+
+ // Temporarily discard any journal additions (don't double add on load)
+ journal.writer = new(devNull)
+ defer func() { journal.writer = nil }()
+
+ // Inject all transactions from the journal into the pool
+ stream := rlp.NewStream(input, 0)
+ total, dropped := 0, 0
+
+ // Create a method to load a limited batch of transactions and bump the
+ // appropriate progress counters. Then use this method to load all the
+ // journaled transactions in small-ish batches.
+ loadBatch := func(txs types.Transactions) {
+ for _, err := range add(txs) {
+ if err != nil {
+ log.Debug("Failed to add journaled transaction", "err", err)
+ dropped++
+ }
+ }
+ }
+ var (
+ failure error
+ batch types.Transactions
+ )
+ for {
+ // Parse the next transaction and terminate on error
+ tx := new(types.Transaction)
+ if err = stream.Decode(tx); err != nil {
+ if err != io.EOF {
+ failure = err
+ }
+ if batch.Len() > 0 {
+ loadBatch(batch)
+ }
+ break
+ }
+ // New transaction parsed, queue up for later, import if threshold is reached
+ total++
+
+ if batch = append(batch, tx); batch.Len() > 1024 {
+ loadBatch(batch)
+ batch = batch[:0]
+ }
+ }
+ log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped)
+
+ return failure
+}
+
+// insert adds the specified transaction to the local disk journal.
+func (journal *txJournal) insert(tx *types.Transaction) error {
+ if journal.writer == nil {
+ return errNoActiveJournal
+ }
+ if err := rlp.Encode(journal.writer, tx); err != nil {
+ return err
+ }
+ return nil
+}
+
+// rotate regenerates the transaction journal based on the current contents of
+// the transaction pool.
+func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error {
+ // Close the current journal (if any is open)
+ if journal.writer != nil {
+ if err := journal.writer.Close(); err != nil {
+ return err
+ }
+ journal.writer = nil
+ }
+ // Generate a new journal with the contents of the current pool
+ replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
+ if err != nil {
+ return err
+ }
+ journaled := 0
+ for _, txs := range all {
+ for _, tx := range txs {
+ if err = rlp.Encode(replacement, tx); err != nil {
+ replacement.Close()
+ return err
+ }
+ }
+ journaled += len(txs)
+ }
+ replacement.Close()
+
+ // Replace the live journal with the newly generated one
+ if err = os.Rename(journal.path+".new", journal.path); err != nil {
+ return err
+ }
+ sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0755)
+ if err != nil {
+ return err
+ }
+ journal.writer = sink
+ log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all))
+
+ return nil
+}
+
+// close flushes the transaction journal contents to disk and closes the file.
+func (journal *txJournal) close() error {
+ var err error
+
+ if journal.writer != nil {
+ err = journal.writer.Close()
+ journal.writer = nil
+ }
+ return err
+}
diff --git a/core/tx_list.go b/core/tx_list.go
new file mode 100644
index 000000000..a107057f4
--- /dev/null
+++ b/core/tx_list.go
@@ -0,0 +1,520 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "container/heap"
+ "math"
+ "math/big"
+ "sort"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
+// retrieving sorted transactions from the possibly gapped future queue.
+type nonceHeap []uint64
+
+func (h nonceHeap) Len() int { return len(h) }
+func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] }
+func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
+
+func (h *nonceHeap) Push(x interface{}) {
+ *h = append(*h, x.(uint64))
+}
+
+func (h *nonceHeap) Pop() interface{} {
+ old := *h
+ n := len(old)
+ x := old[n-1]
+ *h = old[0 : n-1]
+ return x
+}
+
+// txSortedMap is a nonce->transaction hash map with a heap based index to allow
+// iterating over the contents in a nonce-incrementing way.
+type txSortedMap struct {
+ items map[uint64]*types.Transaction // Hash map storing the transaction data
+ index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
+ cache types.Transactions // Cache of the transactions already sorted
+}
+
+// newTxSortedMap creates a new nonce-sorted transaction map.
+func newTxSortedMap() *txSortedMap {
+ return &txSortedMap{
+ items: make(map[uint64]*types.Transaction),
+ index: new(nonceHeap),
+ }
+}
+
+// Get retrieves the current transactions associated with the given nonce.
+func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
+ return m.items[nonce]
+}
+
+// Put inserts a new transaction into the map, also updating the map's nonce
+// index. If a transaction already exists with the same nonce, it's overwritten.
+func (m *txSortedMap) Put(tx *types.Transaction) {
+ nonce := tx.Nonce()
+ if m.items[nonce] == nil {
+ heap.Push(m.index, nonce)
+ }
+ m.items[nonce], m.cache = tx, nil
+}
+
+// Forward removes all transactions from the map with a nonce lower than the
+// provided threshold. Every removed transaction is returned for any post-removal
+// maintenance.
+func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
+ var removed types.Transactions
+
+ // Pop off heap items until the threshold is reached
+ for m.index.Len() > 0 && (*m.index)[0] < threshold {
+ nonce := heap.Pop(m.index).(uint64)
+ removed = append(removed, m.items[nonce])
+ delete(m.items, nonce)
+ }
+ // If we had a cached order, shift the front
+ if m.cache != nil {
+ m.cache = m.cache[len(removed):]
+ }
+ return removed
+}
+
+// Filter iterates over the list of transactions and removes all of them for which
+// the specified function evaluates to true.
+func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
+ var removed types.Transactions
+
+ // Collect all the transactions to filter out
+ for nonce, tx := range m.items {
+ if filter(tx) {
+ removed = append(removed, tx)
+ delete(m.items, nonce)
+ }
+ }
+ // If transactions were removed, the heap and cache are ruined
+ if len(removed) > 0 {
+ *m.index = make([]uint64, 0, len(m.items))
+ for nonce := range m.items {
+ *m.index = append(*m.index, nonce)
+ }
+ heap.Init(m.index)
+
+ m.cache = nil
+ }
+ return removed
+}
+
+// Cap places a hard limit on the number of items, returning all transactions
+// exceeding that limit.
+func (m *txSortedMap) Cap(threshold int) types.Transactions {
+ // Short circuit if the number of items is under the limit
+ if len(m.items) <= threshold {
+ return nil
+ }
+ // Otherwise gather and drop the highest nonce'd transactions
+ var drops types.Transactions
+
+ sort.Sort(*m.index)
+ for size := len(m.items); size > threshold; size-- {
+ drops = append(drops, m.items[(*m.index)[size-1]])
+ delete(m.items, (*m.index)[size-1])
+ }
+ *m.index = (*m.index)[:threshold]
+ heap.Init(m.index)
+
+ // If we had a cache, shift the back
+ if m.cache != nil {
+ m.cache = m.cache[:len(m.cache)-len(drops)]
+ }
+ return drops
+}
+
+// Remove deletes a transaction from the maintained map, returning whether the
+// transaction was found.
+func (m *txSortedMap) Remove(nonce uint64) bool {
+ // Short circuit if no transaction is present
+ _, ok := m.items[nonce]
+ if !ok {
+ return false
+ }
+ // Otherwise delete the transaction and fix the heap index
+ for i := 0; i < m.index.Len(); i++ {
+ if (*m.index)[i] == nonce {
+ heap.Remove(m.index, i)
+ break
+ }
+ }
+ delete(m.items, nonce)
+ m.cache = nil
+
+ return true
+}
+
+// Ready retrieves a sequentially increasing list of transactions starting at the
+// provided nonce that is ready for processing. The returned transactions will be
+// removed from the list.
+//
+// Note, all transactions with nonces lower than start will also be returned to
+// prevent getting into and invalid state. This is not something that should ever
+// happen but better to be self correcting than failing!
+func (m *txSortedMap) Ready(start uint64) types.Transactions {
+ // Short circuit if no transactions are available
+ if m.index.Len() == 0 || (*m.index)[0] > start {
+ return nil
+ }
+ // Otherwise start accumulating incremental transactions
+ var ready types.Transactions
+ for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ {
+ ready = append(ready, m.items[next])
+ delete(m.items, next)
+ heap.Pop(m.index)
+ }
+ m.cache = nil
+
+ return ready
+}
+
+// Len returns the length of the transaction map.
+func (m *txSortedMap) Len() int {
+ return len(m.items)
+}
+
+// Flatten creates a nonce-sorted slice of transactions based on the loosely
+// sorted internal representation. The result of the sorting is cached in case
+// it's requested again before any modifications are made to the contents.
+func (m *txSortedMap) Flatten() types.Transactions {
+ // If the sorting was not cached yet, create and cache it
+ if m.cache == nil {
+ m.cache = make(types.Transactions, 0, len(m.items))
+ for _, tx := range m.items {
+ m.cache = append(m.cache, tx)
+ }
+ sort.Sort(types.TxByNonce(m.cache))
+ }
+ // Copy the cache to prevent accidental modifications
+ txs := make(types.Transactions, len(m.cache))
+ copy(txs, m.cache)
+ return txs
+}
+
+// txList is a "list" of transactions belonging to an account, sorted by account
+// nonce. The same type can be used both for storing contiguous transactions for
+// the executable/pending queue; and for storing gapped transactions for the non-
+// executable/future queue, with minor behavioral changes.
+type txList struct {
+ strict bool // Whether nonces are strictly continuous or not
+ txs *txSortedMap // Heap indexed sorted hash map of the transactions
+
+ costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
+ gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
+}
+
+// newTxList create a new transaction list for maintaining nonce-indexable fast,
+// gapped, sortable transaction lists.
+func newTxList(strict bool) *txList {
+ return &txList{
+ strict: strict,
+ txs: newTxSortedMap(),
+ costcap: new(big.Int),
+ }
+}
+
+// Overlaps returns whether the transaction specified has the same nonce as one
+// already contained within the list.
+func (l *txList) Overlaps(tx *types.Transaction) bool {
+ return l.txs.Get(tx.Nonce()) != nil
+}
+
+// Add tries to insert a new transaction into the list, returning whether the
+// transaction was accepted, and if yes, any previous transaction it replaced.
+//
+// If the new transaction is accepted into the list, the lists' cost and gas
+// thresholds are also potentially updated.
+func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
+ // If there's an older better transaction, abort
+ old := l.txs.Get(tx.Nonce())
+ if old != nil {
+ threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100))
+ // Have to ensure that the new gas price is higher than the old gas
+ // price as well as checking the percentage threshold to ensure that
+ // this is accurate for low (Wei-level) gas price replacements
+ if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 {
+ return false, nil
+ }
+ }
+ // Otherwise overwrite the old transaction with the current one
+ l.txs.Put(tx)
+ if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
+ l.costcap = cost
+ }
+ if gas := tx.Gas(); l.gascap < gas {
+ l.gascap = gas
+ }
+ return true, old
+}
+
+// Forward removes all transactions from the list with a nonce lower than the
+// provided threshold. Every removed transaction is returned for any post-removal
+// maintenance.
+func (l *txList) Forward(threshold uint64) types.Transactions {
+ return l.txs.Forward(threshold)
+}
+
+// Filter removes all transactions from the list with a cost or gas limit higher
+// than the provided thresholds. Every removed transaction is returned for any
+// post-removal maintenance. Strict-mode invalidated transactions are also
+// returned.
+//
+// This method uses the cached costcap and gascap to quickly decide if there's even
+// a point in calculating all the costs or if the balance covers all. If the threshold
+// is lower than the costgas cap, the caps will be reset to a new high after removing
+// the newly invalidated transactions.
+func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
+ // If all transactions are below the threshold, short circuit
+ if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
+ return nil, nil
+ }
+ l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
+ l.gascap = gasLimit
+
+ // Filter out all the transactions above the account's funds
+ removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas() > gasLimit })
+
+ // If the list was strict, filter anything above the lowest nonce
+ var invalids types.Transactions
+
+ if l.strict && len(removed) > 0 {
+ lowest := uint64(math.MaxUint64)
+ for _, tx := range removed {
+ if nonce := tx.Nonce(); lowest > nonce {
+ lowest = nonce
+ }
+ }
+ invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
+ }
+ return removed, invalids
+}
+
+// Cap places a hard limit on the number of items, returning all transactions
+// exceeding that limit.
+func (l *txList) Cap(threshold int) types.Transactions {
+ return l.txs.Cap(threshold)
+}
+
+// Remove deletes a transaction from the maintained list, returning whether the
+// transaction was found, and also returning any transaction invalidated due to
+// the deletion (strict mode only).
+func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
+ // Remove the transaction from the set
+ nonce := tx.Nonce()
+ if removed := l.txs.Remove(nonce); !removed {
+ return false, nil
+ }
+ // In strict mode, filter out non-executable transactions
+ if l.strict {
+ return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce })
+ }
+ return true, nil
+}
+
+// Ready retrieves a sequentially increasing list of transactions starting at the
+// provided nonce that is ready for processing. The returned transactions will be
+// removed from the list.
+//
+// Note, all transactions with nonces lower than start will also be returned to
+// prevent getting into and invalid state. This is not something that should ever
+// happen but better to be self correcting than failing!
+func (l *txList) Ready(start uint64) types.Transactions {
+ return l.txs.Ready(start)
+}
+
+// Len returns the length of the transaction list.
+func (l *txList) Len() int {
+ return l.txs.Len()
+}
+
+// Empty returns whether the list of transactions is empty or not.
+func (l *txList) Empty() bool {
+ return l.Len() == 0
+}
+
+// Flatten creates a nonce-sorted slice of transactions based on the loosely
+// sorted internal representation. The result of the sorting is cached in case
+// it's requested again before any modifications are made to the contents.
+func (l *txList) Flatten() types.Transactions {
+ return l.txs.Flatten()
+}
+
+// priceHeap is a heap.Interface implementation over transactions for retrieving
+// price-sorted transactions to discard when the pool fills up.
+type priceHeap []*types.Transaction
+
+func (h priceHeap) Len() int { return len(h) }
+func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
+
+func (h priceHeap) Less(i, j int) bool {
+ // Sort primarily by price, returning the cheaper one
+ switch h[i].GasPrice().Cmp(h[j].GasPrice()) {
+ case -1:
+ return true
+ case 1:
+ return false
+ }
+ // If the prices match, stabilize via nonces (high nonce is worse)
+ return h[i].Nonce() > h[j].Nonce()
+}
+
+func (h *priceHeap) Push(x interface{}) {
+ *h = append(*h, x.(*types.Transaction))
+}
+
+func (h *priceHeap) Pop() interface{} {
+ old := *h
+ n := len(old)
+ x := old[n-1]
+ *h = old[0 : n-1]
+ return x
+}
+
+// txPricedList is a price-sorted heap to allow operating on transactions pool
+// contents in a price-incrementing way.
+type txPricedList struct {
+ all *txLookup // Pointer to the map of all transactions
+ items *priceHeap // Heap of prices of all the stored transactions
+ stales int // Number of stale price points to (re-heap trigger)
+}
+
+// newTxPricedList creates a new price-sorted transaction heap.
+func newTxPricedList(all *txLookup) *txPricedList {
+ return &txPricedList{
+ all: all,
+ items: new(priceHeap),
+ }
+}
+
+// Put inserts a new transaction into the heap.
+func (l *txPricedList) Put(tx *types.Transaction) {
+ heap.Push(l.items, tx)
+}
+
+// Removed notifies the prices transaction list that an old transaction dropped
+// from the pool. The list will just keep a counter of stale objects and update
+// the heap if a large enough ratio of transactions go stale.
+func (l *txPricedList) Removed() {
+ // Bump the stale counter, but exit if still too low (< 25%)
+ l.stales++
+ if l.stales <= len(*l.items)/4 {
+ return
+ }
+ // Seems we've reached a critical number of stale transactions, reheap
+ reheap := make(priceHeap, 0, l.all.Count())
+
+ l.stales, l.items = 0, &reheap
+ l.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
+ *l.items = append(*l.items, tx)
+ return true
+ })
+ heap.Init(l.items)
+}
+
+// Cap finds all the transactions below the given price threshold, drops them
+// from the priced list and returns them for further removal from the entire pool.
+func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transactions {
+ drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop
+ save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
+
+ for len(*l.items) > 0 {
+ // Discard stale transactions if found during cleanup
+ tx := heap.Pop(l.items).(*types.Transaction)
+ if l.all.Get(tx.Hash()) == nil {
+ l.stales--
+ continue
+ }
+ // Stop the discards if we've reached the threshold
+ if tx.GasPrice().Cmp(threshold) >= 0 {
+ save = append(save, tx)
+ break
+ }
+ // Non stale transaction found, discard unless local
+ if local.containsTx(tx) {
+ save = append(save, tx)
+ } else {
+ drop = append(drop, tx)
+ }
+ }
+ for _, tx := range save {
+ heap.Push(l.items, tx)
+ }
+ return drop
+}
+
+// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
+// lowest priced transaction currently being tracked.
+func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) bool {
+ // Local transactions cannot be underpriced
+ if local.containsTx(tx) {
+ return false
+ }
+ // Discard stale price points if found at the heap start
+ for len(*l.items) > 0 {
+ head := []*types.Transaction(*l.items)[0]
+ if l.all.Get(head.Hash()) == nil {
+ l.stales--
+ heap.Pop(l.items)
+ continue
+ }
+ break
+ }
+ // Check if the transaction is underpriced or not
+ if len(*l.items) == 0 {
+ log.Error("Pricing query for empty pool") // This cannot happen, print to catch programming errors
+ return false
+ }
+ cheapest := []*types.Transaction(*l.items)[0]
+ return cheapest.GasPrice().Cmp(tx.GasPrice()) >= 0
+}
+
+// Discard finds a number of most underpriced transactions, removes them from the
+// priced list and returns them for further removal from the entire pool.
+func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions {
+ drop := make(types.Transactions, 0, count) // Remote underpriced transactions to drop
+ save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
+
+ for len(*l.items) > 0 && count > 0 {
+ // Discard stale transactions if found during cleanup
+ tx := heap.Pop(l.items).(*types.Transaction)
+ if l.all.Get(tx.Hash()) == nil {
+ l.stales--
+ continue
+ }
+ // Non stale transaction found, discard unless local
+ if local.containsTx(tx) {
+ save = append(save, tx)
+ } else {
+ drop = append(drop, tx)
+ count--
+ }
+ }
+ for _, tx := range save {
+ heap.Push(l.items, tx)
+ }
+ return drop
+}
diff --git a/core/tx_list_test.go b/core/tx_list_test.go
new file mode 100644
index 000000000..1200c6b02
--- /dev/null
+++ b/core/tx_list_test.go
@@ -0,0 +1,51 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "math/rand"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// Tests that transactions can be added to strict lists and list contents and
+// nonce boundaries are correctly maintained.
+func TestStrictTxListAdd(t *testing.T) {
+ // Generate a list of transactions to insert
+ key, _ := crypto.GenerateKey()
+
+ txs := make(types.Transactions, 1024)
+ for i := 0; i < len(txs); i++ {
+ txs[i] = transaction(uint64(i), 0, key)
+ }
+ // Insert the transactions in a random order
+ list := newTxList(true)
+ for _, v := range rand.Perm(len(txs)) {
+ list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
+ }
+ // Verify internal state
+ if len(list.txs.items) != len(txs) {
+ t.Errorf("transaction count mismatch: have %d, want %d", len(list.txs.items), len(txs))
+ }
+ for i, tx := range txs {
+ if list.txs.items[tx.Nonce()] != tx {
+ t.Errorf("item %d: transaction mismatch: have %v, want %v", i, list.txs.items[tx.Nonce()], tx)
+ }
+ }
+}
diff --git a/core/tx_pool.go b/core/tx_pool.go
new file mode 100644
index 000000000..6b7fa4ff0
--- /dev/null
+++ b/core/tx_pool.go
@@ -0,0 +1,1266 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/prque"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+const (
+ // chainHeadChanSize is the size of channel listening to ChainHeadEvent.
+ chainHeadChanSize = 10
+)
+
+var (
+ // ErrInvalidSender is returned if the transaction contains an invalid signature.
+ ErrInvalidSender = errors.New("invalid sender")
+
+ // ErrNonceTooLow is returned if the nonce of a transaction is lower than the
+ // one present in the local chain.
+ ErrNonceTooLow = errors.New("nonce too low")
+
+ // ErrUnderpriced is returned if a transaction's gas price is below the minimum
+ // configured for the transaction pool.
+ ErrUnderpriced = errors.New("transaction underpriced")
+
+ // ErrReplaceUnderpriced is returned if a transaction is attempted to be replaced
+ // with a different one without the required price bump.
+ ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
+
+ // ErrInsufficientFunds is returned if the total cost of executing a transaction
+ // is higher than the balance of the user's account.
+ ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
+
+ // ErrIntrinsicGas is returned if the transaction is specified to use less gas
+ // than required to start the invocation.
+ ErrIntrinsicGas = errors.New("intrinsic gas too low")
+
+ // ErrGasLimit is returned if a transaction's requested gas limit exceeds the
+ // maximum allowance of the current block.
+ ErrGasLimit = errors.New("exceeds block gas limit")
+
+ // ErrNegativeValue is a sanity error to ensure noone is able to specify a
+ // transaction with a negative value.
+ ErrNegativeValue = errors.New("negative value")
+
+ // ErrOversizedData is returned if the input data of a transaction is greater
+ // than some meaningful limit a user might use. This is not a consensus error
+ // making the transaction invalid, rather a DOS protection.
+ ErrOversizedData = errors.New("oversized data")
+)
+
+var (
+ evictionInterval = time.Minute // Time interval to check for evictable transactions
+ statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats
+)
+
+var (
+ // Metrics for the pending pool
+ pendingDiscardCounter = metrics.NewRegisteredCounter("txpool/pending/discard", nil)
+ pendingReplaceCounter = metrics.NewRegisteredCounter("txpool/pending/replace", nil)
+ pendingRateLimitCounter = metrics.NewRegisteredCounter("txpool/pending/ratelimit", nil) // Dropped due to rate limiting
+ pendingNofundsCounter = metrics.NewRegisteredCounter("txpool/pending/nofunds", nil) // Dropped due to out-of-funds
+
+ // Metrics for the queued pool
+ queuedDiscardCounter = metrics.NewRegisteredCounter("txpool/queued/discard", nil)
+ queuedReplaceCounter = metrics.NewRegisteredCounter("txpool/queued/replace", nil)
+ queuedRateLimitCounter = metrics.NewRegisteredCounter("txpool/queued/ratelimit", nil) // Dropped due to rate limiting
+ queuedNofundsCounter = metrics.NewRegisteredCounter("txpool/queued/nofunds", nil) // Dropped due to out-of-funds
+
+ // General tx metrics
+ invalidTxCounter = metrics.NewRegisteredCounter("txpool/invalid", nil)
+ underpricedTxCounter = metrics.NewRegisteredCounter("txpool/underpriced", nil)
+)
+
+// TxStatus is the current status of a transaction as seen by the pool.
+type TxStatus uint
+
+const (
+ TxStatusUnknown TxStatus = iota
+ TxStatusQueued
+ TxStatusPending
+ TxStatusIncluded
+)
+
+// blockChain provides the state of blockchain and current gas limit to do
+// some pre checks in tx pool and event subscribers.
+type blockChain interface {
+ CurrentBlock() *types.Block
+ GetBlock(hash common.Hash, number uint64) *types.Block
+ StateAt(root common.Hash) (*state.StateDB, error)
+
+ SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
+}
+
+// TxPoolConfig are the configuration parameters of the transaction pool.
+type TxPoolConfig struct {
+ Locals []common.Address // Addresses that should be treated by default as local
+ NoLocals bool // Whether local transaction handling should be disabled
+ Journal string // Journal of local transactions to survive node restarts
+ Rejournal time.Duration // Time interval to regenerate the local transaction journal
+
+ PriceLimit uint64 // Minimum gas price to enforce for acceptance into the pool
+ PriceBump uint64 // Minimum price bump percentage to replace an already existing transaction (nonce)
+
+ AccountSlots uint64 // Number of executable transaction slots guaranteed per account
+ GlobalSlots uint64 // Maximum number of executable transaction slots for all accounts
+ AccountQueue uint64 // Maximum number of non-executable transaction slots permitted per account
+ GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts
+
+ Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
+}
+
+// DefaultTxPoolConfig contains the default configurations for the transaction
+// pool.
+var DefaultTxPoolConfig = TxPoolConfig{
+ Journal: "transactions.rlp",
+ Rejournal: time.Hour,
+
+ PriceLimit: 1,
+ PriceBump: 10,
+
+ AccountSlots: 16,
+ GlobalSlots: 4096,
+ AccountQueue: 64,
+ GlobalQueue: 1024,
+
+ Lifetime: 3 * time.Hour,
+}
+
+// sanitize checks the provided user configurations and changes anything that's
+// unreasonable or unworkable.
+func (config *TxPoolConfig) sanitize() TxPoolConfig {
+ conf := *config
+ if conf.Rejournal < time.Second {
+ log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
+ conf.Rejournal = time.Second
+ }
+ if conf.PriceLimit < 1 {
+ log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit)
+ conf.PriceLimit = DefaultTxPoolConfig.PriceLimit
+ }
+ if conf.PriceBump < 1 {
+ log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump)
+ conf.PriceBump = DefaultTxPoolConfig.PriceBump
+ }
+ return conf
+}
+
+// TxPool contains all currently known transactions. Transactions
+// enter the pool when they are received from the network or submitted
+// locally. They exit the pool when they are included in the blockchain.
+//
+// The pool separates processable transactions (which can be applied to the
+// current state) and future transactions. Transactions move between those
+// two states over time as they are received and processed.
+type TxPool struct {
+ config TxPoolConfig
+ chainconfig *params.ChainConfig
+ chain blockChain
+ gasPrice *big.Int
+ txFeed event.Feed
+ scope event.SubscriptionScope
+ chainHeadCh chan ChainHeadEvent
+ chainHeadSub event.Subscription
+ signer types.Signer
+ mu sync.RWMutex
+
+ currentState *state.StateDB // Current state in the blockchain head
+ pendingState *state.ManagedState // Pending state tracking virtual nonces
+ currentMaxGas uint64 // Current gas limit for transaction caps
+
+ locals *accountSet // Set of local transaction to exempt from eviction rules
+ journal *txJournal // Journal of local transaction to back up to disk
+
+ pending map[common.Address]*txList // All currently processable transactions
+ queue map[common.Address]*txList // Queued but non-processable transactions
+ beats map[common.Address]time.Time // Last heartbeat from each known account
+ all *txLookup // All transactions to allow lookups
+ priced *txPricedList // All transactions sorted by price
+
+ wg sync.WaitGroup // for shutdown sync
+
+ homestead bool
+}
+
+// NewTxPool creates a new transaction pool to gather, sort and filter inbound
+// transactions from the network.
+func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
+ // Sanitize the input to ensure no vulnerable gas prices are set
+ config = (&config).sanitize()
+
+ // Create the transaction pool with its initial settings
+ pool := &TxPool{
+ config: config,
+ chainconfig: chainconfig,
+ chain: chain,
+ signer: types.NewEIP155Signer(chainconfig.ChainID),
+ pending: make(map[common.Address]*txList),
+ queue: make(map[common.Address]*txList),
+ beats: make(map[common.Address]time.Time),
+ all: newTxLookup(),
+ chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
+ gasPrice: new(big.Int).SetUint64(config.PriceLimit),
+ }
+ pool.locals = newAccountSet(pool.signer)
+ for _, addr := range config.Locals {
+ log.Info("Setting new local account", "address", addr)
+ pool.locals.add(addr)
+ }
+ pool.priced = newTxPricedList(pool.all)
+ pool.reset(nil, chain.CurrentBlock().Header())
+
+ // If local transactions and journaling is enabled, load from disk
+ if !config.NoLocals && config.Journal != "" {
+ pool.journal = newTxJournal(config.Journal)
+
+ if err := pool.journal.load(pool.AddLocals); err != nil {
+ log.Warn("Failed to load transaction journal", "err", err)
+ }
+ if err := pool.journal.rotate(pool.local()); err != nil {
+ log.Warn("Failed to rotate transaction journal", "err", err)
+ }
+ }
+ // Subscribe events from blockchain
+ pool.chainHeadSub = pool.chain.SubscribeChainHeadEvent(pool.chainHeadCh)
+
+ // Start the event loop and return
+ pool.wg.Add(1)
+ go pool.loop()
+
+ return pool
+}
+
+// loop is the transaction pool's main event loop, waiting for and reacting to
+// outside blockchain events as well as for various reporting and transaction
+// eviction events.
+func (pool *TxPool) loop() {
+ defer pool.wg.Done()
+
+ // Start the stats reporting and transaction eviction tickers
+ var prevPending, prevQueued, prevStales int
+
+ report := time.NewTicker(statsReportInterval)
+ defer report.Stop()
+
+ evict := time.NewTicker(evictionInterval)
+ defer evict.Stop()
+
+ journal := time.NewTicker(pool.config.Rejournal)
+ defer journal.Stop()
+
+ // Track the previous head headers for transaction reorgs
+ //head := pool.chain.CurrentBlock()
+
+ // Keep waiting for and reacting to the various events
+ for {
+ select {
+ // Handle ChainHeadEvent
+ case ev := <-pool.chainHeadCh:
+ if ev.Block != nil {
+ pool.mu.Lock()
+ //if pool.chainconfig.IsHomestead(ev.Block.Number()) {
+ // pool.homestead = true
+ //}
+ //pool.reset(head.Header(), ev.Block.Header())
+ //head = ev.Block
+
+ pool.mu.Unlock()
+ }
+ // Be unsubscribed due to system stopped
+ case <-pool.chainHeadSub.Err():
+ return
+
+ // Handle stats reporting ticks
+ case <-report.C:
+ pool.mu.RLock()
+ pending, queued := pool.stats()
+ stales := pool.priced.stales
+ pool.mu.RUnlock()
+
+ if pending != prevPending || queued != prevQueued || stales != prevStales {
+ log.Debug("Transaction pool status report", "executable", pending, "queued", queued, "stales", stales)
+ prevPending, prevQueued, prevStales = pending, queued, stales
+ }
+
+ // Handle inactive account transaction eviction
+ case <-evict.C:
+ pool.mu.Lock()
+ for addr := range pool.queue {
+ // Skip local transactions from the eviction mechanism
+ if pool.locals.contains(addr) {
+ continue
+ }
+ // Any non-locals old enough should be removed
+ if time.Since(pool.beats[addr]) > pool.config.Lifetime {
+ for _, tx := range pool.queue[addr].Flatten() {
+ pool.removeTx(tx.Hash(), true)
+ }
+ }
+ }
+ pool.mu.Unlock()
+
+ // Handle local transaction journal rotation
+ case <-journal.C:
+ if pool.journal != nil {
+ pool.mu.Lock()
+ if err := pool.journal.rotate(pool.local()); err != nil {
+ log.Warn("Failed to rotate local tx journal", "err", err)
+ }
+ pool.mu.Unlock()
+ }
+ }
+ }
+}
+
+// lockedReset is a wrapper around reset to allow calling it in a thread safe
+// manner. This method is only ever used in the tester!
+func (pool *TxPool) lockedReset(oldHead, newHead *types.Header) {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ pool.reset(oldHead, newHead)
+}
+
+// reset retrieves the current state of the blockchain and ensures the content
+// of the transaction pool is valid with regard to the chain state.
+func (pool *TxPool) reset(oldHead, newHead *types.Header) {
+ // If we're reorging an old state, reinject all dropped transactions
+ var reinject types.Transactions
+
+ if oldHead != nil && oldHead.Hash() != newHead.ParentHash {
+ // If the reorg is too deep, avoid doing it (will happen during fast sync)
+ oldNum := oldHead.Number.Uint64()
+ newNum := newHead.Number.Uint64()
+
+ if depth := uint64(math.Abs(float64(oldNum) - float64(newNum))); depth > 64 {
+ log.Debug("Skipping deep transaction reorg", "depth", depth)
+ } else {
+ // Reorg seems shallow enough to pull in all transactions into memory
+ var discarded, included types.Transactions
+
+ var (
+ rem = pool.chain.GetBlock(oldHead.Hash(), oldHead.Number.Uint64())
+ add = pool.chain.GetBlock(newHead.Hash(), newHead.Number.Uint64())
+ )
+ for rem.NumberU64() > add.NumberU64() {
+ discarded = append(discarded, rem.Transactions()...)
+ if rem = pool.chain.GetBlock(rem.ParentHash(), rem.NumberU64()-1); rem == nil {
+ log.Error("Unrooted old chain seen by tx pool", "block", oldHead.Number, "hash", oldHead.Hash())
+ return
+ }
+ }
+ for add.NumberU64() > rem.NumberU64() {
+ included = append(included, add.Transactions()...)
+ if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil {
+ log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash())
+ return
+ }
+ }
+ for rem.Hash() != add.Hash() {
+ discarded = append(discarded, rem.Transactions()...)
+ if rem = pool.chain.GetBlock(rem.ParentHash(), rem.NumberU64()-1); rem == nil {
+ log.Error("Unrooted old chain seen by tx pool", "block", oldHead.Number, "hash", oldHead.Hash())
+ return
+ }
+ included = append(included, add.Transactions()...)
+ if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil {
+ log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash())
+ return
+ }
+ }
+ reinject = types.TxDifference(discarded, included)
+ }
+ }
+ // Initialize the internal state to the current head
+ if newHead == nil {
+ newHead = pool.chain.CurrentBlock().Header() // Special case during testing
+ }
+ statedb, err := pool.chain.StateAt(newHead.Root)
+ if err != nil {
+ log.Error("Failed to reset txpool state", "err", err)
+ return
+ }
+ pool.currentState = statedb
+ pool.pendingState = state.ManageState(statedb)
+ pool.currentMaxGas = newHead.GasLimit
+
+ // Inject any transactions discarded due to reorgs
+ log.Debug("Reinjecting stale transactions", "count", len(reinject))
+ //senderCacher.recover(pool.signer, reinject)
+ pool.addTxsLocked(reinject, false)
+
+ // validate the pool of pending transactions, this will remove
+ // any transactions that have been included in the block or
+ // have been invalidated because of another transaction (e.g.
+ // higher gas price)
+ pool.demoteUnexecutables()
+
+ // Update all accounts to the latest known pending nonce
+ for addr, list := range pool.pending {
+ txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway
+ pool.pendingState.SetNonce(addr, txs[len(txs)-1].Nonce()+1)
+ }
+ // Check the queue and move transactions over to the pending if possible
+ // or remove those that have become invalid
+ pool.promoteExecutables(nil)
+}
+
+// Stop terminates the transaction pool.
+func (pool *TxPool) Stop() {
+ // Unsubscribe all subscriptions registered from txpool
+ pool.scope.Close()
+
+ // Unsubscribe subscriptions registered from blockchain
+ pool.chainHeadSub.Unsubscribe()
+ pool.wg.Wait()
+
+ if pool.journal != nil {
+ pool.journal.close()
+ }
+ log.Info("Transaction pool stopped")
+}
+
+// SubscribeNewTxsEvent registers a subscription of NewTxsEvent and
+// starts sending event to the given channel.
+func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- NewTxsEvent) event.Subscription {
+ return pool.scope.Track(pool.txFeed.Subscribe(ch))
+}
+
+// GasPrice returns the current gas price enforced by the transaction pool.
+func (pool *TxPool) GasPrice() *big.Int {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ return new(big.Int).Set(pool.gasPrice)
+}
+
+// SetGasPrice updates the minimum price required by the transaction pool for a
+// new transaction, and drops all transactions below this threshold.
+func (pool *TxPool) SetGasPrice(price *big.Int) {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ pool.gasPrice = price
+ for _, tx := range pool.priced.Cap(price, pool.locals) {
+ pool.removeTx(tx.Hash(), false)
+ }
+ log.Info("Transaction pool price threshold updated", "price", price)
+}
+
+// State returns the virtual managed state of the transaction pool.
+func (pool *TxPool) State() *state.ManagedState {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ return pool.pendingState
+}
+
+// Stats retrieves the current pool stats, namely the number of pending and the
+// number of queued (non-executable) transactions.
+func (pool *TxPool) Stats() (int, int) {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ return pool.stats()
+}
+
+// stats retrieves the current pool stats, namely the number of pending and the
+// number of queued (non-executable) transactions.
+func (pool *TxPool) stats() (int, int) {
+ pending := 0
+ for _, list := range pool.pending {
+ pending += list.Len()
+ }
+ queued := 0
+ for _, list := range pool.queue {
+ queued += list.Len()
+ }
+ return pending, queued
+}
+
+// Content retrieves the data content of the transaction pool, returning all the
+// pending as well as queued transactions, grouped by account and sorted by nonce.
+func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ pending := make(map[common.Address]types.Transactions)
+ for addr, list := range pool.pending {
+ pending[addr] = list.Flatten()
+ }
+ queued := make(map[common.Address]types.Transactions)
+ for addr, list := range pool.queue {
+ queued[addr] = list.Flatten()
+ }
+ return pending, queued
+}
+
+// Pending retrieves all currently processable transactions, grouped by origin
+// account and sorted by nonce. The returned transaction set is a copy and can be
+// freely modified by calling code.
+func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ pending := make(map[common.Address]types.Transactions)
+ for addr, list := range pool.pending {
+ pending[addr] = list.Flatten()
+ }
+ return pending, nil
+}
+
+// Locals retrieves the accounts currently considered local by the pool.
+func (pool *TxPool) Locals() []common.Address {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ return pool.locals.flatten()
+}
+
+// local retrieves all currently known local transactions, grouped by origin
+// account and sorted by nonce. The returned transaction set is a copy and can be
+// freely modified by calling code.
+func (pool *TxPool) local() map[common.Address]types.Transactions {
+ txs := make(map[common.Address]types.Transactions)
+ for addr := range pool.locals.accounts {
+ if pending := pool.pending[addr]; pending != nil {
+ txs[addr] = append(txs[addr], pending.Flatten()...)
+ }
+ if queued := pool.queue[addr]; queued != nil {
+ txs[addr] = append(txs[addr], queued.Flatten()...)
+ }
+ }
+ return txs
+}
+
+// validateTx checks whether a transaction is valid according to the consensus
+// rules and adheres to some heuristic limits of the local node (price and size).
+func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
+ // Heuristic limit, reject transactions over 32KB to prevent DOS attacks
+ if tx.Size() > 32*1024 {
+ return ErrOversizedData
+ }
+ // Transactions can't be negative. This may never happen using RLP decoded
+ // transactions but may occur if you create a transaction using the RPC.
+ if tx.Value().Sign() < 0 {
+ return ErrNegativeValue
+ }
+ // Ensure the transaction doesn't exceed the current block limit gas.
+ if pool.currentMaxGas < tx.Gas() {
+ return ErrGasLimit
+ }
+ // Make sure the transaction is signed properly
+ from, err := types.Sender(pool.signer, tx)
+ if err != nil {
+ return ErrInvalidSender
+ }
+ // Drop non-local transactions under our own minimal accepted gas price
+ local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
+ if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
+ return ErrUnderpriced
+ }
+ // Ensure the transaction adheres to nonce ordering
+ if pool.currentState.GetNonce(from) > tx.Nonce() {
+ return ErrNonceTooLow
+ }
+ // Transactor should have enough funds to cover the costs
+ // cost == V + GP * GL
+ if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
+ return ErrInsufficientFunds
+ }
+ intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
+ if err != nil {
+ return err
+ }
+ if tx.Gas() < intrGas {
+ return ErrIntrinsicGas
+ }
+ return nil
+}
+
+// add validates a transaction and inserts it into the non-executable queue for
+// later pending promotion and execution. If the transaction is a replacement for
+// an already pending or queued one, it overwrites the previous and returns this
+// so outer code doesn't uselessly call promote.
+//
+// If a newly added transaction is marked as local, its sending account will be
+// whitelisted, preventing any associated transaction from being dropped out of
+// the pool due to pricing constraints.
+func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
+ // If the transaction is already known, discard it
+ hash := tx.Hash()
+ if pool.all.Get(hash) != nil {
+ log.Trace("Discarding already known transaction", "hash", hash)
+ return false, fmt.Errorf("known transaction: %x", hash)
+ }
+ // If the transaction fails basic validation, discard it
+ if err := pool.validateTx(tx, local); err != nil {
+ log.Trace("Discarding invalid transaction", "hash", hash, "err", err)
+ invalidTxCounter.Inc(1)
+ return false, err
+ }
+ // If the transaction pool is full, discard underpriced transactions
+ if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
+ // If the new transaction is underpriced, don't accept it
+ if !local && pool.priced.Underpriced(tx, pool.locals) {
+ log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
+ underpricedTxCounter.Inc(1)
+ return false, ErrUnderpriced
+ }
+ // New transaction is better than our worse ones, make room for it
+ drop := pool.priced.Discard(pool.all.Count()-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1), pool.locals)
+ for _, tx := range drop {
+ log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
+ underpricedTxCounter.Inc(1)
+ pool.removeTx(tx.Hash(), false)
+ }
+ }
+ // If the transaction is replacing an already pending one, do directly
+ from, _ := types.Sender(pool.signer, tx) // already validated
+ if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
+ // Nonce already pending, check if required price bump is met
+ inserted, old := list.Add(tx, pool.config.PriceBump)
+ if !inserted {
+ pendingDiscardCounter.Inc(1)
+ return false, ErrReplaceUnderpriced
+ }
+ // New transaction is better, replace old one
+ if old != nil {
+ pool.all.Remove(old.Hash())
+ pool.priced.Removed()
+ pendingReplaceCounter.Inc(1)
+ }
+ pool.all.Add(tx)
+ pool.priced.Put(tx)
+ pool.journalTx(from, tx)
+
+ log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
+
+ // We've directly injected a replacement transaction, notify subsystems
+ // go pool.txFeed.Send(NewTxsEvent{types.Transactions{tx}})
+
+ return old != nil, nil
+ }
+ // New transaction isn't replacing a pending one, push into queue
+ replace, err := pool.enqueueTx(hash, tx)
+ if err != nil {
+ return false, err
+ }
+ // Mark local addresses and journal local transactions
+ if local {
+ if !pool.locals.contains(from) {
+ log.Info("Setting new local account", "address", from)
+ pool.locals.add(from)
+ }
+ }
+ pool.journalTx(from, tx)
+
+ log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To())
+ return replace, nil
+}
+
+// enqueueTx inserts a new transaction into the non-executable transaction queue.
+//
+// Note, this method assumes the pool lock is held!
+func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, error) {
+ // Try to insert the transaction into the future queue
+ from, _ := types.Sender(pool.signer, tx) // already validated
+ if pool.queue[from] == nil {
+ pool.queue[from] = newTxList(false)
+ }
+ inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump)
+ if !inserted {
+ // An older transaction was better, discard this
+ queuedDiscardCounter.Inc(1)
+ return false, ErrReplaceUnderpriced
+ }
+ // Discard any previous transaction and mark this
+ if old != nil {
+ pool.all.Remove(old.Hash())
+ pool.priced.Removed()
+ queuedReplaceCounter.Inc(1)
+ }
+ if pool.all.Get(hash) == nil {
+ pool.all.Add(tx)
+ pool.priced.Put(tx)
+ }
+ return old != nil, nil
+}
+
+// journalTx adds the specified transaction to the local disk journal if it is
+// deemed to have been sent from a local account.
+func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) {
+ // Only journal if it's enabled and the transaction is local
+ if pool.journal == nil || !pool.locals.contains(from) {
+ return
+ }
+ if err := pool.journal.insert(tx); err != nil {
+ log.Warn("Failed to journal local transaction", "err", err)
+ }
+}
+
+// promoteTx adds a transaction to the pending (processable) list of transactions
+// and returns whether it was inserted or an older was better.
+//
+// Note, this method assumes the pool lock is held!
+func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool {
+ // Try to insert the transaction into the pending queue
+ if pool.pending[addr] == nil {
+ pool.pending[addr] = newTxList(true)
+ }
+ list := pool.pending[addr]
+
+ inserted, old := list.Add(tx, pool.config.PriceBump)
+ if !inserted {
+ // An older transaction was better, discard this
+ pool.all.Remove(hash)
+ pool.priced.Removed()
+
+ pendingDiscardCounter.Inc(1)
+ return false
+ }
+ // Otherwise discard any previous transaction and mark this
+ if old != nil {
+ pool.all.Remove(old.Hash())
+ pool.priced.Removed()
+
+ pendingReplaceCounter.Inc(1)
+ }
+ // Failsafe to work around direct pending inserts (tests)
+ if pool.all.Get(hash) == nil {
+ pool.all.Add(tx)
+ pool.priced.Put(tx)
+ }
+ // Set the potentially new pending nonce and notify any subsystems of the new tx
+ pool.beats[addr] = time.Now()
+ pool.pendingState.SetNonce(addr, tx.Nonce()+1)
+
+ return true
+}
+
+// AddLocal enqueues a single transaction into the pool if it is valid, marking
+// the sender as a local one in the mean time, ensuring it goes around the local
+// pricing constraints.
+func (pool *TxPool) AddLocal(tx *types.Transaction) error {
+ return pool.addTx(tx, !pool.config.NoLocals)
+}
+
+// AddRemote enqueues a single transaction into the pool if it is valid. If the
+// sender is not among the locally tracked ones, full pricing constraints will
+// apply.
+func (pool *TxPool) AddRemote(tx *types.Transaction) error {
+ return pool.addTx(tx, false)
+}
+
+// AddLocals enqueues a batch of transactions into the pool if they are valid,
+// marking the senders as a local ones in the mean time, ensuring they go around
+// the local pricing constraints.
+func (pool *TxPool) AddLocals(txs []*types.Transaction) []error {
+ return pool.addTxs(txs, !pool.config.NoLocals)
+}
+
+// AddRemotes enqueues a batch of transactions into the pool if they are valid.
+// If the senders are not among the locally tracked ones, full pricing constraints
+// will apply.
+func (pool *TxPool) AddRemotes(txs []*types.Transaction) []error {
+ return pool.addTxs(txs, false)
+}
+
+// addTx enqueues a single transaction into the pool if it is valid.
+func (pool *TxPool) addTx(tx *types.Transaction, local bool) error {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ // Try to inject the transaction and update any state
+ replace, err := pool.add(tx, local)
+ if err != nil {
+ return err
+ }
+ // If we added a new transaction, run promotion checks and return
+ if !replace {
+ from, _ := types.Sender(pool.signer, tx) // already validated
+ pool.promoteExecutables([]common.Address{from})
+ }
+ return nil
+}
+
+// addTxs attempts to queue a batch of transactions if they are valid.
+func (pool *TxPool) addTxs(txs []*types.Transaction, local bool) []error {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ return pool.addTxsLocked(txs, local)
+}
+
+// addTxsLocked attempts to queue a batch of transactions if they are valid,
+// whilst assuming the transaction pool lock is already held.
+func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) []error {
+ // Add the batch of transaction, tracking the accepted ones
+ dirty := make(map[common.Address]struct{})
+ errs := make([]error, len(txs))
+
+ for i, tx := range txs {
+ var replace bool
+ if replace, errs[i] = pool.add(tx, local); errs[i] == nil && !replace {
+ from, _ := types.Sender(pool.signer, tx) // already validated
+ dirty[from] = struct{}{}
+ }
+ }
+ // Only reprocess the internal state if something was actually added
+ if len(dirty) > 0 {
+ addrs := make([]common.Address, 0, len(dirty))
+ for addr := range dirty {
+ addrs = append(addrs, addr)
+ }
+ pool.promoteExecutables(addrs)
+ }
+ return errs
+}
+
+// Status returns the status (unknown/pending/queued) of a batch of transactions
+// identified by their hashes.
+func (pool *TxPool) Status(hashes []common.Hash) []TxStatus {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ status := make([]TxStatus, len(hashes))
+ for i, hash := range hashes {
+ if tx := pool.all.Get(hash); tx != nil {
+ from, _ := types.Sender(pool.signer, tx) // already validated
+ if pool.pending[from] != nil && pool.pending[from].txs.items[tx.Nonce()] != nil {
+ status[i] = TxStatusPending
+ } else {
+ status[i] = TxStatusQueued
+ }
+ }
+ }
+ return status
+}
+
+// Get returns a transaction if it is contained in the pool
+// and nil otherwise.
+func (pool *TxPool) Get(hash common.Hash) *types.Transaction {
+ return pool.all.Get(hash)
+}
+
+// removeTx removes a single transaction from the queue, moving all subsequent
+// transactions back to the future queue.
+func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
+ // Fetch the transaction we wish to delete
+ tx := pool.all.Get(hash)
+ if tx == nil {
+ return
+ }
+ addr, _ := types.Sender(pool.signer, tx) // already validated during insertion
+
+ // Remove it from the list of known transactions
+ pool.all.Remove(hash)
+ if outofbound {
+ pool.priced.Removed()
+ }
+ // Remove the transaction from the pending lists and reset the account nonce
+ if pending := pool.pending[addr]; pending != nil {
+ if removed, invalids := pending.Remove(tx); removed {
+ // If no more pending transactions are left, remove the list
+ if pending.Empty() {
+ delete(pool.pending, addr)
+ delete(pool.beats, addr)
+ }
+ // Postpone any invalidated transactions
+ for _, tx := range invalids {
+ pool.enqueueTx(tx.Hash(), tx)
+ }
+ // Update the account nonce if needed
+ if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
+ pool.pendingState.SetNonce(addr, nonce)
+ }
+ return
+ }
+ }
+ // Transaction is in the future queue
+ if future := pool.queue[addr]; future != nil {
+ future.Remove(tx)
+ if future.Empty() {
+ delete(pool.queue, addr)
+ }
+ }
+}
+
+// promoteExecutables moves transactions that have become processable from the
+// future queue to the set of pending transactions. During this process, all
+// invalidated transactions (low nonce, low balance) are deleted.
+func (pool *TxPool) promoteExecutables(accounts []common.Address) {
+ // Track the promoted transactions to broadcast them at once
+ var promoted []*types.Transaction
+
+ // Gather all the accounts potentially needing updates
+ if accounts == nil {
+ accounts = make([]common.Address, 0, len(pool.queue))
+ for addr := range pool.queue {
+ accounts = append(accounts, addr)
+ }
+ }
+ // Iterate over all accounts and promote any executable transactions
+ for _, addr := range accounts {
+ list := pool.queue[addr]
+ if list == nil {
+ continue // Just in case someone calls with a non existing account
+ }
+ // Drop all transactions that are deemed too old (low nonce)
+ for _, tx := range list.Forward(pool.currentState.GetNonce(addr)) {
+ hash := tx.Hash()
+ log.Trace("Removed old queued transaction", "hash", hash)
+ pool.all.Remove(hash)
+ pool.priced.Removed()
+ }
+ // Drop all transactions that are too costly (low balance or out of gas)
+ drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)
+ for _, tx := range drops {
+ hash := tx.Hash()
+ log.Trace("Removed unpayable queued transaction", "hash", hash)
+ pool.all.Remove(hash)
+ pool.priced.Removed()
+ queuedNofundsCounter.Inc(1)
+ }
+ // Gather all executable transactions and promote them
+ for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {
+ hash := tx.Hash()
+ if pool.promoteTx(addr, hash, tx) {
+ log.Trace("Promoting queued transaction", "hash", hash)
+ promoted = append(promoted, tx)
+ }
+ }
+ // Drop all transactions over the allowed limit
+ if !pool.locals.contains(addr) {
+ for _, tx := range list.Cap(int(pool.config.AccountQueue)) {
+ hash := tx.Hash()
+ pool.all.Remove(hash)
+ pool.priced.Removed()
+ queuedRateLimitCounter.Inc(1)
+ log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
+ }
+ }
+ // Delete the entire queue entry if it became empty.
+ if list.Empty() {
+ delete(pool.queue, addr)
+ }
+ }
+ // Notify subsystem for new promoted transactions.
+ //if len(promoted) > 0 {
+ // go pool.txFeed.Send(NewTxsEvent{promoted})
+ //}
+ // If the pending limit is overflown, start equalizing allowances
+ pending := uint64(0)
+ for _, list := range pool.pending {
+ pending += uint64(list.Len())
+ }
+ if pending > pool.config.GlobalSlots {
+ pendingBeforeCap := pending
+ // Assemble a spam order to penalize large transactors first
+ spammers := prque.New(nil)
+ for addr, list := range pool.pending {
+ // Only evict transactions from high rollers
+ if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
+ spammers.Push(addr, int64(list.Len()))
+ }
+ }
+ // Gradually drop transactions from offenders
+ offenders := []common.Address{}
+ for pending > pool.config.GlobalSlots && !spammers.Empty() {
+ // Retrieve the next offender if not local address
+ offender, _ := spammers.Pop()
+ offenders = append(offenders, offender.(common.Address))
+
+ // Equalize balances until all the same or below threshold
+ if len(offenders) > 1 {
+ // Calculate the equalization threshold for all current offenders
+ threshold := pool.pending[offender.(common.Address)].Len()
+
+ // Iteratively reduce all offenders until below limit or threshold reached
+ for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
+ for i := 0; i < len(offenders)-1; i++ {
+ list := pool.pending[offenders[i]]
+ for _, tx := range list.Cap(list.Len() - 1) {
+ // Drop the transaction from the global pools too
+ hash := tx.Hash()
+ pool.all.Remove(hash)
+ pool.priced.Removed()
+
+ // Update the account nonce to the dropped transaction
+ if nonce := tx.Nonce(); pool.pendingState.GetNonce(offenders[i]) > nonce {
+ pool.pendingState.SetNonce(offenders[i], nonce)
+ }
+ log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
+ }
+ pending--
+ }
+ }
+ }
+ }
+ // If still above threshold, reduce to limit or min allowance
+ if pending > pool.config.GlobalSlots && len(offenders) > 0 {
+ for pending > pool.config.GlobalSlots && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > pool.config.AccountSlots {
+ for _, addr := range offenders {
+ list := pool.pending[addr]
+ for _, tx := range list.Cap(list.Len() - 1) {
+ // Drop the transaction from the global pools too
+ hash := tx.Hash()
+ pool.all.Remove(hash)
+ pool.priced.Removed()
+
+ // Update the account nonce to the dropped transaction
+ if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
+ pool.pendingState.SetNonce(addr, nonce)
+ }
+ log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
+ }
+ pending--
+ }
+ }
+ }
+ pendingRateLimitCounter.Inc(int64(pendingBeforeCap - pending))
+ }
+ // If we've queued more transactions than the hard limit, drop oldest ones
+ queued := uint64(0)
+ for _, list := range pool.queue {
+ queued += uint64(list.Len())
+ }
+ if queued > pool.config.GlobalQueue {
+ // Sort all accounts with queued transactions by heartbeat
+ addresses := make(addressesByHeartbeat, 0, len(pool.queue))
+ for addr := range pool.queue {
+ if !pool.locals.contains(addr) { // don't drop locals
+ addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
+ }
+ }
+ sort.Sort(addresses)
+
+ // Drop transactions until the total is below the limit or only locals remain
+ for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0; {
+ addr := addresses[len(addresses)-1]
+ list := pool.queue[addr.address]
+
+ addresses = addresses[:len(addresses)-1]
+
+ // Drop all transactions if they are less than the overflow
+ if size := uint64(list.Len()); size <= drop {
+ for _, tx := range list.Flatten() {
+ pool.removeTx(tx.Hash(), true)
+ }
+ drop -= size
+ queuedRateLimitCounter.Inc(int64(size))
+ continue
+ }
+ // Otherwise drop only last few transactions
+ txs := list.Flatten()
+ for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
+ pool.removeTx(txs[i].Hash(), true)
+ drop--
+ queuedRateLimitCounter.Inc(1)
+ }
+ }
+ }
+}
+
+// demoteUnexecutables removes invalid and processed transactions from the pools
+// executable/pending queue and any subsequent transactions that become unexecutable
+// are moved back into the future queue.
+func (pool *TxPool) demoteUnexecutables() {
+ // Iterate over all accounts and demote any non-executable transactions
+ for addr, list := range pool.pending {
+ nonce := pool.currentState.GetNonce(addr)
+
+ // Drop all transactions that are deemed too old (low nonce)
+ for _, tx := range list.Forward(nonce) {
+ hash := tx.Hash()
+ log.Trace("Removed old pending transaction", "hash", hash)
+ pool.all.Remove(hash)
+ pool.priced.Removed()
+ }
+ // Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
+ drops, invalids := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)
+ for _, tx := range drops {
+ hash := tx.Hash()
+ log.Trace("Removed unpayable pending transaction", "hash", hash)
+ pool.all.Remove(hash)
+ pool.priced.Removed()
+ pendingNofundsCounter.Inc(1)
+ }
+ for _, tx := range invalids {
+ hash := tx.Hash()
+ log.Trace("Demoting pending transaction", "hash", hash)
+ pool.enqueueTx(hash, tx)
+ }
+ // If there's a gap in front, alert (should never happen) and postpone all transactions
+ if list.Len() > 0 && list.txs.Get(nonce) == nil {
+ for _, tx := range list.Cap(0) {
+ hash := tx.Hash()
+ log.Error("Demoting invalidated transaction", "hash", hash)
+ pool.enqueueTx(hash, tx)
+ }
+ }
+ // Delete the entire queue entry if it became empty.
+ if list.Empty() {
+ delete(pool.pending, addr)
+ delete(pool.beats, addr)
+ }
+ }
+}
+
+// addressByHeartbeat is an account address tagged with its last activity timestamp.
+type addressByHeartbeat struct {
+ address common.Address
+ heartbeat time.Time
+}
+
+type addressesByHeartbeat []addressByHeartbeat
+
+func (a addressesByHeartbeat) Len() int { return len(a) }
+func (a addressesByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) }
+func (a addressesByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// accountSet is simply a set of addresses to check for existence, and a signer
+// capable of deriving addresses from transactions.
+type accountSet struct {
+ accounts map[common.Address]struct{}
+ signer types.Signer
+ cache *[]common.Address
+}
+
+// newAccountSet creates a new address set with an associated signer for sender
+// derivations.
+func newAccountSet(signer types.Signer) *accountSet {
+ return &accountSet{
+ accounts: make(map[common.Address]struct{}),
+ signer: signer,
+ }
+}
+
+// contains checks if a given address is contained within the set.
+func (as *accountSet) contains(addr common.Address) bool {
+ _, exist := as.accounts[addr]
+ return exist
+}
+
+// containsTx checks if the sender of a given tx is within the set. If the sender
+// cannot be derived, this method returns false.
+func (as *accountSet) containsTx(tx *types.Transaction) bool {
+ if addr, err := types.Sender(as.signer, tx); err == nil {
+ return as.contains(addr)
+ }
+ return false
+}
+
+// add inserts a new address into the set to track.
+func (as *accountSet) add(addr common.Address) {
+ as.accounts[addr] = struct{}{}
+ as.cache = nil
+}
+
+// flatten returns the list of addresses within this set, also caching it for later
+// reuse. The returned slice should not be changed!
+func (as *accountSet) flatten() []common.Address {
+ if as.cache == nil {
+ accounts := make([]common.Address, 0, len(as.accounts))
+ for account := range as.accounts {
+ accounts = append(accounts, account)
+ }
+ as.cache = &accounts
+ }
+ return *as.cache
+}
+
+// txLookup is used internally by TxPool to track transactions while allowing lookup without
+// mutex contention.
+//
+// Note, although this type is properly protected against concurrent access, it
+// is **not** a type that should ever be mutated or even exposed outside of the
+// transaction pool, since its internal state is tightly coupled with the pools
+// internal mechanisms. The sole purpose of the type is to permit out-of-bound
+// peeking into the pool in TxPool.Get without having to acquire the widely scoped
+// TxPool.mu mutex.
+type txLookup struct {
+ all map[common.Hash]*types.Transaction
+ lock sync.RWMutex
+}
+
+// newTxLookup returns a new txLookup structure.
+func newTxLookup() *txLookup {
+ return &txLookup{
+ all: make(map[common.Hash]*types.Transaction),
+ }
+}
+
+// Range calls f on each key and value present in the map.
+func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction) bool) {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+
+ for key, value := range t.all {
+ if !f(key, value) {
+ break
+ }
+ }
+}
+
+// Get returns a transaction if it exists in the lookup, or nil if not found.
+func (t *txLookup) Get(hash common.Hash) *types.Transaction {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+
+ return t.all[hash]
+}
+
+// Count returns the current number of items in the lookup.
+func (t *txLookup) Count() int {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+
+ return len(t.all)
+}
+
+// Add adds a transaction to the lookup.
+func (t *txLookup) Add(tx *types.Transaction) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ t.all[tx.Hash()] = tx
+}
+
+// Remove removes a transaction from the lookup.
+func (t *txLookup) Remove(hash common.Hash) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ delete(t.all, hash)
+}
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
new file mode 100644
index 000000000..c74d4c91a
--- /dev/null
+++ b/core/tx_pool_test.go
@@ -0,0 +1,1358 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "crypto/ecdsa"
+ "fmt"
+ "io/ioutil"
+ "math/big"
+ "math/rand"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// testTxPoolConfig is a transaction pool configuration without stateful disk
+// sideeffects used during testing.
+var testTxPoolConfig TxPoolConfig
+
+func init() {
+ testTxPoolConfig = DefaultTxPoolConfig
+ testTxPoolConfig.Journal = ""
+}
+
+type testBlockChain struct {
+ statedb *state.StateDB
+ gasLimit uint64
+ chainHeadFeed *event.Feed
+}
+
+func (bc *testBlockChain) CurrentBlock() *types.Block {
+ return types.NewBlock(&types.Header{
+ GasLimit: bc.gasLimit,
+ }, nil, nil, nil)
+}
+
+func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
+ return bc.CurrentBlock()
+}
+
+func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) {
+ return bc.statedb, nil
+}
+
+func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription {
+ return bc.chainHeadFeed.Subscribe(ch)
+}
+
+func transaction(nonce uint64, gaslimit uint64, key *ecdsa.PrivateKey) *types.Transaction {
+ return pricedTransaction(nonce, gaslimit, big.NewInt(1), key)
+}
+
+func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
+ tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
+ return tx
+}
+
+func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ key, _ := crypto.GenerateKey()
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
+
+ return pool, key
+}
+
+// validateTxPoolInternals checks various consistency invariants within the pool.
+func validateTxPoolInternals(pool *TxPool) error {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ // Ensure the total transaction set is consistent with pending + queued
+ pending, queued := pool.stats()
+ if total := pool.all.Count(); total != pending+queued {
+ return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued)
+ }
+ if priced := pool.priced.items.Len() - pool.priced.stales; priced != pending+queued {
+ return fmt.Errorf("total priced transaction count %d != %d pending + %d queued", priced, pending, queued)
+ }
+ // Ensure the next nonce to assign is the correct one
+ for addr, txs := range pool.pending {
+ // Find the last transaction
+ var last uint64
+ for nonce := range txs.txs.items {
+ if last < nonce {
+ last = nonce
+ }
+ }
+ if nonce := pool.pendingState.GetNonce(addr); nonce != last+1 {
+ return fmt.Errorf("pending nonce mismatch: have %v, want %v", nonce, last+1)
+ }
+ }
+ return nil
+}
+
+// validateEvents checks that the correct number of transaction addition events
+// were fired on the pool's event feed.
+func validateEvents(events chan NewTxsEvent, count int) error {
+ var received []*types.Transaction
+
+ for len(received) < count {
+ select {
+ case ev := <-events:
+ received = append(received, ev.Txs...)
+ case <-time.After(time.Second):
+ return fmt.Errorf("event #%d not fired", received)
+ }
+ }
+ if len(received) > count {
+ return fmt.Errorf("more than %d events fired: %v", count, received[count:])
+ }
+ select {
+ case ev := <-events:
+ return fmt.Errorf("more than %d events fired: %v", count, ev.Txs)
+
+ case <-time.After(50 * time.Millisecond):
+ // This branch should be "default", but it's a data race between goroutines,
+ // reading the event channel and pushing into it, so better wait a bit ensuring
+ // really nothing gets injected.
+ }
+ return nil
+}
+
+func deriveSender(tx *types.Transaction) (common.Address, error) {
+ return types.Sender(types.HomesteadSigner{}, tx)
+}
+
+type testChain struct {
+ *testBlockChain
+ address common.Address
+ trigger *bool
+}
+
+// testChain.State() is used multiple times to reset the pending state.
+// when simulate is true it will create a state that indicates
+// that tx0 and tx1 are included in the chain.
+func (c *testChain) State() (*state.StateDB, error) {
+ // delay "state change" by one. The tx pool fetches the
+ // state multiple times and by delaying it a bit we simulate
+ // a state change between those fetches.
+ stdb := c.statedb
+ if *c.trigger {
+ c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ // simulate that the new head block included tx0 and tx1
+ c.statedb.SetNonce(c.address, 2)
+ c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether))
+ *c.trigger = false
+ }
+ return stdb, nil
+}
+
+// This test simulates a scenario where a new block is imported during a
+// state reset and tests whether the pending state is in sync with the
+// block head event that initiated the resetState().
+func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
+ t.Parallel()
+
+ var (
+ key, _ = crypto.GenerateKey()
+ address = crypto.PubkeyToAddress(key.PublicKey)
+ statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ trigger = false
+ )
+
+ // setup pool with 2 transaction in it
+ statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether))
+ blockchain := &testChain{&testBlockChain{statedb, 1000000000, new(event.Feed)}, address, &trigger}
+
+ tx0 := transaction(0, 100000, key)
+ tx1 := transaction(1, 100000, key)
+
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ nonce := pool.State().GetNonce(address)
+ if nonce != 0 {
+ t.Fatalf("Invalid nonce, want 0, got %d", nonce)
+ }
+
+ pool.AddRemotes(types.Transactions{tx0, tx1})
+
+ nonce = pool.State().GetNonce(address)
+ if nonce != 2 {
+ t.Fatalf("Invalid nonce, want 2, got %d", nonce)
+ }
+
+ // trigger state change in the background
+ trigger = true
+
+ pool.lockedReset(nil, nil)
+
+ _, err := pool.Pending()
+ if err != nil {
+ t.Fatalf("Could not fetch pending transactions: %v", err)
+ }
+ nonce = pool.State().GetNonce(address)
+ if nonce != 2 {
+ t.Fatalf("Invalid nonce, want 2, got %d", nonce)
+ }
+}
+
+func TestInvalidTransactions(t *testing.T) {
+ t.Parallel()
+
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ tx := transaction(0, 100, key)
+ from, _ := deriveSender(tx)
+
+ pool.currentState.AddBalance(from, big.NewInt(1))
+ if err := pool.AddRemote(tx); err != ErrInsufficientFunds {
+ t.Error("expected", ErrInsufficientFunds)
+ }
+
+ balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice()))
+ pool.currentState.AddBalance(from, balance)
+ if err := pool.AddRemote(tx); err != ErrIntrinsicGas {
+ t.Error("expected", ErrIntrinsicGas, "got", err)
+ }
+
+ pool.currentState.SetNonce(from, 1)
+ pool.currentState.AddBalance(from, big.NewInt(0xffffffffffffff))
+ tx = transaction(0, 100000, key)
+ if err := pool.AddRemote(tx); err != ErrNonceTooLow {
+ t.Error("expected", ErrNonceTooLow)
+ }
+
+ tx = transaction(1, 100000, key)
+ pool.gasPrice = big.NewInt(1000)
+ if err := pool.AddRemote(tx); err != ErrUnderpriced {
+ t.Error("expected", ErrUnderpriced, "got", err)
+ }
+ if err := pool.AddLocal(tx); err != nil {
+ t.Error("expected", nil, "got", err)
+ }
+}
+
+func TestTransactionQueue(t *testing.T) {
+ t.Parallel()
+
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ tx := transaction(0, 100, key)
+ from, _ := deriveSender(tx)
+ pool.currentState.AddBalance(from, big.NewInt(1000))
+ pool.lockedReset(nil, nil)
+ pool.enqueueTx(tx.Hash(), tx)
+
+ pool.promoteExecutables([]common.Address{from})
+ if len(pool.pending) != 1 {
+ t.Error("expected valid txs to be 1 is", len(pool.pending))
+ }
+
+ tx = transaction(1, 100, key)
+ from, _ = deriveSender(tx)
+ pool.currentState.SetNonce(from, 2)
+ pool.enqueueTx(tx.Hash(), tx)
+ pool.promoteExecutables([]common.Address{from})
+ if _, ok := pool.pending[from].txs.items[tx.Nonce()]; ok {
+ t.Error("expected transaction to be in tx pool")
+ }
+
+ if len(pool.queue) > 0 {
+ t.Error("expected transaction queue to be empty. is", len(pool.queue))
+ }
+
+ pool, key = setupTxPool()
+ defer pool.Stop()
+
+ tx1 := transaction(0, 100, key)
+ tx2 := transaction(10, 100, key)
+ tx3 := transaction(11, 100, key)
+ from, _ = deriveSender(tx1)
+ pool.currentState.AddBalance(from, big.NewInt(1000))
+ pool.lockedReset(nil, nil)
+
+ pool.enqueueTx(tx1.Hash(), tx1)
+ pool.enqueueTx(tx2.Hash(), tx2)
+ pool.enqueueTx(tx3.Hash(), tx3)
+
+ pool.promoteExecutables([]common.Address{from})
+
+ if len(pool.pending) != 1 {
+ t.Error("expected tx pool to be 1, got", len(pool.pending))
+ }
+ if pool.queue[from].Len() != 2 {
+ t.Error("expected len(queue) == 2, got", pool.queue[from].Len())
+ }
+}
+
+func TestTransactionNegativeValue(t *testing.T) {
+ t.Parallel()
+
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key)
+ from, _ := deriveSender(tx)
+ pool.currentState.AddBalance(from, big.NewInt(1))
+ if err := pool.AddRemote(tx); err != ErrNegativeValue {
+ t.Error("expected", ErrNegativeValue, "got", err)
+ }
+}
+
+func TestTransactionChainFork(t *testing.T) {
+ t.Parallel()
+
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ resetState := func() {
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ statedb.AddBalance(addr, big.NewInt(100000000000000))
+
+ pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
+ pool.lockedReset(nil, nil)
+ }
+ resetState()
+
+ tx := transaction(0, 100000, key)
+ if _, err := pool.add(tx, false); err != nil {
+ t.Error("didn't expect error", err)
+ }
+ pool.removeTx(tx.Hash(), true)
+
+ // reset the pool's internal state
+ resetState()
+ if _, err := pool.add(tx, false); err != nil {
+ t.Error("didn't expect error", err)
+ }
+}
+
+func TestTransactionDoubleNonce(t *testing.T) {
+ t.Parallel()
+
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ resetState := func() {
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ statedb.AddBalance(addr, big.NewInt(100000000000000))
+
+ pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
+ pool.lockedReset(nil, nil)
+ }
+ resetState()
+
+ signer := types.HomesteadSigner{}
+ tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), nil), signer, key)
+ tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(2), nil), signer, key)
+ tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(1), nil), signer, key)
+
+ // Add the first two transaction, ensure higher priced stays only
+ if replace, err := pool.add(tx1, false); err != nil || replace {
+ t.Errorf("first transaction insert failed (%v) or reported replacement (%v)", err, replace)
+ }
+ if replace, err := pool.add(tx2, false); err != nil || !replace {
+ t.Errorf("second transaction insert failed (%v) or not reported replacement (%v)", err, replace)
+ }
+ pool.promoteExecutables([]common.Address{addr})
+ if pool.pending[addr].Len() != 1 {
+ t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
+ }
+ if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() {
+ t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash())
+ }
+ // Add the third transaction and ensure it's not saved (smaller price)
+ pool.add(tx3, false)
+ pool.promoteExecutables([]common.Address{addr})
+ if pool.pending[addr].Len() != 1 {
+ t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
+ }
+ if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() {
+ t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash())
+ }
+ // Ensure the total transaction count is correct
+ if pool.all.Count() != 1 {
+ t.Error("expected 1 total transactions, got", pool.all.Count())
+ }
+}
+
+func TestTransactionMissingNonce(t *testing.T) {
+ t.Parallel()
+
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
+ tx := transaction(1, 100000, key)
+ if _, err := pool.add(tx, false); err != nil {
+ t.Error("didn't expect error", err)
+ }
+ if len(pool.pending) != 0 {
+ t.Error("expected 0 pending transactions, got", len(pool.pending))
+ }
+ if pool.queue[addr].Len() != 1 {
+ t.Error("expected 1 queued transaction, got", pool.queue[addr].Len())
+ }
+ if pool.all.Count() != 1 {
+ t.Error("expected 1 total transactions, got", pool.all.Count())
+ }
+}
+
+func TestTransactionNonceRecovery(t *testing.T) {
+ t.Parallel()
+
+ const n = 10
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ pool.currentState.SetNonce(addr, n)
+ pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
+ pool.lockedReset(nil, nil)
+
+ tx := transaction(n, 100000, key)
+ if err := pool.AddRemote(tx); err != nil {
+ t.Error(err)
+ }
+ // simulate some weird re-order of transactions and missing nonce(s)
+ pool.currentState.SetNonce(addr, n-1)
+ pool.lockedReset(nil, nil)
+ if fn := pool.pendingState.GetNonce(addr); fn != n-1 {
+ t.Errorf("expected nonce to be %d, got %d", n-1, fn)
+ }
+}
+
+// Tests that if an account runs out of funds, any pending and queued transactions
+// are dropped.
+func TestTransactionDropping(t *testing.T) {
+ t.Parallel()
+
+ // Create a test account and fund it
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ account, _ := deriveSender(transaction(0, 0, key))
+ pool.currentState.AddBalance(account, big.NewInt(1000))
+
+ // Add some pending and some queued transactions
+ var (
+ tx0 = transaction(0, 100, key)
+ tx1 = transaction(1, 200, key)
+ tx2 = transaction(2, 300, key)
+ tx10 = transaction(10, 100, key)
+ tx11 = transaction(11, 200, key)
+ tx12 = transaction(12, 300, key)
+ )
+ pool.promoteTx(account, tx0.Hash(), tx0)
+ pool.promoteTx(account, tx1.Hash(), tx1)
+ pool.promoteTx(account, tx2.Hash(), tx2)
+ pool.enqueueTx(tx10.Hash(), tx10)
+ pool.enqueueTx(tx11.Hash(), tx11)
+ pool.enqueueTx(tx12.Hash(), tx12)
+
+ // Check that pre and post validations leave the pool as is
+ if pool.pending[account].Len() != 3 {
+ t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 3)
+ }
+ if pool.queue[account].Len() != 3 {
+ t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
+ }
+ if pool.all.Count() != 6 {
+ t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6)
+ }
+ pool.lockedReset(nil, nil)
+ if pool.pending[account].Len() != 3 {
+ t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 3)
+ }
+ if pool.queue[account].Len() != 3 {
+ t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
+ }
+ if pool.all.Count() != 6 {
+ t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6)
+ }
+ // Reduce the balance of the account, and check that invalidated transactions are dropped
+ pool.currentState.AddBalance(account, big.NewInt(-650))
+ pool.lockedReset(nil, nil)
+
+ if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
+ t.Errorf("funded pending transaction missing: %v", tx0)
+ }
+ if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; !ok {
+ t.Errorf("funded pending transaction missing: %v", tx0)
+ }
+ if _, ok := pool.pending[account].txs.items[tx2.Nonce()]; ok {
+ t.Errorf("out-of-fund pending transaction present: %v", tx1)
+ }
+ if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok {
+ t.Errorf("funded queued transaction missing: %v", tx10)
+ }
+ if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; !ok {
+ t.Errorf("funded queued transaction missing: %v", tx10)
+ }
+ if _, ok := pool.queue[account].txs.items[tx12.Nonce()]; ok {
+ t.Errorf("out-of-fund queued transaction present: %v", tx11)
+ }
+ if pool.all.Count() != 4 {
+ t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 4)
+ }
+ // Reduce the block gas limit, check that invalidated transactions are dropped
+ pool.chain.(*testBlockChain).gasLimit = 100
+ pool.lockedReset(nil, nil)
+
+ if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
+ t.Errorf("funded pending transaction missing: %v", tx0)
+ }
+ if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; ok {
+ t.Errorf("over-gased pending transaction present: %v", tx1)
+ }
+ if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok {
+ t.Errorf("funded queued transaction missing: %v", tx10)
+ }
+ if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
+ t.Errorf("over-gased queued transaction present: %v", tx11)
+ }
+ if pool.all.Count() != 2 {
+ t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 2)
+ }
+}
+
+// Tests that if a transaction is dropped from the current pending pool (e.g. out
+// of fund), all consecutive (still valid, but not executable) transactions are
+// postponed back into the future queue to prevent broadcasting them.
+func TestTransactionPostponing(t *testing.T) {
+ t.Parallel()
+
+ // Create the pool to test the postponing with
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ // Create two test accounts to produce different gap profiles with
+ keys := make([]*ecdsa.PrivateKey, 2)
+ accs := make([]common.Address, len(keys))
+
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ accs[i] = crypto.PubkeyToAddress(keys[i].PublicKey)
+
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100))
+ }
+ // Add a batch consecutive pending transactions for validation
+ txs := []*types.Transaction{}
+ for i, key := range keys {
+
+ for j := 0; j < 100; j++ {
+ var tx *types.Transaction
+ if (i+j)%2 == 0 {
+ tx = transaction(uint64(j), 25000, key)
+ } else {
+ tx = transaction(uint64(j), 50000, key)
+ }
+ txs = append(txs, tx)
+ }
+ }
+ for i, err := range pool.AddRemotes(txs) {
+ if err != nil {
+ t.Fatalf("tx %d: failed to add transactions: %v", i, err)
+ }
+ }
+ // Check that pre and post validations leave the pool as is
+ if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) {
+ t.Errorf("pending transaction mismatch: have %d, want %d", pending, len(txs))
+ }
+ if len(pool.queue) != 0 {
+ t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
+ }
+ if pool.all.Count() != len(txs) {
+ t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs))
+ }
+ pool.lockedReset(nil, nil)
+ if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) {
+ t.Errorf("pending transaction mismatch: have %d, want %d", pending, len(txs))
+ }
+ if len(pool.queue) != 0 {
+ t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
+ }
+ if pool.all.Count() != len(txs) {
+ t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs))
+ }
+ // Reduce the balance of the account, and check that transactions are reorganised
+ for _, addr := range accs {
+ pool.currentState.AddBalance(addr, big.NewInt(-1))
+ }
+ pool.lockedReset(nil, nil)
+
+ // The first account's first transaction remains valid, check that subsequent
+ // ones are either filtered out, or queued up for later.
+ if _, ok := pool.pending[accs[0]].txs.items[txs[0].Nonce()]; !ok {
+ t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txs[0])
+ }
+ if _, ok := pool.queue[accs[0]].txs.items[txs[0].Nonce()]; ok {
+ t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txs[0])
+ }
+ for i, tx := range txs[1:100] {
+ if i%2 == 1 {
+ if _, ok := pool.pending[accs[0]].txs.items[tx.Nonce()]; ok {
+ t.Errorf("tx %d: valid but future transaction present in pending pool: %v", i+1, tx)
+ }
+ if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; !ok {
+ t.Errorf("tx %d: valid but future transaction missing from future queue: %v", i+1, tx)
+ }
+ } else {
+ if _, ok := pool.pending[accs[0]].txs.items[tx.Nonce()]; ok {
+ t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx)
+ }
+ if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; ok {
+ t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx)
+ }
+ }
+ }
+ // The second account's first transaction got invalid, check that all transactions
+ // are either filtered out, or queued up for later.
+ if pool.pending[accs[1]] != nil {
+ t.Errorf("invalidated account still has pending transactions")
+ }
+ for i, tx := range txs[100:] {
+ if i%2 == 1 {
+ if _, ok := pool.queue[accs[1]].txs.items[tx.Nonce()]; !ok {
+ t.Errorf("tx %d: valid but future transaction missing from future queue: %v", 100+i, tx)
+ }
+ } else {
+ if _, ok := pool.queue[accs[1]].txs.items[tx.Nonce()]; ok {
+ t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", 100+i, tx)
+ }
+ }
+ }
+ if pool.all.Count() != len(txs)/2 {
+ t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), len(txs)/2)
+ }
+}
+
+// Tests that if the transaction count belonging to a single account goes above
+// some threshold, the higher transactions are dropped to prevent DOS attacks.
+func TestTransactionQueueAccountLimiting(t *testing.T) {
+ t.Parallel()
+
+ // Create a test account and fund it
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ account, _ := deriveSender(transaction(0, 0, key))
+ pool.currentState.AddBalance(account, big.NewInt(1000000))
+
+ // Keep queuing up transactions and make sure all above a limit are dropped
+ for i := uint64(1); i <= testTxPoolConfig.AccountQueue+5; i++ {
+ if err := pool.AddRemote(transaction(i, 100000, key)); err != nil {
+ t.Fatalf("tx %d: failed to add transaction: %v", i, err)
+ }
+ if len(pool.pending) != 0 {
+ t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), 0)
+ }
+ if i <= testTxPoolConfig.AccountQueue {
+ if pool.queue[account].Len() != int(i) {
+ t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), i)
+ }
+ } else {
+ if pool.queue[account].Len() != int(testTxPoolConfig.AccountQueue) {
+ t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), testTxPoolConfig.AccountQueue)
+ }
+ }
+ }
+ if pool.all.Count() != int(testTxPoolConfig.AccountQueue) {
+ t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), testTxPoolConfig.AccountQueue)
+ }
+}
+
+// Tests that if the transaction count belonging to multiple accounts go above
+// some threshold, the higher transactions are dropped to prevent DOS attacks.
+//
+// This logic should not hold for local transactions, unless the local tracking
+// mechanism is disabled.
+func TestTransactionQueueGlobalLimiting(t *testing.T) {
+ testTransactionQueueGlobalLimiting(t, false)
+}
+func TestTransactionQueueGlobalLimitingNoLocals(t *testing.T) {
+ testTransactionQueueGlobalLimiting(t, true)
+}
+
+func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
+ t.Parallel()
+
+ // Create the pool to test the limit enforcement with
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ config := testTxPoolConfig
+ config.NoLocals = nolocals
+ config.GlobalQueue = config.AccountQueue*3 - 1 // reduce the queue limits to shorten test time (-1 to make it non divisible)
+
+ pool := NewTxPool(config, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ // Create a number of test accounts and fund them (last one will be the local)
+ keys := make([]*ecdsa.PrivateKey, 5)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ }
+ local := keys[len(keys)-1]
+
+ // Generate and queue a batch of transactions
+ nonces := make(map[common.Address]uint64)
+
+ txs := make(types.Transactions, 0, 3*config.GlobalQueue)
+ for len(txs) < cap(txs) {
+ key := keys[rand.Intn(len(keys)-1)] // skip adding transactions with the local account
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+
+ txs = append(txs, transaction(nonces[addr]+1, 100000, key))
+ nonces[addr]++
+ }
+ // Import the batch and verify that limits have been enforced
+ pool.AddRemotes(txs)
+
+ queued := 0
+ for addr, list := range pool.queue {
+ if list.Len() > int(config.AccountQueue) {
+ t.Errorf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), config.AccountQueue)
+ }
+ queued += list.Len()
+ }
+ if queued > int(config.GlobalQueue) {
+ t.Fatalf("total transactions overflow allowance: %d > %d", queued, config.GlobalQueue)
+ }
+ // Generate a batch of transactions from the local account and import them
+ txs = txs[:0]
+ for i := uint64(0); i < 3*config.GlobalQueue; i++ {
+ txs = append(txs, transaction(i+1, 100000, local))
+ }
+ pool.AddLocals(txs)
+
+ // If locals are disabled, the previous eviction algorithm should apply here too
+ if nolocals {
+ queued := 0
+ for addr, list := range pool.queue {
+ if list.Len() > int(config.AccountQueue) {
+ t.Errorf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), config.AccountQueue)
+ }
+ queued += list.Len()
+ }
+ if queued > int(config.GlobalQueue) {
+ t.Fatalf("total transactions overflow allowance: %d > %d", queued, config.GlobalQueue)
+ }
+ } else {
+ // Local exemptions are enabled, make sure the local account owned the queue
+ if len(pool.queue) != 1 {
+ t.Errorf("multiple accounts in queue: have %v, want %v", len(pool.queue), 1)
+ }
+ // Also ensure no local transactions are ever dropped, even if above global limits
+ if queued := pool.queue[crypto.PubkeyToAddress(local.PublicKey)].Len(); uint64(queued) != 3*config.GlobalQueue {
+ t.Fatalf("local account queued transaction count mismatch: have %v, want %v", queued, 3*config.GlobalQueue)
+ }
+ }
+}
+
+// Tests that if an account remains idle for a prolonged amount of time, any
+// non-executable transactions queued up are dropped to prevent wasting resources
+// on shuffling them around.
+//
+// This logic should not hold for local transactions, unless the local tracking
+// mechanism is disabled.
+func TestTransactionQueueTimeLimiting(t *testing.T) { testTransactionQueueTimeLimiting(t, false) }
+func TestTransactionQueueTimeLimitingNoLocals(t *testing.T) { testTransactionQueueTimeLimiting(t, true) }
+
+func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
+ // Reduce the eviction interval to a testable amount
+ defer func(old time.Duration) { evictionInterval = old }(evictionInterval)
+ evictionInterval = time.Second
+
+ // Create the pool to test the non-expiration enforcement
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ config := testTxPoolConfig
+ config.Lifetime = time.Second
+ config.NoLocals = nolocals
+
+ pool := NewTxPool(config, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ // Create two test accounts to ensure remotes expire but locals do not
+ local, _ := crypto.GenerateKey()
+ remote, _ := crypto.GenerateKey()
+
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
+
+ // Add the two transactions and ensure they both are queued up
+ if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(1), local)); err != nil {
+ t.Fatalf("failed to add local transaction: %v", err)
+ }
+ if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), remote)); err != nil {
+ t.Fatalf("failed to add remote transaction: %v", err)
+ }
+ pending, queued := pool.Stats()
+ if pending != 0 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
+ }
+ if queued != 2 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Wait a bit for eviction to run and clean up any leftovers, and ensure only the local remains
+ time.Sleep(2 * config.Lifetime)
+
+ pending, queued = pool.Stats()
+ if pending != 0 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
+ }
+ if nolocals {
+ if queued != 0 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
+ }
+ } else {
+ if queued != 1 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
+ }
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
+// Tests that the transaction limits are enforced the same way irrelevant whether
+// the transactions are added one by one or in batches.
+func TestTransactionQueueLimitingEquivalency(t *testing.T) { testTransactionLimitingEquivalency(t, 1) }
+func TestTransactionPendingLimitingEquivalency(t *testing.T) { testTransactionLimitingEquivalency(t, 0) }
+
+func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
+ t.Parallel()
+
+ // Add a batch of transactions to a pool one by one
+ pool1, key1 := setupTxPool()
+ defer pool1.Stop()
+
+ account1, _ := deriveSender(transaction(0, 0, key1))
+ pool1.currentState.AddBalance(account1, big.NewInt(1000000))
+
+ for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
+ if err := pool1.AddRemote(transaction(origin+i, 100000, key1)); err != nil {
+ t.Fatalf("tx %d: failed to add transaction: %v", i, err)
+ }
+ }
+ // Add a batch of transactions to a pool in one big batch
+ pool2, key2 := setupTxPool()
+ defer pool2.Stop()
+
+ account2, _ := deriveSender(transaction(0, 0, key2))
+ pool2.currentState.AddBalance(account2, big.NewInt(1000000))
+
+ txs := []*types.Transaction{}
+ for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
+ txs = append(txs, transaction(origin+i, 100000, key2))
+ }
+ pool2.AddRemotes(txs)
+
+ // Ensure the batch optimization honors the same pool mechanics
+ if len(pool1.pending) != len(pool2.pending) {
+ t.Errorf("pending transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.pending), len(pool2.pending))
+ }
+ if len(pool1.queue) != len(pool2.queue) {
+ t.Errorf("queued transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.queue), len(pool2.queue))
+ }
+ if pool1.all.Count() != pool2.all.Count() {
+ t.Errorf("total transaction count mismatch: one-by-one algo %d, batch algo %d", pool1.all.Count(), pool2.all.Count())
+ }
+ if err := validateTxPoolInternals(pool1); err != nil {
+ t.Errorf("pool 1 internal state corrupted: %v", err)
+ }
+ if err := validateTxPoolInternals(pool2); err != nil {
+ t.Errorf("pool 2 internal state corrupted: %v", err)
+ }
+}
+
+// Tests that if the transaction count belonging to multiple accounts go above
+// some hard threshold, the higher transactions are dropped to prevent DOS
+// attacks.
+func TestTransactionPendingGlobalLimiting(t *testing.T) {
+ t.Parallel()
+
+ // Create the pool to test the limit enforcement with
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ config := testTxPoolConfig
+ config.GlobalSlots = config.AccountSlots * 10
+
+ pool := NewTxPool(config, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ // Create a number of test accounts and fund them
+ keys := make([]*ecdsa.PrivateKey, 5)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ }
+ // Generate and queue a batch of transactions
+ nonces := make(map[common.Address]uint64)
+
+ txs := types.Transactions{}
+ for _, key := range keys {
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ for j := 0; j < int(config.GlobalSlots)/len(keys)*2; j++ {
+ txs = append(txs, transaction(nonces[addr], 100000, key))
+ nonces[addr]++
+ }
+ }
+ // Import the batch and verify that limits have been enforced
+ pool.AddRemotes(txs)
+
+ pending := 0
+ for _, list := range pool.pending {
+ pending += list.Len()
+ }
+ if pending > int(config.GlobalSlots) {
+ t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, config.GlobalSlots)
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
+// Tests that if transactions start being capped, transactions are also removed from 'all'
+func TestTransactionCapClearsFromAll(t *testing.T) {
+ t.Parallel()
+
+ // Create the pool to test the limit enforcement with
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ config := testTxPoolConfig
+ config.AccountSlots = 2
+ config.AccountQueue = 2
+ config.GlobalSlots = 8
+
+ pool := NewTxPool(config, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ // Create a number of test accounts and fund them
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ pool.currentState.AddBalance(addr, big.NewInt(1000000))
+
+ txs := types.Transactions{}
+ for j := 0; j < int(config.GlobalSlots)*2; j++ {
+ txs = append(txs, transaction(uint64(j), 100000, key))
+ }
+ // Import the batch and verify that limits have been enforced
+ pool.AddRemotes(txs)
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
+// Tests that if the transaction count belonging to multiple accounts go above
+// some hard threshold, if they are under the minimum guaranteed slot count then
+// the transactions are still kept.
+func TestTransactionPendingMinimumAllowance(t *testing.T) {
+ t.Parallel()
+
+ // Create the pool to test the limit enforcement with
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ config := testTxPoolConfig
+ config.GlobalSlots = 0
+
+ pool := NewTxPool(config, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ // Create a number of test accounts and fund them
+ keys := make([]*ecdsa.PrivateKey, 5)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ }
+ // Generate and queue a batch of transactions
+ nonces := make(map[common.Address]uint64)
+
+ txs := types.Transactions{}
+ for _, key := range keys {
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ for j := 0; j < int(config.AccountSlots)*2; j++ {
+ txs = append(txs, transaction(nonces[addr], 100000, key))
+ nonces[addr]++
+ }
+ }
+ // Import the batch and verify that limits have been enforced
+ pool.AddRemotes(txs)
+
+ for addr, list := range pool.pending {
+ if list.Len() != int(config.AccountSlots) {
+ t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), config.AccountSlots)
+ }
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
+// Tests that setting the transaction pool gas price to a higher value does not
+// remove local transactions.
+func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
+ t.Parallel()
+
+ // Create the pool to test the pricing enforcement with
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ // Create a number of test accounts and fund them
+ keys := make([]*ecdsa.PrivateKey, 3)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000*1000000))
+ }
+ // Create transaction (both pending and queued) with a linearly growing gasprice
+ for i := uint64(0); i < 500; i++ {
+ // Add pending
+ p_tx := pricedTransaction(i, 100000, big.NewInt(int64(i)), keys[2])
+ if err := pool.AddLocal(p_tx); err != nil {
+ t.Fatal(err)
+ }
+ // Add queued
+ q_tx := pricedTransaction(i+501, 100000, big.NewInt(int64(i)), keys[2])
+ if err := pool.AddLocal(q_tx); err != nil {
+ t.Fatal(err)
+ }
+ }
+ pending, queued := pool.Stats()
+ expPending, expQueued := 500, 500
+ validate := func() {
+ pending, queued = pool.Stats()
+ if pending != expPending {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, expPending)
+ }
+ if queued != expQueued {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, expQueued)
+ }
+
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ }
+ validate()
+
+ // Reprice the pool and check that nothing is dropped
+ pool.SetGasPrice(big.NewInt(2))
+ validate()
+
+ pool.SetGasPrice(big.NewInt(2))
+ pool.SetGasPrice(big.NewInt(4))
+ pool.SetGasPrice(big.NewInt(8))
+ pool.SetGasPrice(big.NewInt(100))
+ validate()
+}
+
+// Tests that local transactions are journaled to disk, but remote transactions
+// get discarded between restarts.
+func TestTransactionJournaling(t *testing.T) { testTransactionJournaling(t, false) }
+func TestTransactionJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) }
+
+func testTransactionJournaling(t *testing.T, nolocals bool) {
+ t.Parallel()
+
+ // Create a temporary file for the journal
+ file, err := ioutil.TempFile("", "")
+ if err != nil {
+ t.Fatalf("failed to create temporary journal: %v", err)
+ }
+ journal := file.Name()
+ defer os.Remove(journal)
+
+ // Clean up the temporary file, we only need the path for now
+ file.Close()
+ os.Remove(journal)
+
+ // Create the original pool to inject transaction into the journal
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ config := testTxPoolConfig
+ config.NoLocals = nolocals
+ config.Journal = journal
+ config.Rejournal = time.Second
+
+ pool := NewTxPool(config, params.TestChainConfig, blockchain)
+
+ // Create two test accounts to ensure remotes expire but locals do not
+ local, _ := crypto.GenerateKey()
+ remote, _ := crypto.GenerateKey()
+
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
+
+ // Add three local and a remote transactions and ensure they are queued up
+ if err := pool.AddLocal(pricedTransaction(0, 100000, big.NewInt(1), local)); err != nil {
+ t.Fatalf("failed to add local transaction: %v", err)
+ }
+ if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(1), local)); err != nil {
+ t.Fatalf("failed to add local transaction: %v", err)
+ }
+ if err := pool.AddLocal(pricedTransaction(2, 100000, big.NewInt(1), local)); err != nil {
+ t.Fatalf("failed to add local transaction: %v", err)
+ }
+ if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), remote)); err != nil {
+ t.Fatalf("failed to add remote transaction: %v", err)
+ }
+ pending, queued := pool.Stats()
+ if pending != 4 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 4)
+ }
+ if queued != 0 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive
+ pool.Stop()
+ statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
+ blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ pool = NewTxPool(config, params.TestChainConfig, blockchain)
+
+ pending, queued = pool.Stats()
+ if queued != 0 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
+ }
+ if nolocals {
+ if pending != 0 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
+ }
+ } else {
+ if pending != 2 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
+ }
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Bump the nonce temporarily and ensure the newly invalidated transaction is removed
+ statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 2)
+ pool.lockedReset(nil, nil)
+ time.Sleep(2 * config.Rejournal)
+ pool.Stop()
+
+ statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
+ blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)}
+ pool = NewTxPool(config, params.TestChainConfig, blockchain)
+
+ pending, queued = pool.Stats()
+ if pending != 0 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
+ }
+ if nolocals {
+ if queued != 0 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
+ }
+ } else {
+ if queued != 1 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
+ }
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ pool.Stop()
+}
+
+// TestTransactionStatusCheck tests that the pool can correctly retrieve the
+// pending status of individual transactions.
+func TestTransactionStatusCheck(t *testing.T) {
+ t.Parallel()
+
+ // Create the pool to test the status retrievals with
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
+ blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
+ defer pool.Stop()
+
+ // Create the test accounts to check various transaction statuses with
+ keys := make([]*ecdsa.PrivateKey, 3)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ }
+ // Generate and queue a batch of transactions, both pending and queued
+ txs := types.Transactions{}
+
+ txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[0])) // Pending only
+ txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[1])) // Pending and queued
+ txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[1]))
+ txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[2])) // Queued only
+
+ // Import the transaction and ensure they are correctly added
+ pool.AddRemotes(txs)
+
+ pending, queued := pool.Stats()
+ if pending != 2 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
+ }
+ if queued != 2 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Retrieve the status of each transaction and validate them
+ hashes := make([]common.Hash, len(txs))
+ for i, tx := range txs {
+ hashes[i] = tx.Hash()
+ }
+ hashes = append(hashes, common.Hash{})
+
+ statuses := pool.Status(hashes)
+ expect := []TxStatus{TxStatusPending, TxStatusPending, TxStatusQueued, TxStatusQueued, TxStatusUnknown}
+
+ for i := 0; i < len(statuses); i++ {
+ if statuses[i] != expect[i] {
+ t.Errorf("transaction %d: status mismatch: have %v, want %v", i, statuses[i], expect[i])
+ }
+ }
+}
+
+// Benchmarks the speed of validating the contents of the pending queue of the
+// transaction pool.
+func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
+func BenchmarkPendingDemotion1000(b *testing.B) { benchmarkPendingDemotion(b, 1000) }
+func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 10000) }
+
+func benchmarkPendingDemotion(b *testing.B, size int) {
+ // Add a batch of transactions to a pool one by one
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ account, _ := deriveSender(transaction(0, 0, key))
+ pool.currentState.AddBalance(account, big.NewInt(1000000))
+
+ for i := 0; i < size; i++ {
+ tx := transaction(uint64(i), 100000, key)
+ pool.promoteTx(account, tx.Hash(), tx)
+ }
+ // Benchmark the speed of pool validation
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ pool.demoteUnexecutables()
+ }
+}
+
+// Benchmarks the speed of scheduling the contents of the future queue of the
+// transaction pool.
+func BenchmarkFuturePromotion100(b *testing.B) { benchmarkFuturePromotion(b, 100) }
+func BenchmarkFuturePromotion1000(b *testing.B) { benchmarkFuturePromotion(b, 1000) }
+func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 10000) }
+
+func benchmarkFuturePromotion(b *testing.B, size int) {
+ // Add a batch of transactions to a pool one by one
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ account, _ := deriveSender(transaction(0, 0, key))
+ pool.currentState.AddBalance(account, big.NewInt(1000000))
+
+ for i := 0; i < size; i++ {
+ tx := transaction(uint64(1+i), 100000, key)
+ pool.enqueueTx(tx.Hash(), tx)
+ }
+ // Benchmark the speed of pool validation
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ pool.promoteExecutables(nil)
+ }
+}
+
+// Benchmarks the speed of iterative transaction insertion.
+func BenchmarkPoolInsert(b *testing.B) {
+ // Generate a batch of transactions to enqueue into the pool
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ account, _ := deriveSender(transaction(0, 0, key))
+ pool.currentState.AddBalance(account, big.NewInt(1000000))
+
+ txs := make(types.Transactions, b.N)
+ for i := 0; i < b.N; i++ {
+ txs[i] = transaction(uint64(i), 100000, key)
+ }
+ // Benchmark importing the transactions into the queue
+ b.ResetTimer()
+ for _, tx := range txs {
+ pool.AddRemote(tx)
+ }
+}
+
+// Benchmarks the speed of batched transaction insertion.
+func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100) }
+func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000) }
+func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000) }
+
+func benchmarkPoolBatchInsert(b *testing.B, size int) {
+ // Generate a batch of transactions to enqueue into the pool
+ pool, key := setupTxPool()
+ defer pool.Stop()
+
+ account, _ := deriveSender(transaction(0, 0, key))
+ pool.currentState.AddBalance(account, big.NewInt(1000000))
+
+ batches := make([]types.Transactions, b.N)
+ for i := 0; i < b.N; i++ {
+ batches[i] = make(types.Transactions, size)
+ for j := 0; j < size; j++ {
+ batches[i][j] = transaction(uint64(size*i+j), 100000, key)
+ }
+ }
+ // Benchmark importing the transactions into the queue
+ b.ResetTimer()
+ for _, batch := range batches {
+ pool.AddRemotes(batch)
+ }
+}
diff --git a/core/types.go b/core/types.go
new file mode 100644
index 000000000..1c2b19577
--- /dev/null
+++ b/core/types.go
@@ -0,0 +1,46 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// Validator is an interface which defines the standard for block validation. It
+// is only responsible for validating block contents, as the header validation is
+// done by the specific consensus engines.
+//
+type Validator interface {
+ // ValidateBody validates the given block's content.
+ ValidateBody(block *types.Block) error
+
+ // ValidateState validates the given statedb and optionally the receipts and
+ // gas used.
+ ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error
+}
+
+// Processor is an interface for processing blocks using a given initial state.
+//
+// Process takes the block to be processed and the statedb upon which the
+// initial state is based. It should return the receipts generated, amount
+// of gas used in the process and return an error if any of the internal rules
+// failed.
+type Processor interface {
+ Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error)
+}
diff --git a/core/types/block.go b/core/types/block.go
new file mode 100644
index 000000000..8a21bba1e
--- /dev/null
+++ b/core/types/block.go
@@ -0,0 +1,391 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package types contains data types related to Ethereum consensus.
+package types
+
+import (
+ "encoding/binary"
+ "io"
+ "math/big"
+ "sort"
+ "sync/atomic"
+ "time"
+ "unsafe"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto/sha3"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+var (
+ EmptyRootHash = DeriveSha(Transactions{})
+ EmptyUncleHash = CalcUncleHash(nil)
+)
+
+// A BlockNonce is a 64-bit hash which proves (combined with the
+// mix-hash) that a sufficient amount of computation has been carried
+// out on a block.
+type BlockNonce [8]byte
+
+// EncodeNonce converts the given integer to a block nonce.
+func EncodeNonce(i uint64) BlockNonce {
+ var n BlockNonce
+ binary.BigEndian.PutUint64(n[:], i)
+ return n
+}
+
+// Uint64 returns the integer value of a block nonce.
+func (n BlockNonce) Uint64() uint64 {
+ return binary.BigEndian.Uint64(n[:])
+}
+
+// MarshalText encodes n as a hex string with 0x prefix.
+func (n BlockNonce) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(n[:]).MarshalText()
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (n *BlockNonce) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
+}
+
+//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
+
+// Header represents a block header in the Ethereum blockchain.
+type Header struct {
+ ParentHash common.Hash `json:"parentHash" gencodec:"required"`
+ UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
+ Coinbase common.Address `json:"miner" gencodec:"required"`
+ Root common.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
+ ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
+ Bloom Bloom `json:"logsBloom" gencodec:"required"`
+ Difficulty *big.Int `json:"difficulty" gencodec:"required"`
+ Number *big.Int `json:"number" gencodec:"required"`
+ GasLimit uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed uint64 `json:"gasUsed" gencodec:"required"`
+ Time *big.Int `json:"timestamp" gencodec:"required"`
+ Extra []byte `json:"extraData" gencodec:"required"`
+ MixDigest common.Hash `json:"mixHash" gencodec:"required"`
+ Nonce BlockNonce `json:"nonce" gencodec:"required"`
+}
+
+// field type overrides for gencodec
+type headerMarshaling struct {
+ Difficulty *hexutil.Big
+ Number *hexutil.Big
+ GasLimit hexutil.Uint64
+ GasUsed hexutil.Uint64
+ Time *hexutil.Big
+ Extra hexutil.Bytes
+ Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
+}
+
+// Hash returns the block hash of the header, which is simply the keccak256 hash of its
+// RLP encoding.
+func (h *Header) Hash() common.Hash {
+ return rlpHash(h)
+}
+
+// Size returns the approximate memory used by all internal contents. It is used
+// to approximate and limit the memory consumption of various caches.
+func (h *Header) Size() common.StorageSize {
+ return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
+}
+
+func rlpHash(x interface{}) (h common.Hash) {
+ hw := sha3.NewKeccak256()
+ rlp.Encode(hw, x)
+ hw.Sum(h[:0])
+ return h
+}
+
+// Body is a simple (mutable, non-safe) data container for storing and moving
+// a block's data contents (transactions and uncles) together.
+type Body struct {
+ Transactions []*Transaction
+ Uncles []*Header
+}
+
+// Block represents an entire block in the Ethereum blockchain.
+type Block struct {
+ header *Header
+ uncles []*Header
+ transactions Transactions
+
+ // caches
+ hash atomic.Value
+ size atomic.Value
+
+ // Td is used by package core to store the total difficulty
+ // of the chain up to and including the block.
+ td *big.Int
+
+ // These fields are used by package eth to track
+ // inter-peer block relay.
+ ReceivedAt time.Time
+ ReceivedFrom interface{}
+}
+
+// DeprecatedTd is an old relic for extracting the TD of a block. It is in the
+// code solely to facilitate upgrading the database from the old format to the
+// new, after which it should be deleted. Do not use!
+func (b *Block) DeprecatedTd() *big.Int {
+ return b.td
+}
+
+// [deprecated by eth/63]
+// StorageBlock defines the RLP encoding of a Block stored in the
+// state database. The StorageBlock encoding contains fields that
+// would otherwise need to be recomputed.
+type StorageBlock Block
+
+// "external" block encoding. used for eth protocol, etc.
+type extblock struct {
+ Header *Header
+ Txs []*Transaction
+ Uncles []*Header
+}
+
+// [deprecated by eth/63]
+// "storage" block encoding. used for database.
+type storageblock struct {
+ Header *Header
+ Txs []*Transaction
+ Uncles []*Header
+ TD *big.Int
+}
+
+// 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)
+ }
+
+ if len(receipts) == 0 {
+ b.header.ReceiptHash = EmptyRootHash
+ } else {
+ b.header.ReceiptHash = DeriveSha(Receipts(receipts))
+ b.header.Bloom = CreateBloom(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: CopyHeader(header)}
+}
+
+// CopyHeader creates a deep copy of a block header to prevent side effects from
+// modifying a header variable.
+func CopyHeader(h *Header) *Header {
+ cpy := *h
+ if cpy.Time = new(big.Int); h.Time != nil {
+ cpy.Time.Set(h.Time)
+ }
+ 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 len(h.Extra) > 0 {
+ cpy.Extra = make([]byte, len(h.Extra))
+ copy(cpy.Extra, h.Extra)
+ }
+ return &cpy
+}
+
+// DecodeRLP decodes the Ethereum
+func (b *Block) DecodeRLP(s *rlp.Stream) error {
+ var eb extblock
+ _, size, _ := s.Kind()
+ if err := s.Decode(&eb); err != nil {
+ return err
+ }
+ b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs
+ b.size.Store(common.StorageSize(rlp.ListSize(size)))
+ return nil
+}
+
+// EncodeRLP serializes b into the Ethereum RLP block format.
+func (b *Block) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, extblock{
+ Header: b.header,
+ Txs: b.transactions,
+ Uncles: b.uncles,
+ })
+}
+
+// [deprecated by eth/63]
+func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
+ var sb storageblock
+ if err := s.Decode(&sb); err != nil {
+ return err
+ }
+ b.header, b.uncles, b.transactions, b.td = sb.Header, sb.Uncles, sb.Txs, sb.TD
+ return nil
+}
+
+// TODO: copies
+
+func (b *Block) Uncles() []*Header { return b.uncles }
+func (b *Block) Transactions() Transactions { return b.transactions }
+
+func (b *Block) Transaction(hash common.Hash) *Transaction {
+ for _, transaction := range b.transactions {
+ if transaction.Hash() == hash {
+ return transaction
+ }
+ }
+ return nil
+}
+
+func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) }
+func (b *Block) GasLimit() uint64 { return b.header.GasLimit }
+func (b *Block) GasUsed() uint64 { return b.header.GasUsed }
+func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }
+func (b *Block) Time() *big.Int { return new(big.Int).Set(b.header.Time) }
+
+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) 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) }
+
+// Body returns the non-header content of the block.
+func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} }
+
+// Size returns the true RLP encoded storage size of the block, either by encoding
+// and returning it, or returning a previsouly cached value.
+func (b *Block) Size() common.StorageSize {
+ if size := b.size.Load(); size != nil {
+ return size.(common.StorageSize)
+ }
+ c := writeCounter(0)
+ rlp.Encode(&c, b)
+ b.size.Store(common.StorageSize(c))
+ return common.StorageSize(c)
+}
+
+type writeCounter common.StorageSize
+
+func (c *writeCounter) Write(b []byte) (int, error) {
+ *c += writeCounter(len(b))
+ return len(b), nil
+}
+
+func CalcUncleHash(uncles []*Header) common.Hash {
+ return rlpHash(uncles)
+}
+
+// WithSeal returns a new block with the data from b but the header replaced with
+// the sealed one.
+func (b *Block) WithSeal(header *Header) *Block {
+ cpy := *header
+
+ return &Block{
+ header: &cpy,
+ transactions: b.transactions,
+ uncles: b.uncles,
+ }
+}
+
+// WithBody returns a new block with the given transaction and uncle contents.
+func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block {
+ block := &Block{
+ header: CopyHeader(b.header),
+ transactions: make([]*Transaction, len(transactions)),
+ uncles: make([]*Header, len(uncles)),
+ }
+ copy(block.transactions, transactions)
+ for i := range uncles {
+ block.uncles[i] = CopyHeader(uncles[i])
+ }
+ return block
+}
+
+// Hash returns the keccak256 hash of b's header.
+// The hash is computed on the first call and cached thereafter.
+func (b *Block) Hash() common.Hash {
+ if hash := b.hash.Load(); hash != nil {
+ return hash.(common.Hash)
+ }
+ v := b.header.Hash()
+ b.hash.Store(v)
+ return v
+}
+
+type Blocks []*Block
+
+type BlockBy func(b1, b2 *Block) bool
+
+func (self BlockBy) Sort(blocks Blocks) {
+ bs := blockSorter{
+ blocks: blocks,
+ by: self,
+ }
+ sort.Sort(bs)
+}
+
+type blockSorter struct {
+ blocks Blocks
+ by func(b1, b2 *Block) bool
+}
+
+func (self blockSorter) Len() int { return len(self.blocks) }
+func (self blockSorter) Swap(i, j int) {
+ self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
+}
+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 }
diff --git a/core/types/block_test.go b/core/types/block_test.go
new file mode 100644
index 000000000..a35fbc25b
--- /dev/null
+++ b/core/types/block_test.go
@@ -0,0 +1,70 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+ "reflect"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// from bcValidBlockTest.json, "SimpleTx"
+func TestBlockEncoding(t *testing.T) {
+ blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
+ var block Block
+ if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
+ t.Fatal("decode error: ", err)
+ }
+
+ check := func(f string, got, want interface{}) {
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("%s mismatch: got %v, want %v", f, got, want)
+ }
+ }
+ check("Difficulty", block.Difficulty(), big.NewInt(131072))
+ check("GasLimit", block.GasLimit(), uint64(3141592))
+ check("GasUsed", block.GasUsed(), uint64(21000))
+ check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
+ check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
+ check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
+ check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e"))
+ check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
+ check("Time", block.Time(), big.NewInt(1426516743))
+ check("Size", block.Size(), common.StorageSize(len(blockEnc)))
+
+ tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil)
+
+ tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
+ fmt.Println(block.Transactions()[0].Hash())
+ fmt.Println(tx1.data)
+ fmt.Println(tx1.Hash())
+ check("len(Transactions)", len(block.Transactions()), 1)
+ check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
+
+ ourBlockEnc, err := rlp.EncodeToBytes(&block)
+ if err != nil {
+ t.Fatal("encode error: ", err)
+ }
+ if !bytes.Equal(ourBlockEnc, blockEnc) {
+ t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
+ }
+}
diff --git a/core/types/bloom9.go b/core/types/bloom9.go
new file mode 100644
index 000000000..d045c9e66
--- /dev/null
+++ b/core/types/bloom9.go
@@ -0,0 +1,136 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+type bytesBacked interface {
+ Bytes() []byte
+}
+
+const (
+ // BloomByteLength represents the number of bytes used in a header log bloom.
+ BloomByteLength = 256
+
+ // BloomBitLength represents the number of bits used in a header log bloom.
+ BloomBitLength = 8 * BloomByteLength
+)
+
+// Bloom represents a 2048 bit bloom filter.
+type Bloom [BloomByteLength]byte
+
+// BytesToBloom converts a byte slice to a bloom filter.
+// It panics if b is not of suitable size.
+func BytesToBloom(b []byte) Bloom {
+ var bloom Bloom
+ bloom.SetBytes(b)
+ return bloom
+}
+
+// SetBytes sets the content of b to the given bytes.
+// It panics if d is not of suitable size.
+func (b *Bloom) SetBytes(d []byte) {
+ if len(b) < len(d) {
+ panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d)))
+ }
+ copy(b[BloomByteLength-len(d):], d)
+}
+
+// Add adds d to the filter. Future calls of Test(d) will return true.
+func (b *Bloom) Add(d *big.Int) {
+ bin := new(big.Int).SetBytes(b[:])
+ bin.Or(bin, bloom9(d.Bytes()))
+ b.SetBytes(bin.Bytes())
+}
+
+// Big converts b to a big integer.
+func (b Bloom) Big() *big.Int {
+ return new(big.Int).SetBytes(b[:])
+}
+
+func (b Bloom) Bytes() []byte {
+ return b[:]
+}
+
+func (b Bloom) Test(test *big.Int) bool {
+ return BloomLookup(b, test)
+}
+
+func (b Bloom) TestBytes(test []byte) bool {
+ return b.Test(new(big.Int).SetBytes(test))
+
+}
+
+// MarshalText encodes b as a hex string with 0x prefix.
+func (b Bloom) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(b[:]).MarshalText()
+}
+
+// UnmarshalText b as a hex string with 0x prefix.
+func (b *Bloom) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedText("Bloom", input, b[:])
+}
+
+func CreateBloom(receipts Receipts) Bloom {
+ bin := new(big.Int)
+ for _, receipt := range receipts {
+ bin.Or(bin, LogsBloom(receipt.Logs))
+ }
+
+ return BytesToBloom(bin.Bytes())
+}
+
+func LogsBloom(logs []*Log) *big.Int {
+ bin := new(big.Int)
+ for _, log := range logs {
+ bin.Or(bin, bloom9(log.Address.Bytes()))
+ for _, b := range log.Topics {
+ bin.Or(bin, bloom9(b[:]))
+ }
+ }
+
+ return bin
+}
+
+func bloom9(b []byte) *big.Int {
+ b = crypto.Keccak256(b)
+
+ r := new(big.Int)
+
+ for i := 0; i < 6; i += 2 {
+ t := big.NewInt(1)
+ b := (uint(b[i+1]) + (uint(b[i]) << 8)) & 2047
+ r.Or(r, t.Lsh(t, b))
+ }
+
+ return r
+}
+
+var Bloom9 = bloom9
+
+func BloomLookup(bin Bloom, topic bytesBacked) bool {
+ bloom := bin.Big()
+ cmp := bloom9(topic.Bytes())
+
+ return bloom.And(bloom, cmp).Cmp(cmp) == 0
+}
diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go
new file mode 100644
index 000000000..a28ac0e7a
--- /dev/null
+++ b/core/types/bloom9_test.go
@@ -0,0 +1,81 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "math/big"
+ "testing"
+)
+
+func TestBloom(t *testing.T) {
+ positive := []string{
+ "testtest",
+ "test",
+ "hallo",
+ "other",
+ }
+ negative := []string{
+ "tes",
+ "lo",
+ }
+
+ var bloom Bloom
+ for _, data := range positive {
+ bloom.Add(new(big.Int).SetBytes([]byte(data)))
+ }
+
+ for _, data := range positive {
+ if !bloom.TestBytes([]byte(data)) {
+ t.Error("expected", data, "to test true")
+ }
+ }
+ for _, data := range negative {
+ if bloom.TestBytes([]byte(data)) {
+ t.Error("did not expect", data, "to test true")
+ }
+ }
+}
+
+/*
+import (
+ "testing"
+
+ "github.com/ethereum/go-ethereum/core/state"
+)
+
+func TestBloom9(t *testing.T) {
+ testCase := []byte("testtest")
+ bin := LogsBloom([]state.Log{
+ {testCase, [][]byte{[]byte("hellohello")}, nil},
+ }).Bytes()
+ res := BloomLookup(bin, testCase)
+
+ if !res {
+ t.Errorf("Bloom lookup failed")
+ }
+}
+
+
+func TestAddress(t *testing.T) {
+ block := &Block{}
+ block.Coinbase = common.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f")
+ fmt.Printf("%x\n", crypto.Keccak256(block.Coinbase))
+
+ bin := CreateBloom(block)
+ fmt.Printf("bin = %x\n", common.LeftPadBytes(bin, 64))
+}
+*/
diff --git a/core/types/derive_sha.go b/core/types/derive_sha.go
new file mode 100644
index 000000000..00c42c5bc
--- /dev/null
+++ b/core/types/derive_sha.go
@@ -0,0 +1,41 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "bytes"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
+)
+
+type DerivableList interface {
+ Len() int
+ GetRlp(i int) []byte
+}
+
+func DeriveSha(list DerivableList) common.Hash {
+ keybuf := new(bytes.Buffer)
+ trie := new(trie.Trie)
+ for i := 0; i < list.Len(); i++ {
+ keybuf.Reset()
+ rlp.Encode(keybuf, uint(i))
+ trie.Update(keybuf.Bytes(), list.GetRlp(i))
+ }
+ return trie.Hash()
+}
diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go
new file mode 100644
index 000000000..1b92cd9cf
--- /dev/null
+++ b/core/types/gen_header_json.go
@@ -0,0 +1,138 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package types
+
+import (
+ "encoding/json"
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*headerMarshaling)(nil)
+
+func (h Header) MarshalJSON() ([]byte, error) {
+ type Header struct {
+ ParentHash common.Hash `json:"parentHash" gencodec:"required"`
+ UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
+ Coinbase common.Address `json:"miner" gencodec:"required"`
+ Root common.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
+ ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
+ Bloom Bloom `json:"logsBloom" gencodec:"required"`
+ Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
+ Number *hexutil.Big `json:"number" gencodec:"required"`
+ GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
+ Time *hexutil.Big `json:"timestamp" gencodec:"required"`
+ Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
+ MixDigest common.Hash `json:"mixHash" gencodec:"required"`
+ Nonce BlockNonce `json:"nonce" gencodec:"required"`
+ Hash common.Hash `json:"hash"`
+ }
+ var enc Header
+ enc.ParentHash = h.ParentHash
+ enc.UncleHash = h.UncleHash
+ enc.Coinbase = h.Coinbase
+ enc.Root = h.Root
+ enc.TxHash = h.TxHash
+ enc.ReceiptHash = h.ReceiptHash
+ enc.Bloom = h.Bloom
+ enc.Difficulty = (*hexutil.Big)(h.Difficulty)
+ enc.Number = (*hexutil.Big)(h.Number)
+ enc.GasLimit = hexutil.Uint64(h.GasLimit)
+ enc.GasUsed = hexutil.Uint64(h.GasUsed)
+ enc.Time = (*hexutil.Big)(h.Time)
+ enc.Extra = h.Extra
+ enc.MixDigest = h.MixDigest
+ enc.Nonce = h.Nonce
+ enc.Hash = h.Hash()
+ return json.Marshal(&enc)
+}
+
+func (h *Header) UnmarshalJSON(input []byte) error {
+ type Header struct {
+ ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
+ UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
+ Coinbase *common.Address `json:"miner" gencodec:"required"`
+ Root *common.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
+ ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
+ Bloom *Bloom `json:"logsBloom" gencodec:"required"`
+ Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
+ Number *hexutil.Big `json:"number" gencodec:"required"`
+ GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
+ Time *hexutil.Big `json:"timestamp" gencodec:"required"`
+ Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
+ MixDigest *common.Hash `json:"mixHash" gencodec:"required"`
+ Nonce *BlockNonce `json:"nonce" gencodec:"required"`
+ }
+ var dec Header
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.ParentHash == nil {
+ return errors.New("missing required field 'parentHash' for Header")
+ }
+ h.ParentHash = *dec.ParentHash
+ if dec.UncleHash == nil {
+ return errors.New("missing required field 'sha3Uncles' for Header")
+ }
+ h.UncleHash = *dec.UncleHash
+ if dec.Coinbase == nil {
+ return errors.New("missing required field 'miner' for Header")
+ }
+ h.Coinbase = *dec.Coinbase
+ if dec.Root == nil {
+ return errors.New("missing required field 'stateRoot' for Header")
+ }
+ h.Root = *dec.Root
+ if dec.TxHash == nil {
+ return errors.New("missing required field 'transactionsRoot' for Header")
+ }
+ h.TxHash = *dec.TxHash
+ if dec.ReceiptHash == nil {
+ return errors.New("missing required field 'receiptsRoot' for Header")
+ }
+ h.ReceiptHash = *dec.ReceiptHash
+ if dec.Bloom == nil {
+ return errors.New("missing required field 'logsBloom' for Header")
+ }
+ h.Bloom = *dec.Bloom
+ if dec.Difficulty == nil {
+ return errors.New("missing required field 'difficulty' for Header")
+ }
+ h.Difficulty = (*big.Int)(dec.Difficulty)
+ if dec.Number == nil {
+ return errors.New("missing required field 'number' for Header")
+ }
+ h.Number = (*big.Int)(dec.Number)
+ if dec.GasLimit == nil {
+ return errors.New("missing required field 'gasLimit' for Header")
+ }
+ h.GasLimit = uint64(*dec.GasLimit)
+ if dec.GasUsed == nil {
+ return errors.New("missing required field 'gasUsed' for Header")
+ }
+ h.GasUsed = uint64(*dec.GasUsed)
+ if dec.Time == nil {
+ return errors.New("missing required field 'timestamp' for Header")
+ }
+ h.Time = (*big.Int)(dec.Time)
+ if dec.Extra == nil {
+ return errors.New("missing required field 'extraData' for Header")
+ }
+ h.Extra = *dec.Extra
+ if dec.MixDigest == nil {
+ return errors.New("missing required field 'mixHash' for Header")
+ }
+ h.MixDigest = *dec.MixDigest
+ if dec.Nonce == nil {
+ return errors.New("missing required field 'nonce' for Header")
+ }
+ h.Nonce = *dec.Nonce
+ return nil
+}
diff --git a/core/types/gen_log_json.go b/core/types/gen_log_json.go
new file mode 100644
index 000000000..1b5ae3c65
--- /dev/null
+++ b/core/types/gen_log_json.go
@@ -0,0 +1,90 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package types
+
+import (
+ "encoding/json"
+ "errors"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*logMarshaling)(nil)
+
+func (l Log) MarshalJSON() ([]byte, error) {
+ type Log struct {
+ Address common.Address `json:"address" gencodec:"required"`
+ Topics []common.Hash `json:"topics" gencodec:"required"`
+ Data hexutil.Bytes `json:"data" gencodec:"required"`
+ BlockNumber hexutil.Uint64 `json:"blockNumber"`
+ TxHash common.Hash `json:"transactionHash" gencodec:"required"`
+ TxIndex hexutil.Uint `json:"transactionIndex" gencodec:"required"`
+ BlockHash common.Hash `json:"blockHash"`
+ Index hexutil.Uint `json:"logIndex" gencodec:"required"`
+ Removed bool `json:"removed"`
+ }
+ var enc Log
+ enc.Address = l.Address
+ enc.Topics = l.Topics
+ enc.Data = l.Data
+ enc.BlockNumber = hexutil.Uint64(l.BlockNumber)
+ enc.TxHash = l.TxHash
+ enc.TxIndex = hexutil.Uint(l.TxIndex)
+ enc.BlockHash = l.BlockHash
+ enc.Index = hexutil.Uint(l.Index)
+ enc.Removed = l.Removed
+ return json.Marshal(&enc)
+}
+
+func (l *Log) UnmarshalJSON(input []byte) error {
+ type Log struct {
+ Address *common.Address `json:"address" gencodec:"required"`
+ Topics []common.Hash `json:"topics" gencodec:"required"`
+ Data *hexutil.Bytes `json:"data" gencodec:"required"`
+ BlockNumber *hexutil.Uint64 `json:"blockNumber"`
+ TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
+ TxIndex *hexutil.Uint `json:"transactionIndex" gencodec:"required"`
+ BlockHash *common.Hash `json:"blockHash"`
+ Index *hexutil.Uint `json:"logIndex" gencodec:"required"`
+ Removed *bool `json:"removed"`
+ }
+ var dec Log
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Address == nil {
+ return errors.New("missing required field 'address' for Log")
+ }
+ l.Address = *dec.Address
+ if dec.Topics == nil {
+ return errors.New("missing required field 'topics' for Log")
+ }
+ l.Topics = dec.Topics
+ if dec.Data == nil {
+ return errors.New("missing required field 'data' for Log")
+ }
+ l.Data = *dec.Data
+ if dec.BlockNumber != nil {
+ l.BlockNumber = uint64(*dec.BlockNumber)
+ }
+ if dec.TxHash == nil {
+ return errors.New("missing required field 'transactionHash' for Log")
+ }
+ l.TxHash = *dec.TxHash
+ if dec.TxIndex == nil {
+ return errors.New("missing required field 'transactionIndex' for Log")
+ }
+ l.TxIndex = uint(*dec.TxIndex)
+ if dec.BlockHash != nil {
+ l.BlockHash = *dec.BlockHash
+ }
+ if dec.Index == nil {
+ return errors.New("missing required field 'logIndex' for Log")
+ }
+ l.Index = uint(*dec.Index)
+ if dec.Removed != nil {
+ l.Removed = *dec.Removed
+ }
+ return nil
+}
diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go
new file mode 100644
index 000000000..5c807a4cc
--- /dev/null
+++ b/core/types/gen_receipt_json.go
@@ -0,0 +1,85 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package types
+
+import (
+ "encoding/json"
+ "errors"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*receiptMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (r Receipt) MarshalJSON() ([]byte, error) {
+ type Receipt struct {
+ PostState hexutil.Bytes `json:"root"`
+ Status hexutil.Uint64 `json:"status"`
+ CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
+ Bloom Bloom `json:"logsBloom" gencodec:"required"`
+ Logs []*Log `json:"logs" gencodec:"required"`
+ TxHash common.Hash `json:"transactionHash" gencodec:"required"`
+ ContractAddress common.Address `json:"contractAddress"`
+ GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
+ }
+ var enc Receipt
+ enc.PostState = r.PostState
+ enc.Status = hexutil.Uint64(r.Status)
+ enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed)
+ enc.Bloom = r.Bloom
+ enc.Logs = r.Logs
+ enc.TxHash = r.TxHash
+ enc.ContractAddress = r.ContractAddress
+ enc.GasUsed = hexutil.Uint64(r.GasUsed)
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (r *Receipt) UnmarshalJSON(input []byte) error {
+ type Receipt struct {
+ PostState *hexutil.Bytes `json:"root"`
+ Status *hexutil.Uint64 `json:"status"`
+ CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"`
+ Bloom *Bloom `json:"logsBloom" gencodec:"required"`
+ Logs []*Log `json:"logs" gencodec:"required"`
+ TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
+ ContractAddress *common.Address `json:"contractAddress"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
+ }
+ var dec Receipt
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.PostState != nil {
+ r.PostState = *dec.PostState
+ }
+ if dec.Status != nil {
+ r.Status = uint64(*dec.Status)
+ }
+ if dec.CumulativeGasUsed == nil {
+ return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
+ }
+ r.CumulativeGasUsed = uint64(*dec.CumulativeGasUsed)
+ if dec.Bloom == nil {
+ return errors.New("missing required field 'logsBloom' for Receipt")
+ }
+ r.Bloom = *dec.Bloom
+ if dec.Logs == nil {
+ return errors.New("missing required field 'logs' for Receipt")
+ }
+ r.Logs = dec.Logs
+ if dec.TxHash == nil {
+ return errors.New("missing required field 'transactionHash' for Receipt")
+ }
+ r.TxHash = *dec.TxHash
+ if dec.ContractAddress != nil {
+ r.ContractAddress = *dec.ContractAddress
+ }
+ if dec.GasUsed == nil {
+ return errors.New("missing required field 'gasUsed' for Receipt")
+ }
+ r.GasUsed = uint64(*dec.GasUsed)
+ return nil
+}
diff --git a/core/types/gen_tx_json.go b/core/types/gen_tx_json.go
new file mode 100644
index 000000000..c27da6709
--- /dev/null
+++ b/core/types/gen_tx_json.go
@@ -0,0 +1,99 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package types
+
+import (
+ "encoding/json"
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*txdataMarshaling)(nil)
+
+func (t txdata) MarshalJSON() ([]byte, error) {
+ type txdata struct {
+ AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
+ Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
+ GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"`
+ Recipient *common.Address `json:"to" rlp:"nil"`
+ Amount *hexutil.Big `json:"value" gencodec:"required"`
+ Payload hexutil.Bytes `json:"input" gencodec:"required"`
+ V *hexutil.Big `json:"v" gencodec:"required"`
+ R *hexutil.Big `json:"r" gencodec:"required"`
+ S *hexutil.Big `json:"s" gencodec:"required"`
+ Hash *common.Hash `json:"hash" rlp:"-"`
+ }
+ var enc txdata
+ enc.AccountNonce = hexutil.Uint64(t.AccountNonce)
+ enc.Price = (*hexutil.Big)(t.Price)
+ enc.GasLimit = hexutil.Uint64(t.GasLimit)
+ enc.Recipient = t.Recipient
+ enc.Amount = (*hexutil.Big)(t.Amount)
+ enc.Payload = t.Payload
+ enc.V = (*hexutil.Big)(t.V)
+ enc.R = (*hexutil.Big)(t.R)
+ enc.S = (*hexutil.Big)(t.S)
+ enc.Hash = t.Hash
+ return json.Marshal(&enc)
+}
+
+func (t *txdata) UnmarshalJSON(input []byte) error {
+ type txdata struct {
+ AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
+ Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
+ GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"`
+ Recipient *common.Address `json:"to" rlp:"nil"`
+ Amount *hexutil.Big `json:"value" gencodec:"required"`
+ Payload *hexutil.Bytes `json:"input" gencodec:"required"`
+ V *hexutil.Big `json:"v" gencodec:"required"`
+ R *hexutil.Big `json:"r" gencodec:"required"`
+ S *hexutil.Big `json:"s" gencodec:"required"`
+ Hash *common.Hash `json:"hash" rlp:"-"`
+ }
+ var dec txdata
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.AccountNonce == nil {
+ return errors.New("missing required field 'nonce' for txdata")
+ }
+ t.AccountNonce = uint64(*dec.AccountNonce)
+ if dec.Price == nil {
+ return errors.New("missing required field 'gasPrice' for txdata")
+ }
+ t.Price = (*big.Int)(dec.Price)
+ if dec.GasLimit == nil {
+ return errors.New("missing required field 'gas' for txdata")
+ }
+ t.GasLimit = uint64(*dec.GasLimit)
+ if dec.Recipient != nil {
+ t.Recipient = dec.Recipient
+ }
+ if dec.Amount == nil {
+ return errors.New("missing required field 'value' for txdata")
+ }
+ t.Amount = (*big.Int)(dec.Amount)
+ if dec.Payload == nil {
+ return errors.New("missing required field 'input' for txdata")
+ }
+ t.Payload = *dec.Payload
+ if dec.V == nil {
+ return errors.New("missing required field 'v' for txdata")
+ }
+ t.V = (*big.Int)(dec.V)
+ if dec.R == nil {
+ return errors.New("missing required field 'r' for txdata")
+ }
+ t.R = (*big.Int)(dec.R)
+ if dec.S == nil {
+ return errors.New("missing required field 's' for txdata")
+ }
+ t.S = (*big.Int)(dec.S)
+ if dec.Hash != nil {
+ t.Hash = dec.Hash
+ }
+ return nil
+}
diff --git a/core/types/log.go b/core/types/log.go
new file mode 100644
index 000000000..717cd2e5a
--- /dev/null
+++ b/core/types/log.go
@@ -0,0 +1,132 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "io"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+//go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go
+
+// Log represents a contract log event. These events are generated by the LOG opcode and
+// stored/indexed by the node.
+type Log struct {
+ // Consensus fields:
+ // address of the contract that generated the event
+ Address common.Address `json:"address" gencodec:"required"`
+ // list of topics provided by the contract.
+ Topics []common.Hash `json:"topics" gencodec:"required"`
+ // supplied by the contract, usually ABI-encoded
+ Data []byte `json:"data" gencodec:"required"`
+
+ // Derived fields. These fields are filled in by the node
+ // but not secured by consensus.
+ // block in which the transaction was included
+ BlockNumber uint64 `json:"blockNumber"`
+ // hash of the transaction
+ TxHash common.Hash `json:"transactionHash" gencodec:"required"`
+ // index of the transaction in the block
+ TxIndex uint `json:"transactionIndex" gencodec:"required"`
+ // hash of the block in which the transaction was included
+ BlockHash common.Hash `json:"blockHash"`
+ // index of the log in the block
+ Index uint `json:"logIndex" gencodec:"required"`
+
+ // The Removed field is true if this log was reverted due to a chain reorganisation.
+ // You must pay attention to this field if you receive logs through a filter query.
+ Removed bool `json:"removed"`
+}
+
+type logMarshaling struct {
+ Data hexutil.Bytes
+ BlockNumber hexutil.Uint64
+ TxIndex hexutil.Uint
+ Index hexutil.Uint
+}
+
+type rlpLog struct {
+ Address common.Address
+ Topics []common.Hash
+ Data []byte
+}
+
+type rlpStorageLog struct {
+ Address common.Address
+ Topics []common.Hash
+ Data []byte
+ BlockNumber uint64
+ TxHash common.Hash
+ TxIndex uint
+ BlockHash common.Hash
+ Index uint
+}
+
+// EncodeRLP implements rlp.Encoder.
+func (l *Log) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
+}
+
+// DecodeRLP implements rlp.Decoder.
+func (l *Log) DecodeRLP(s *rlp.Stream) error {
+ var dec rlpLog
+ err := s.Decode(&dec)
+ if err == nil {
+ l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data
+ }
+ return err
+}
+
+// LogForStorage is a wrapper around a Log that flattens and parses the entire content of
+// a log including non-consensus fields.
+type LogForStorage Log
+
+// EncodeRLP implements rlp.Encoder.
+func (l *LogForStorage) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, rlpStorageLog{
+ Address: l.Address,
+ Topics: l.Topics,
+ Data: l.Data,
+ BlockNumber: l.BlockNumber,
+ TxHash: l.TxHash,
+ TxIndex: l.TxIndex,
+ BlockHash: l.BlockHash,
+ Index: l.Index,
+ })
+}
+
+// DecodeRLP implements rlp.Decoder.
+func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
+ var dec rlpStorageLog
+ err := s.Decode(&dec)
+ if err == nil {
+ *l = LogForStorage{
+ Address: dec.Address,
+ Topics: dec.Topics,
+ Data: dec.Data,
+ BlockNumber: dec.BlockNumber,
+ TxHash: dec.TxHash,
+ TxIndex: dec.TxIndex,
+ BlockHash: dec.BlockHash,
+ Index: dec.Index,
+ }
+ }
+ return err
+}
diff --git a/core/types/log_test.go b/core/types/log_test.go
new file mode 100644
index 000000000..1baebdcb1
--- /dev/null
+++ b/core/types/log_test.go
@@ -0,0 +1,115 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var unmarshalLogTests = map[string]struct {
+ input string
+ want *Log
+ wantError error
+}{
+ "ok": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
+ want: &Log{
+ Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
+ BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"),
+ BlockNumber: 2019236,
+ Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000001a055690d9db80000"),
+ Index: 2,
+ TxIndex: 3,
+ TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
+ Topics: []common.Hash{
+ common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
+ common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"),
+ },
+ },
+ },
+ "empty data": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
+ want: &Log{
+ Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
+ BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"),
+ BlockNumber: 2019236,
+ Data: []byte{},
+ Index: 2,
+ TxIndex: 3,
+ TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
+ Topics: []common.Hash{
+ common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
+ common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"),
+ },
+ },
+ },
+ "missing block fields (pending logs)": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","data":"0x","logIndex":"0x0","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
+ want: &Log{
+ Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
+ BlockHash: common.Hash{},
+ BlockNumber: 0,
+ Data: []byte{},
+ Index: 0,
+ TxIndex: 3,
+ TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
+ Topics: []common.Hash{
+ common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
+ },
+ },
+ },
+ "Removed: true": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3","removed":true}`,
+ want: &Log{
+ Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
+ BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"),
+ BlockNumber: 2019236,
+ Data: []byte{},
+ Index: 2,
+ TxIndex: 3,
+ TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
+ Topics: []common.Hash{
+ common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
+ },
+ Removed: true,
+ },
+ },
+ "missing data": {
+ input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
+ wantError: fmt.Errorf("missing required field 'data' for Log"),
+ },
+}
+
+func checkError(t *testing.T, testname string, got, want error) bool {
+ if got == nil {
+ if want != nil {
+ t.Errorf("test %q: got no error, want %q", testname, want)
+ return false
+ }
+ return true
+ }
+ if want == nil {
+ t.Errorf("test %q: unexpected error %q", testname, got)
+ } else if got.Error() != want.Error() {
+ t.Errorf("test %q: got error %q, want %q", testname, got, want)
+ }
+ return false
+}
diff --git a/core/types/receipt.go b/core/types/receipt.go
new file mode 100644
index 000000000..3d1fc95aa
--- /dev/null
+++ b/core/types/receipt.go
@@ -0,0 +1,208 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "unsafe"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
+
+var (
+ receiptStatusFailedRLP = []byte{}
+ receiptStatusSuccessfulRLP = []byte{0x01}
+)
+
+const (
+ // ReceiptStatusFailed is the status code of a transaction if execution failed.
+ ReceiptStatusFailed = uint64(0)
+
+ // ReceiptStatusSuccessful is the status code of a transaction if execution succeeded.
+ ReceiptStatusSuccessful = uint64(1)
+)
+
+// Receipt represents the results of a transaction.
+type Receipt struct {
+ // Consensus fields
+ PostState []byte `json:"root"`
+ Status uint64 `json:"status"`
+ CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
+ Bloom Bloom `json:"logsBloom" gencodec:"required"`
+ Logs []*Log `json:"logs" gencodec:"required"`
+
+ // Implementation fields (don't reorder!)
+ TxHash common.Hash `json:"transactionHash" gencodec:"required"`
+ ContractAddress common.Address `json:"contractAddress"`
+ GasUsed uint64 `json:"gasUsed" gencodec:"required"`
+}
+
+type receiptMarshaling struct {
+ PostState hexutil.Bytes
+ Status hexutil.Uint64
+ CumulativeGasUsed hexutil.Uint64
+ GasUsed hexutil.Uint64
+}
+
+// receiptRLP is the consensus encoding of a receipt.
+type receiptRLP struct {
+ PostStateOrStatus []byte
+ CumulativeGasUsed uint64
+ Bloom Bloom
+ Logs []*Log
+}
+
+type receiptStorageRLP struct {
+ PostStateOrStatus []byte
+ CumulativeGasUsed uint64
+ Bloom Bloom
+ TxHash common.Hash
+ ContractAddress common.Address
+ Logs []*LogForStorage
+ GasUsed uint64
+}
+
+// NewReceipt creates a barebone transaction receipt, copying the init fields.
+func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
+ r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed}
+ if failed {
+ r.Status = ReceiptStatusFailed
+ } else {
+ r.Status = ReceiptStatusSuccessful
+ }
+ return r
+}
+
+// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
+// into an RLP stream. If no post state is present, byzantium fork is assumed.
+func (r *Receipt) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs})
+}
+
+// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
+// from an RLP stream.
+func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
+ var dec receiptRLP
+ if err := s.Decode(&dec); err != nil {
+ return err
+ }
+ if err := r.setStatus(dec.PostStateOrStatus); err != nil {
+ return err
+ }
+ r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs
+ return nil
+}
+
+func (r *Receipt) setStatus(postStateOrStatus []byte) error {
+ switch {
+ case bytes.Equal(postStateOrStatus, receiptStatusSuccessfulRLP):
+ r.Status = ReceiptStatusSuccessful
+ case bytes.Equal(postStateOrStatus, receiptStatusFailedRLP):
+ r.Status = ReceiptStatusFailed
+ case len(postStateOrStatus) == len(common.Hash{}):
+ r.PostState = postStateOrStatus
+ default:
+ return fmt.Errorf("invalid receipt status %x", postStateOrStatus)
+ }
+ return nil
+}
+
+func (r *Receipt) statusEncoding() []byte {
+ if len(r.PostState) == 0 {
+ if r.Status == ReceiptStatusFailed {
+ return receiptStatusFailedRLP
+ }
+ return receiptStatusSuccessfulRLP
+ }
+ return r.PostState
+}
+
+// Size returns the approximate memory used by all internal contents. It is used
+// to approximate and limit the memory consumption of various caches.
+func (r *Receipt) Size() common.StorageSize {
+ size := common.StorageSize(unsafe.Sizeof(*r)) + common.StorageSize(len(r.PostState))
+
+ size += common.StorageSize(len(r.Logs)) * common.StorageSize(unsafe.Sizeof(Log{}))
+ for _, log := range r.Logs {
+ size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data))
+ }
+ return size
+}
+
+// ReceiptForStorage is a wrapper around a Receipt that flattens and parses the
+// entire content of a receipt, as opposed to only the consensus fields originally.
+type ReceiptForStorage Receipt
+
+// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
+// into an RLP stream.
+func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
+ enc := &receiptStorageRLP{
+ PostStateOrStatus: (*Receipt)(r).statusEncoding(),
+ CumulativeGasUsed: r.CumulativeGasUsed,
+ Bloom: r.Bloom,
+ TxHash: r.TxHash,
+ ContractAddress: r.ContractAddress,
+ Logs: make([]*LogForStorage, len(r.Logs)),
+ GasUsed: r.GasUsed,
+ }
+ for i, log := range r.Logs {
+ enc.Logs[i] = (*LogForStorage)(log)
+ }
+ return rlp.Encode(w, enc)
+}
+
+// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
+// fields of a receipt from an RLP stream.
+func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
+ var dec receiptStorageRLP
+ if err := s.Decode(&dec); err != nil {
+ return err
+ }
+ if err := (*Receipt)(r).setStatus(dec.PostStateOrStatus); err != nil {
+ return err
+ }
+ // Assign the consensus fields
+ r.CumulativeGasUsed, r.Bloom = dec.CumulativeGasUsed, dec.Bloom
+ r.Logs = make([]*Log, len(dec.Logs))
+ for i, log := range dec.Logs {
+ r.Logs[i] = (*Log)(log)
+ }
+ // Assign the implementation fields
+ r.TxHash, r.ContractAddress, r.GasUsed = dec.TxHash, dec.ContractAddress, dec.GasUsed
+ return nil
+}
+
+// Receipts is a wrapper around a Receipt array to implement DerivableList.
+type Receipts []*Receipt
+
+// Len returns the number of receipts in this list.
+func (r Receipts) Len() int { return len(r) }
+
+// GetRlp returns the RLP encoding of one receipt from the list.
+func (r Receipts) GetRlp(i int) []byte {
+ bytes, err := rlp.EncodeToBytes(r[i])
+ if err != nil {
+ panic(err)
+ }
+ return bytes
+}
diff --git a/core/types/transaction.go b/core/types/transaction.go
new file mode 100644
index 000000000..7b53cac2c
--- /dev/null
+++ b/core/types/transaction.go
@@ -0,0 +1,417 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "container/heap"
+ "errors"
+ "io"
+ "math/big"
+ "sync/atomic"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
+
+var (
+ ErrInvalidSig = errors.New("invalid transaction v, r, s values")
+)
+
+type Transaction struct {
+ data txdata
+ // caches
+ hash atomic.Value
+ size atomic.Value
+ from atomic.Value
+}
+
+type txdata struct {
+ AccountNonce uint64 `json:"nonce" gencodec:"required"`
+ Price *big.Int `json:"gasPrice" gencodec:"required"`
+ GasLimit uint64 `json:"gas" gencodec:"required"`
+ Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
+ Amount *big.Int `json:"value" gencodec:"required"`
+ Payload []byte `json:"input" gencodec:"required"`
+
+ // Signature values
+ V *big.Int `json:"v" gencodec:"required"`
+ R *big.Int `json:"r" gencodec:"required"`
+ S *big.Int `json:"s" gencodec:"required"`
+
+ // This is only used when marshaling to JSON.
+ Hash *common.Hash `json:"hash" rlp:"-"`
+}
+
+type txdataMarshaling struct {
+ AccountNonce hexutil.Uint64
+ Price *hexutil.Big
+ GasLimit hexutil.Uint64
+ Amount *hexutil.Big
+ Payload hexutil.Bytes
+ V *hexutil.Big
+ R *hexutil.Big
+ S *hexutil.Big
+}
+
+func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
+ return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
+}
+
+func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
+ return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
+}
+
+func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
+ if len(data) > 0 {
+ data = common.CopyBytes(data)
+ }
+ d := txdata{
+ AccountNonce: nonce,
+ Recipient: to,
+ Payload: data,
+ Amount: new(big.Int),
+ GasLimit: gasLimit,
+ Price: new(big.Int),
+ V: new(big.Int),
+ R: new(big.Int),
+ S: new(big.Int),
+ }
+ if amount != nil {
+ d.Amount.Set(amount)
+ }
+ if gasPrice != nil {
+ d.Price.Set(gasPrice)
+ }
+
+ return &Transaction{data: d}
+}
+
+// ChainId returns which chain id this transaction was signed for (if at all)
+func (tx *Transaction) ChainId() *big.Int {
+ return deriveChainId(tx.data.V)
+}
+
+// Protected returns whether the transaction is protected from replay protection.
+func (tx *Transaction) Protected() bool {
+ return isProtectedV(tx.data.V)
+}
+
+func isProtectedV(V *big.Int) bool {
+ if V.BitLen() <= 8 {
+ v := V.Uint64()
+ return v != 27 && v != 28
+ }
+ // anything not 27 or 28 is considered protected
+ return true
+}
+
+// EncodeRLP implements rlp.Encoder
+func (tx *Transaction) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, &tx.data)
+}
+
+// DecodeRLP implements rlp.Decoder
+func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
+ _, size, _ := s.Kind()
+ err := s.Decode(&tx.data)
+ if err == nil {
+ tx.size.Store(common.StorageSize(rlp.ListSize(size)))
+ }
+
+ return err
+}
+
+// MarshalJSON encodes the web3 RPC transaction format.
+func (tx *Transaction) MarshalJSON() ([]byte, error) {
+ hash := tx.Hash()
+ data := tx.data
+ data.Hash = &hash
+ return data.MarshalJSON()
+}
+
+// UnmarshalJSON decodes the web3 RPC transaction format.
+func (tx *Transaction) UnmarshalJSON(input []byte) error {
+ var dec txdata
+ if err := dec.UnmarshalJSON(input); err != nil {
+ return err
+ }
+
+ withSignature := dec.V.Sign() != 0 || dec.R.Sign() != 0 || dec.S.Sign() != 0
+ if withSignature {
+ var V byte
+ if isProtectedV(dec.V) {
+ chainID := deriveChainId(dec.V).Uint64()
+ V = byte(dec.V.Uint64() - 35 - 2*chainID)
+ } else {
+ V = byte(dec.V.Uint64() - 27)
+ }
+ if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
+ return ErrInvalidSig
+ }
+ }
+
+ *tx = Transaction{data: dec}
+ return nil
+}
+
+func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
+func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
+func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
+func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
+func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
+func (tx *Transaction) CheckNonce() bool { return true }
+
+// To returns the recipient address of the transaction.
+// It returns nil if the transaction is a contract creation.
+func (tx *Transaction) To() *common.Address {
+ if tx.data.Recipient == nil {
+ return nil
+ }
+ to := *tx.data.Recipient
+ return &to
+}
+
+// Hash hashes the RLP encoding of tx.
+// It uniquely identifies the transaction.
+func (tx *Transaction) Hash() common.Hash {
+ if hash := tx.hash.Load(); hash != nil {
+ return hash.(common.Hash)
+ }
+ v := rlpHash(tx)
+ tx.hash.Store(v)
+ return v
+}
+
+// Size returns the true RLP encoded storage size of the transaction, either by
+// encoding and returning it, or returning a previsouly cached value.
+func (tx *Transaction) Size() common.StorageSize {
+ if size := tx.size.Load(); size != nil {
+ return size.(common.StorageSize)
+ }
+ c := writeCounter(0)
+ rlp.Encode(&c, &tx.data)
+ tx.size.Store(common.StorageSize(c))
+ return common.StorageSize(c)
+}
+
+// AsMessage returns the transaction as a core.Message.
+//
+// AsMessage requires a signer to derive the sender.
+//
+// XXX Rename message to something less arbitrary?
+func (tx *Transaction) AsMessage(s Signer) (Message, error) {
+ msg := Message{
+ nonce: tx.data.AccountNonce,
+ gasLimit: tx.data.GasLimit,
+ gasPrice: new(big.Int).Set(tx.data.Price),
+ to: tx.data.Recipient,
+ amount: tx.data.Amount,
+ data: tx.data.Payload,
+ checkNonce: true,
+ }
+
+ var err error
+ msg.from, err = Sender(s, tx)
+ return msg, err
+}
+
+// WithSignature returns a new transaction with the given signature.
+// This signature needs to be formatted as described in the yellow paper (v+27).
+func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
+ r, s, v, err := signer.SignatureValues(tx, sig)
+ if err != nil {
+ return nil, err
+ }
+ cpy := &Transaction{data: tx.data}
+ cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
+ return cpy, nil
+}
+
+// Cost returns amount + gasprice * gaslimit.
+func (tx *Transaction) Cost() *big.Int {
+ total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit))
+ total.Add(total, tx.data.Amount)
+ return total
+}
+
+func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) {
+ return tx.data.V, tx.data.R, tx.data.S
+}
+
+// Transactions is a Transaction slice type for basic sorting.
+type Transactions []*Transaction
+
+// Len returns the length of s.
+func (s Transactions) Len() int { return len(s) }
+
+// Swap swaps the i'th and the j'th element in s.
+func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// GetRlp implements Rlpable and returns the i'th element of s in rlp.
+func (s Transactions) GetRlp(i int) []byte {
+ enc, _ := rlp.EncodeToBytes(s[i])
+ return enc
+}
+
+// TxDifference returns a new set which is the difference between a and b.
+func TxDifference(a, b Transactions) Transactions {
+ keep := make(Transactions, 0, len(a))
+
+ remove := make(map[common.Hash]struct{})
+ for _, tx := range b {
+ remove[tx.Hash()] = struct{}{}
+ }
+
+ for _, tx := range a {
+ if _, ok := remove[tx.Hash()]; !ok {
+ keep = append(keep, tx)
+ }
+ }
+
+ return keep
+}
+
+// TxByNonce implements the sort interface to allow sorting a list of transactions
+// by their nonces. This is usually only useful for sorting transactions from a
+// single account, otherwise a nonce comparison doesn't make much sense.
+type TxByNonce Transactions
+
+func (s TxByNonce) Len() int { return len(s) }
+func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce }
+func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// TxByPrice implements both the sort and the heap interface, making it useful
+// for all at once sorting as well as individually adding and removing elements.
+type TxByPrice Transactions
+
+func (s TxByPrice) Len() int { return len(s) }
+func (s TxByPrice) Less(i, j int) bool { return s[i].data.Price.Cmp(s[j].data.Price) > 0 }
+func (s TxByPrice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func (s *TxByPrice) Push(x interface{}) {
+ *s = append(*s, x.(*Transaction))
+}
+
+func (s *TxByPrice) Pop() interface{} {
+ old := *s
+ n := len(old)
+ x := old[n-1]
+ *s = old[0 : n-1]
+ return x
+}
+
+// TransactionsByPriceAndNonce represents a set of transactions that can return
+// transactions in a profit-maximizing sorted order, while supporting removing
+// entire batches of transactions for non-executable accounts.
+type TransactionsByPriceAndNonce struct {
+ txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
+ heads TxByPrice // Next transaction for each unique account (price heap)
+ signer Signer // Signer for the set of transactions
+}
+
+// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve
+// price sorted transactions in a nonce-honouring way.
+//
+// Note, the input map is reowned so the caller should not interact any more with
+// if after providing it to the constructor.
+func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
+ // Initialize a price based heap with the head transactions
+ heads := make(TxByPrice, 0, len(txs))
+ for from, accTxs := range txs {
+ heads = append(heads, accTxs[0])
+ // Ensure the sender address is from the signer
+ acc, _ := Sender(signer, accTxs[0])
+ txs[acc] = accTxs[1:]
+ if from != acc {
+ delete(txs, from)
+ }
+ }
+ heap.Init(&heads)
+
+ // Assemble and return the transaction set
+ return &TransactionsByPriceAndNonce{
+ txs: txs,
+ heads: heads,
+ signer: signer,
+ }
+}
+
+// Peek returns the next transaction by price.
+func (t *TransactionsByPriceAndNonce) Peek() *Transaction {
+ if len(t.heads) == 0 {
+ return nil
+ }
+ return t.heads[0]
+}
+
+// Shift replaces the current best head with the next one from the same account.
+func (t *TransactionsByPriceAndNonce) Shift() {
+ acc, _ := Sender(t.signer, t.heads[0])
+ if txs, ok := t.txs[acc]; ok && len(txs) > 0 {
+ t.heads[0], t.txs[acc] = txs[0], txs[1:]
+ heap.Fix(&t.heads, 0)
+ } else {
+ heap.Pop(&t.heads)
+ }
+}
+
+// Pop removes the best transaction, *not* replacing it with the next one from
+// the same account. This should be used when a transaction cannot be executed
+// and hence all subsequent ones should be discarded from the same account.
+func (t *TransactionsByPriceAndNonce) Pop() {
+ heap.Pop(&t.heads)
+}
+
+// Message is a fully derived transaction and implements core.Message
+//
+// NOTE: In a future PR this will be removed.
+type Message struct {
+ to *common.Address
+ from common.Address
+ nonce uint64
+ amount *big.Int
+ gasLimit uint64
+ gasPrice *big.Int
+ data []byte
+ checkNonce bool
+}
+
+func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message {
+ return Message{
+ from: from,
+ to: to,
+ nonce: nonce,
+ amount: amount,
+ gasLimit: gasLimit,
+ gasPrice: gasPrice,
+ data: data,
+ checkNonce: checkNonce,
+ }
+}
+
+func (m Message) From() common.Address { return m.from }
+func (m Message) To() *common.Address { return m.to }
+func (m Message) GasPrice() *big.Int { return m.gasPrice }
+func (m Message) Value() *big.Int { return m.amount }
+func (m Message) Gas() uint64 { return m.gasLimit }
+func (m Message) Nonce() uint64 { return m.nonce }
+func (m Message) Data() []byte { return m.data }
+func (m Message) CheckNonce() bool { return m.checkNonce }
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
new file mode 100644
index 000000000..63132048e
--- /dev/null
+++ b/core/types/transaction_signing.go
@@ -0,0 +1,260 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "crypto/ecdsa"
+ "errors"
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+var (
+ ErrInvalidChainId = errors.New("invalid chain id for signer")
+)
+
+// sigCache is used to cache the derived sender and contains
+// the signer used to derive it.
+type sigCache struct {
+ signer Signer
+ from common.Address
+}
+
+// MakeSigner returns a Signer based on the given chain config and block number.
+func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
+ var signer Signer
+ switch {
+ case config.IsEIP155(blockNumber):
+ signer = NewEIP155Signer(config.ChainID)
+ case config.IsHomestead(blockNumber):
+ signer = HomesteadSigner{}
+ default:
+ signer = FrontierSigner{}
+ }
+ return signer
+}
+
+// SignTx signs the transaction using the given signer and private key
+func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ h := s.Hash(tx)
+ sig, err := crypto.Sign(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(s, sig)
+}
+
+// Sender returns the address derived from the signature (V, R, S) using secp256k1
+// elliptic curve and an error if it failed deriving or upon an incorrect
+// signature.
+//
+// Sender may cache the address, allowing it to be used regardless of
+// signing method. The cache is invalidated if the cached signer does
+// not match the signer used in the current call.
+func Sender(signer Signer, tx *Transaction) (common.Address, error) {
+ if sc := tx.from.Load(); sc != nil {
+ sigCache := sc.(sigCache)
+ // If the signer used to derive from in a previous
+ // call is not the same as used current, invalidate
+ // the cache.
+ if sigCache.signer.Equal(signer) {
+ return sigCache.from, nil
+ }
+ }
+
+ addr, err := signer.Sender(tx)
+ if err != nil {
+ return common.Address{}, err
+ }
+ tx.from.Store(sigCache{signer: signer, from: addr})
+ return addr, nil
+}
+
+// Signer encapsulates transaction signature handling. Note that this interface is not a
+// stable API and may change at any time to accommodate new protocol rules.
+type Signer interface {
+ // Sender returns the sender address of the transaction.
+ Sender(tx *Transaction) (common.Address, error)
+ // SignatureValues returns the raw R, S, V values corresponding to the
+ // given signature.
+ SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error)
+ // Hash returns the hash to be signed.
+ Hash(tx *Transaction) common.Hash
+ // Equal returns true if the given signer is the same as the receiver.
+ Equal(Signer) bool
+}
+
+// EIP155Transaction implements Signer using the EIP155 rules.
+type EIP155Signer struct {
+ chainId, chainIdMul *big.Int
+}
+
+func NewEIP155Signer(chainId *big.Int) EIP155Signer {
+ if chainId == nil {
+ chainId = new(big.Int)
+ }
+ return EIP155Signer{
+ chainId: chainId,
+ chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)),
+ }
+}
+
+func (s EIP155Signer) Equal(s2 Signer) bool {
+ eip155, ok := s2.(EIP155Signer)
+ return ok && eip155.chainId.Cmp(s.chainId) == 0
+}
+
+var big8 = big.NewInt(8)
+
+func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
+ if !tx.Protected() {
+ return HomesteadSigner{}.Sender(tx)
+ }
+ if tx.ChainId().Cmp(s.chainId) != 0 {
+ return common.Address{}, ErrInvalidChainId
+ }
+ V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
+ V.Sub(V, big8)
+ return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
+}
+
+// SignatureValues returns signature values. This signature
+// needs to be in the [R || S || V] format where V is 0 or 1.
+func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
+ R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ if s.chainId.Sign() != 0 {
+ V = big.NewInt(int64(sig[64] + 35))
+ V.Add(V, s.chainIdMul)
+ }
+ return R, S, V, nil
+}
+
+// Hash returns the hash to be signed by the sender.
+// It does not uniquely identify the transaction.
+func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
+ return rlpHash([]interface{}{
+ tx.data.AccountNonce,
+ tx.data.Price,
+ tx.data.GasLimit,
+ tx.data.Recipient,
+ tx.data.Amount,
+ tx.data.Payload,
+ s.chainId, uint(0), uint(0),
+ })
+}
+
+// HomesteadTransaction implements TransactionInterface using the
+// homestead rules.
+type HomesteadSigner struct{ FrontierSigner }
+
+func (s HomesteadSigner) Equal(s2 Signer) bool {
+ _, ok := s2.(HomesteadSigner)
+ return ok
+}
+
+// SignatureValues returns signature values. This signature
+// needs to be in the [R || S || V] format where V is 0 or 1.
+func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
+ return hs.FrontierSigner.SignatureValues(tx, sig)
+}
+
+func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
+ return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true)
+}
+
+type FrontierSigner struct{}
+
+func (s FrontierSigner) Equal(s2 Signer) bool {
+ _, ok := s2.(FrontierSigner)
+ return ok
+}
+
+// SignatureValues returns signature values. This signature
+// needs to be in the [R || S || V] format where V is 0 or 1.
+func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
+ if len(sig) != 65 {
+ panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
+ }
+ r = new(big.Int).SetBytes(sig[:32])
+ s = new(big.Int).SetBytes(sig[32:64])
+ v = new(big.Int).SetBytes([]byte{sig[64] + 27})
+ return r, s, v, nil
+}
+
+// Hash returns the hash to be signed by the sender.
+// It does not uniquely identify the transaction.
+func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
+ return rlpHash([]interface{}{
+ tx.data.AccountNonce,
+ tx.data.Price,
+ tx.data.GasLimit,
+ tx.data.Recipient,
+ tx.data.Amount,
+ tx.data.Payload,
+ })
+}
+
+func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
+ return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false)
+}
+
+func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) {
+ if Vb.BitLen() > 8 {
+ return common.Address{}, ErrInvalidSig
+ }
+ V := byte(Vb.Uint64() - 27)
+ if !crypto.ValidateSignatureValues(V, R, S, homestead) {
+ return common.Address{}, ErrInvalidSig
+ }
+ // encode the signature in uncompressed format
+ r, s := R.Bytes(), S.Bytes()
+ sig := make([]byte, 65)
+ copy(sig[32-len(r):32], r)
+ copy(sig[64-len(s):64], s)
+ sig[64] = V
+ // recover the public key from the signature
+ pub, err := crypto.Ecrecover(sighash[:], sig)
+ if err != nil {
+ return common.Address{}, err
+ }
+ if len(pub) == 0 || pub[0] != 4 {
+ return common.Address{}, errors.New("invalid public key")
+ }
+ var addr common.Address
+ copy(addr[:], crypto.Keccak256(pub[1:])[12:])
+ return addr, nil
+}
+
+// deriveChainId derives the chain id from the given v parameter
+func deriveChainId(v *big.Int) *big.Int {
+ if v.BitLen() <= 64 {
+ v := v.Uint64()
+ if v == 27 || v == 28 {
+ return new(big.Int)
+ }
+ return new(big.Int).SetUint64((v - 35) / 2)
+ }
+ v = new(big.Int).Sub(v, big.NewInt(35))
+ return v.Div(v, big.NewInt(2))
+}
diff --git a/core/types/transaction_signing_test.go b/core/types/transaction_signing_test.go
new file mode 100644
index 000000000..689fc38a9
--- /dev/null
+++ b/core/types/transaction_signing_test.go
@@ -0,0 +1,138 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+func TestEIP155Signing(t *testing.T) {
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+
+ signer := NewEIP155Signer(big.NewInt(18))
+ tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ from, err := Sender(signer, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if from != addr {
+ t.Errorf("exected from and address to be equal. Got %x want %x", from, addr)
+ }
+}
+
+func TestEIP155ChainId(t *testing.T) {
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+
+ signer := NewEIP155Signer(big.NewInt(18))
+ tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !tx.Protected() {
+ t.Fatal("expected tx to be protected")
+ }
+
+ if tx.ChainId().Cmp(signer.chainId) != 0 {
+ t.Error("expected chainId to be", signer.chainId, "got", tx.ChainId())
+ }
+
+ tx = NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil)
+ tx, err = SignTx(tx, HomesteadSigner{}, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if tx.Protected() {
+ t.Error("didn't expect tx to be protected")
+ }
+
+ if tx.ChainId().Sign() != 0 {
+ t.Error("expected chain id to be 0 got", tx.ChainId())
+ }
+}
+
+func TestEIP155SigningVitalik(t *testing.T) {
+ // Test vectors come from http://vitalik.ca/files/eip155_testvec.txt
+ for i, test := range []struct {
+ txRlp, addr string
+ }{
+ {"f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce"},
+ {"f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112"},
+ {"f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be"},
+ {"f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0"},
+ {"f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554"},
+ {"f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4"},
+ {"f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35"},
+ {"f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332"},
+ {"f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029"},
+ {"f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f"},
+ } {
+ signer := NewEIP155Signer(big.NewInt(1))
+
+ var tx *Transaction
+ err := rlp.DecodeBytes(common.Hex2Bytes(test.txRlp), &tx)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+
+ from, err := Sender(signer, tx)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+
+ addr := common.HexToAddress(test.addr)
+ if from != addr {
+ t.Errorf("%d: expected %x got %x", i, addr, from)
+ }
+
+ }
+}
+
+func TestChainId(t *testing.T) {
+ key, _ := defaultTestKey()
+
+ tx := NewTransaction(0, common.Address{}, new(big.Int), 0, new(big.Int), nil)
+
+ var err error
+ tx, err = SignTx(tx, NewEIP155Signer(big.NewInt(1)), key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = Sender(NewEIP155Signer(big.NewInt(2)), tx)
+ if err != ErrInvalidChainId {
+ t.Error("expected error:", ErrInvalidChainId)
+ }
+
+ _, err = Sender(NewEIP155Signer(big.NewInt(1)), tx)
+ if err != nil {
+ t.Error("expected no error")
+ }
+}
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
new file mode 100644
index 000000000..f38d8e717
--- /dev/null
+++ b/core/types/transaction_test.go
@@ -0,0 +1,226 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "crypto/ecdsa"
+ "encoding/json"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// The values in those tests are from the Transaction Tests
+// at github.com/ethereum/tests.
+var (
+ emptyTx = NewTransaction(
+ 0,
+ common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
+ big.NewInt(0), 0, big.NewInt(0),
+ nil,
+ )
+
+ rightvrsTx, _ = NewTransaction(
+ 3,
+ common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
+ big.NewInt(10),
+ 2000,
+ big.NewInt(1),
+ common.FromHex("5544"),
+ ).WithSignature(
+ HomesteadSigner{},
+ common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"),
+ )
+)
+
+func TestTransactionSigHash(t *testing.T) {
+ var homestead HomesteadSigner
+ if homestead.Hash(emptyTx) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
+ t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
+ }
+ if homestead.Hash(rightvrsTx) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
+ t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
+ }
+}
+
+func TestTransactionEncode(t *testing.T) {
+ txb, err := rlp.EncodeToBytes(rightvrsTx)
+ if err != nil {
+ t.Fatalf("encode error: %v", err)
+ }
+ should := common.FromHex("f86103018207d094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3")
+ if !bytes.Equal(txb, should) {
+ t.Errorf("encoded RLP mismatch, got %x", txb)
+ }
+}
+
+func decodeTx(data []byte) (*Transaction, error) {
+ var tx Transaction
+ t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx)
+
+ return t, err
+}
+
+func defaultTestKey() (*ecdsa.PrivateKey, common.Address) {
+ key, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ return key, addr
+}
+
+func TestRecipientEmpty(t *testing.T) {
+ _, addr := defaultTestKey()
+ tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d"))
+ if err != nil {
+ t.Error(err)
+ t.FailNow()
+ }
+
+ from, err := Sender(HomesteadSigner{}, tx)
+ if err != nil {
+ t.Error(err)
+ t.FailNow()
+ }
+ if addr != from {
+ t.Error("derived address doesn't match")
+ }
+}
+
+func TestRecipientNormal(t *testing.T) {
+ _, addr := defaultTestKey()
+
+ tx, err := decodeTx(common.Hex2Bytes("f85d80808094000000000000000000000000000000000000000080011ca0527c0d8f5c63f7b9f41324a7c8a563ee1190bcbf0dac8ab446291bdbf32f5c79a0552c4ef0a09a04395074dab9ed34d3fbfb843c2f2546cc30fe89ec143ca94ca6"))
+ if err != nil {
+ t.Error(err)
+ t.FailNow()
+ }
+
+ from, err := Sender(HomesteadSigner{}, tx)
+ if err != nil {
+ t.Error(err)
+ t.FailNow()
+ }
+
+ if addr != from {
+ t.Error("derived address doesn't match")
+ }
+}
+
+// Tests that transactions can be correctly sorted according to their price in
+// decreasing order, but at the same time with increasing nonces when issued by
+// the same account.
+func TestTransactionPriceNonceSort(t *testing.T) {
+ // Generate a batch of accounts to start with
+ keys := make([]*ecdsa.PrivateKey, 25)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ }
+
+ signer := HomesteadSigner{}
+ // Generate a batch of transactions with overlapping values, but shifted nonces
+ groups := map[common.Address]Transactions{}
+ for start, key := range keys {
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ for i := 0; i < 25; i++ {
+ tx, _ := SignTx(NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), 100, big.NewInt(int64(start+i)), nil), signer, key)
+ groups[addr] = append(groups[addr], tx)
+ }
+ }
+ // Sort the transactions and cross check the nonce ordering
+ txset := NewTransactionsByPriceAndNonce(signer, groups)
+
+ txs := Transactions{}
+ for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
+ txs = append(txs, tx)
+ txset.Shift()
+ }
+ if len(txs) != 25*25 {
+ t.Errorf("expected %d transactions, found %d", 25*25, len(txs))
+ }
+ for i, txi := range txs {
+ fromi, _ := Sender(signer, txi)
+
+ // Make sure the nonce order is valid
+ for j, txj := range txs[i+1:] {
+ fromj, _ := Sender(signer, txj)
+
+ if fromi == fromj && txi.Nonce() > txj.Nonce() {
+ t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce())
+ }
+ }
+
+ // If the next tx has different from account, the price must be lower than the current one
+ if i+1 < len(txs) {
+ next := txs[i+1]
+ fromNext, _ := Sender(signer, next)
+ if fromi != fromNext && txi.GasPrice().Cmp(next.GasPrice()) < 0 {
+ t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice())
+ }
+ }
+ }
+}
+
+// TestTransactionJSON tests serializing/de-serializing to/from JSON.
+func TestTransactionJSON(t *testing.T) {
+ key, err := crypto.GenerateKey()
+ if err != nil {
+ t.Fatalf("could not generate key: %v", err)
+ }
+ signer := NewEIP155Signer(common.Big1)
+
+ transactions := make([]*Transaction, 0, 50)
+ for i := uint64(0); i < 25; i++ {
+ var tx *Transaction
+ switch i % 2 {
+ case 0:
+ tx = NewTransaction(i, common.Address{1}, common.Big0, 1, common.Big2, []byte("abcdef"))
+ case 1:
+ tx = NewContractCreation(i, common.Big0, 1, common.Big2, []byte("abcdef"))
+ }
+ transactions = append(transactions, tx)
+
+ signedTx, err := SignTx(tx, signer, key)
+ if err != nil {
+ t.Fatalf("could not sign transaction: %v", err)
+ }
+
+ transactions = append(transactions, signedTx)
+ }
+
+ for _, tx := range transactions {
+ data, err := json.Marshal(tx)
+ if err != nil {
+ t.Fatalf("json.Marshal failed: %v", err)
+ }
+
+ var parsedTx *Transaction
+ if err := json.Unmarshal(data, &parsedTx); err != nil {
+ t.Fatalf("json.Unmarshal failed: %v", err)
+ }
+
+ // compare nonce, price, gaslimit, recipient, amount, payload, V, R, S
+ if tx.Hash() != parsedTx.Hash() {
+ t.Errorf("parsed tx differs from original tx, want %v, got %v", tx, parsedTx)
+ }
+ if tx.ChainId().Cmp(parsedTx.ChainId()) != 0 {
+ t.Errorf("invalid chain id, want %d, got %d", tx.ChainId(), parsedTx.ChainId())
+ }
+ }
+}
diff --git a/harmony/main.go b/harmony/main.go
new file mode 100644
index 000000000..9bde4815e
--- /dev/null
+++ b/harmony/main.go
@@ -0,0 +1,56 @@
+package harmony
+
+import (
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/core"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+ "math/big"
+)
+
+var (
+
+ // Test accounts
+ testBankKey, _ = crypto.GenerateKey()
+ testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
+ testBankFunds = big.NewInt(1000000000000000000)
+
+ testUserKey, _ = crypto.GenerateKey()
+ testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
+
+ chainConfig = params.TestChainConfig
+
+ // Test transactions
+ pendingTxs []*types.Transaction
+ newTxs []*types.Transaction
+)
+
+type testWorkerBackend struct {
+ db ethdb.Database
+ txPool *core.TxPool
+ chain *core.BlockChain
+}
+
+func main() {
+
+ var (
+ database = ethdb.NewMemDatabase()
+ gspec = core.Genesis{
+ Config: chainConfig,
+ Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
+ }
+ )
+
+ chain, _ := core.NewBlockChain(database, nil, gspec.Config, nil, vm.Config{}, nil)
+
+ txpool := core.NewTxPool(core.DefaultTxPoolConfig, chainConfig, chain)
+
+ backend := &testWorkerBackend{
+ db: database,
+ chain: chain,
+ txPool: txpool,
+ }
+ backend.txPool.AddLocals(pendingTxs)
+}