forked from cerc-io/ipld-eth-server
Allow Parity as ingest node (#36)
* Upgrade go-ethereum to v1.8 * Add Node Info for parity nodes * Upgrade start_private_blockchain to use v1.8
This commit is contained in:
+1
-4
@@ -114,10 +114,7 @@ func PrintDisassembled(code string) error {
|
||||
fmt.Printf("%06v: %v\n", it.PC(), it.Op())
|
||||
}
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return it.Error()
|
||||
}
|
||||
|
||||
// Return all disassembled EVM instructions in human-readable format.
|
||||
|
||||
+1
-4
@@ -237,10 +237,7 @@ func (c *Compiler) pushBin(v interface{}) {
|
||||
// isPush returns whether the string op is either any of
|
||||
// push(N).
|
||||
func isPush(op string) bool {
|
||||
if op == "push" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return op == "push"
|
||||
}
|
||||
|
||||
// isJump returns whether the string op is jump(i)
|
||||
|
||||
+2
-2
@@ -206,7 +206,7 @@ func lexLine(l *lexer) stateFn {
|
||||
return lexComment
|
||||
case isSpace(r):
|
||||
l.ignore()
|
||||
case isAlphaNumeric(r) || r == '_':
|
||||
case isLetter(r) || r == '_':
|
||||
return lexElement
|
||||
case isNumber(r):
|
||||
return lexNumber
|
||||
@@ -278,7 +278,7 @@ func lexElement(l *lexer) stateFn {
|
||||
return lexLine
|
||||
}
|
||||
|
||||
func isAlphaNumeric(t rune) bool {
|
||||
func isLetter(t rune) bool {
|
||||
return unicode.IsLetter(t)
|
||||
}
|
||||
|
||||
|
||||
+7
-8
@@ -84,7 +84,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||
return func(i int, gen *BlockGen) {
|
||||
toaddr := common.Address{}
|
||||
data := make([]byte, nbytes)
|
||||
gas := IntrinsicGas(data, false, false)
|
||||
gas, _ := IntrinsicGas(data, false, false)
|
||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
|
||||
gen.AddTx(tx)
|
||||
}
|
||||
@@ -93,7 +93,6 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||
var (
|
||||
ringKeys = make([]*ecdsa.PrivateKey, 1000)
|
||||
ringAddrs = make([]common.Address, len(ringKeys))
|
||||
bigTxGas = new(big.Int).SetUint64(params.TxGas)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -113,8 +112,8 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||
return func(i int, gen *BlockGen) {
|
||||
gas := CalcGasLimit(gen.PrevBlock(i - 1))
|
||||
for {
|
||||
gas.Sub(gas, bigTxGas)
|
||||
if gas.Cmp(bigTxGas) < 0 {
|
||||
gas -= params.TxGas
|
||||
if gas < params.TxGas {
|
||||
break
|
||||
}
|
||||
to := (from + 1) % naccounts
|
||||
@@ -122,7 +121,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||
gen.TxNonce(ringAddrs[from]),
|
||||
ringAddrs[to],
|
||||
benchRootFunds,
|
||||
bigTxGas,
|
||||
params.TxGas,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
@@ -170,11 +169,11 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||
Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
|
||||
}
|
||||
genesis := gspec.MustCommit(db)
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, db, b.N, gen)
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, b.N, gen)
|
||||
|
||||
// Time the insertion of the new chain.
|
||||
// State and blocks are stored in the same DB.
|
||||
chainman, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer chainman.Stop()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
@@ -284,7 +283,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||
if err != nil {
|
||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||
}
|
||||
chain, err := NewBlockChain(db, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
|
||||
chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
|
||||
if err != nil {
|
||||
b.Fatalf("error creating chain: %v", err)
|
||||
}
|
||||
|
||||
+22
-23
@@ -18,9 +18,7 @@ package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@@ -52,11 +50,14 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
|
||||
// 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()) {
|
||||
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
|
||||
return ErrKnownBlock
|
||||
}
|
||||
if !v.bc.HasBlockAndState(block.ParentHash()) {
|
||||
return consensus.ErrUnknownAncestor
|
||||
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()
|
||||
@@ -76,10 +77,10 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||
// 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 *big.Int) error {
|
||||
func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
|
||||
header := block.Header()
|
||||
if block.GasUsed().Cmp(usedGas) != 0 {
|
||||
return fmt.Errorf("invalid gas used (remote: %v local: %v)", block.GasUsed(), usedGas)
|
||||
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.
|
||||
@@ -101,17 +102,13 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
|
||||
}
|
||||
|
||||
// CalcGasLimit computes the gas limit of the next block after parent.
|
||||
// The result may be modified by the caller.
|
||||
// This is miner strategy, not consensus protocol.
|
||||
func CalcGasLimit(parent *types.Block) *big.Int {
|
||||
func CalcGasLimit(parent *types.Block) uint64 {
|
||||
// contrib = (parentGasUsed * 3 / 2) / 1024
|
||||
contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
|
||||
contrib = contrib.Div(contrib, big.NewInt(2))
|
||||
contrib = contrib.Div(contrib, params.GasLimitBoundDivisor)
|
||||
contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor
|
||||
|
||||
// decay = parentGasLimit / 1024 -1
|
||||
decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
|
||||
decay.Sub(decay, big.NewInt(1))
|
||||
decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1
|
||||
|
||||
/*
|
||||
strategy: gasLimit of block-to-mine is set based on parent's
|
||||
@@ -120,15 +117,17 @@ func CalcGasLimit(parent *types.Block) *big.Int {
|
||||
at that usage) the amount increased/decreased depends on how far away
|
||||
from parentGasLimit * (2/3) parentGasUsed is.
|
||||
*/
|
||||
gl := new(big.Int).Sub(parent.GasLimit(), decay)
|
||||
gl = gl.Add(gl, contrib)
|
||||
gl.Set(math.BigMax(gl, params.MinGasLimit))
|
||||
|
||||
limit := parent.GasLimit() - decay + contrib
|
||||
if limit < params.MinGasLimit {
|
||||
limit = params.MinGasLimit
|
||||
}
|
||||
// however, if we're now below the target (TargetGasLimit) we increase the
|
||||
// limit as much as we can (parentGasLimit / 1024 -1)
|
||||
if gl.Cmp(params.TargetGasLimit) < 0 {
|
||||
gl.Add(parent.GasLimit(), decay)
|
||||
gl.Set(math.BigMin(gl, params.TargetGasLimit))
|
||||
if limit < params.TargetGasLimit {
|
||||
limit = parent.GasLimit() + decay
|
||||
if limit > params.TargetGasLimit {
|
||||
limit = params.TargetGasLimit
|
||||
}
|
||||
}
|
||||
return gl
|
||||
return limit
|
||||
}
|
||||
|
||||
+7
-7
@@ -35,14 +35,14 @@ func TestHeaderVerification(t *testing.T) {
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
}
|
||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
|
||||
defer chain.Stop()
|
||||
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
@@ -87,7 +87,7 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
seals := make([]bool, len(blocks))
|
||||
@@ -106,11 +106,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||
var results <-chan error
|
||||
|
||||
if valid {
|
||||
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
chain.Stop()
|
||||
} else {
|
||||
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{})
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{})
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
chain.Stop()
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
seals := make([]bool, len(blocks))
|
||||
@@ -173,7 +173,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
// Start the verifications and immediately abort
|
||||
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{})
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{})
|
||||
defer chain.Stop()
|
||||
|
||||
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
|
||||
+325
-153
@@ -42,10 +42,11 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||
)
|
||||
|
||||
var (
|
||||
blockInsertTimer = metrics.NewTimer("chain/inserts")
|
||||
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
|
||||
|
||||
ErrNoGenesis = errors.New("Genesis not found in chain")
|
||||
)
|
||||
@@ -56,11 +57,20 @@ const (
|
||||
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.
|
||||
//
|
||||
@@ -76,10 +86,14 @@ const (
|
||||
// included in the canonical one where as GetBlockByNumber always represents the
|
||||
// canonical chain.
|
||||
type BlockChain struct {
|
||||
config *params.ChainConfig // chain & network configuration
|
||||
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
|
||||
chainDb ethdb.Database
|
||||
rmLogsFeed event.Feed
|
||||
chainFeed event.Feed
|
||||
chainSideFeed event.Feed
|
||||
@@ -93,8 +107,8 @@ type BlockChain struct {
|
||||
procmu sync.RWMutex // block processor lock
|
||||
|
||||
checkpoint int // checkpoint counts towards the new checkpoint
|
||||
currentBlock *types.Block // Current head of the block chain
|
||||
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)
|
||||
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
|
||||
@@ -119,7 +133,13 @@ type BlockChain struct {
|
||||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialises the default Ethereum Validator and
|
||||
// Processor.
|
||||
func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
|
||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
|
||||
if cacheConfig == nil {
|
||||
cacheConfig = &CacheConfig{
|
||||
TrieNodeLimit: 256 * 1024 * 1024,
|
||||
TrieTimeLimit: 5 * time.Minute,
|
||||
}
|
||||
}
|
||||
bodyCache, _ := lru.New(bodyCacheLimit)
|
||||
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
||||
blockCache, _ := lru.New(blockCacheLimit)
|
||||
@@ -127,9 +147,11 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
|
||||
badBlocks, _ := lru.New(badBlockLimit)
|
||||
|
||||
bc := &BlockChain{
|
||||
config: config,
|
||||
chainDb: chainDb,
|
||||
stateCache: state.NewDatabase(chainDb),
|
||||
chainConfig: chainConfig,
|
||||
cacheConfig: cacheConfig,
|
||||
db: db,
|
||||
triegc: prque.New(),
|
||||
stateCache: state.NewDatabase(db),
|
||||
quit: make(chan struct{}),
|
||||
bodyCache: bodyCache,
|
||||
bodyRLPCache: bodyRLPCache,
|
||||
@@ -139,11 +161,11 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
|
||||
vmConfig: vmConfig,
|
||||
badBlocks: badBlocks,
|
||||
}
|
||||
bc.SetValidator(NewBlockValidator(config, bc, engine))
|
||||
bc.SetProcessor(NewStateProcessor(config, bc, engine))
|
||||
bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
|
||||
bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
|
||||
|
||||
var err error
|
||||
bc.hc, err = NewHeaderChain(chainDb, config, engine, bc.getProcInterrupt)
|
||||
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.getProcInterrupt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -180,7 +202,7 @@ func (bc *BlockChain) getProcInterrupt() bool {
|
||||
// assumes that the chain manager mutex is held.
|
||||
func (bc *BlockChain) loadLastState() error {
|
||||
// Restore the last known head block
|
||||
head := GetHeadBlockHash(bc.chainDb)
|
||||
head := GetHeadBlockHash(bc.db)
|
||||
if head == (common.Hash{}) {
|
||||
// Corrupt or empty database, init from scratch
|
||||
log.Warn("Empty database, resetting chain")
|
||||
@@ -196,15 +218,17 @@ func (bc *BlockChain) loadLastState() error {
|
||||
// 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, resetting chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
|
||||
return bc.Reset()
|
||||
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 = currentBlock
|
||||
bc.currentBlock.Store(currentBlock)
|
||||
|
||||
// Restore the last known head header
|
||||
currentHeader := bc.currentBlock.Header()
|
||||
if head := GetHeadHeaderHash(bc.chainDb); head != (common.Hash{}) {
|
||||
currentHeader := currentBlock.Header()
|
||||
if head := GetHeadHeaderHash(bc.db); head != (common.Hash{}) {
|
||||
if header := bc.GetHeaderByHash(head); header != nil {
|
||||
currentHeader = header
|
||||
}
|
||||
@@ -212,21 +236,23 @@ func (bc *BlockChain) loadLastState() error {
|
||||
bc.hc.SetCurrentHeader(currentHeader)
|
||||
|
||||
// Restore the last known head fast block
|
||||
bc.currentFastBlock = bc.currentBlock
|
||||
if head := GetHeadFastBlockHash(bc.chainDb); head != (common.Hash{}) {
|
||||
bc.currentFastBlock.Store(currentBlock)
|
||||
if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) {
|
||||
if block := bc.GetBlockByHash(head); block != nil {
|
||||
bc.currentFastBlock = block
|
||||
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(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
|
||||
fastTd := bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64())
|
||||
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)
|
||||
log.Info("Loaded most recent local full block", "number", bc.currentBlock.Number(), "hash", bc.currentBlock.Hash(), "td", blockTd)
|
||||
log.Info("Loaded most recent local fast block", "number", bc.currentFastBlock.Number(), "hash", bc.currentFastBlock.Hash(), "td", fastTd)
|
||||
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd)
|
||||
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -243,7 +269,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
||||
|
||||
// Rewind the header chain, deleting all block bodies until then
|
||||
delFn := func(hash common.Hash, num uint64) {
|
||||
DeleteBody(bc.chainDb, hash, num)
|
||||
DeleteBody(bc.db, hash, num)
|
||||
}
|
||||
bc.hc.SetHead(head, delFn)
|
||||
currentHeader := bc.hc.CurrentHeader()
|
||||
@@ -255,30 +281,32 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
||||
bc.futureBlocks.Purge()
|
||||
|
||||
// Rewind the block chain, ensuring we don't end up with a stateless head block
|
||||
if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() {
|
||||
bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
||||
if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() {
|
||||
bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
|
||||
}
|
||||
if bc.currentBlock != nil {
|
||||
if _, err := state.New(bc.currentBlock.Root(), bc.stateCache); err != nil {
|
||||
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 = nil
|
||||
bc.currentBlock.Store(bc.genesisBlock)
|
||||
}
|
||||
}
|
||||
// Rewind the fast block in a simpleton way to the target head
|
||||
if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() {
|
||||
bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
||||
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 bc.currentBlock == nil {
|
||||
bc.currentBlock = bc.genesisBlock
|
||||
if currentBlock := bc.CurrentBlock(); currentBlock == nil {
|
||||
bc.currentBlock.Store(bc.genesisBlock)
|
||||
}
|
||||
if bc.currentFastBlock == nil {
|
||||
bc.currentFastBlock = bc.genesisBlock
|
||||
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil {
|
||||
bc.currentFastBlock.Store(bc.genesisBlock)
|
||||
}
|
||||
if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil {
|
||||
currentBlock := bc.CurrentBlock()
|
||||
currentFastBlock := bc.CurrentFastBlock()
|
||||
if err := WriteHeadBlockHash(bc.db, currentBlock.Hash()); err != nil {
|
||||
log.Crit("Failed to reset head full block", "err", err)
|
||||
}
|
||||
if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil {
|
||||
if err := WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash()); err != nil {
|
||||
log.Crit("Failed to reset head fast block", "err", err)
|
||||
}
|
||||
return bc.loadLastState()
|
||||
@@ -292,12 +320,12 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
||||
if block == nil {
|
||||
return fmt.Errorf("non existent block [%x…]", hash[:4])
|
||||
}
|
||||
if _, err := trie.NewSecure(block.Root(), bc.chainDb, 0); err != nil {
|
||||
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 = block
|
||||
bc.currentBlock.Store(block)
|
||||
bc.mu.Unlock()
|
||||
|
||||
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
||||
@@ -305,46 +333,20 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
||||
}
|
||||
|
||||
// GasLimit returns the gas limit of the current HEAD block.
|
||||
func (bc *BlockChain) GasLimit() *big.Int {
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
return bc.currentBlock.GasLimit()
|
||||
}
|
||||
|
||||
// LastBlockHash return the hash of the HEAD block.
|
||||
func (bc *BlockChain) LastBlockHash() common.Hash {
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
return bc.currentBlock.Hash()
|
||||
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 {
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
return bc.currentBlock
|
||||
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 {
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
return bc.currentFastBlock
|
||||
}
|
||||
|
||||
// Status returns status information about the current chain such as the HEAD Td,
|
||||
// the HEAD hash and the hash of the genesis block.
|
||||
func (bc *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
return bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64()), bc.currentBlock.Hash(), bc.genesisBlock.Hash()
|
||||
return bc.currentFastBlock.Load().(*types.Block)
|
||||
}
|
||||
|
||||
// SetProcessor sets the processor required for making state modifications.
|
||||
@@ -404,22 +406,40 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
||||
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
|
||||
log.Crit("Failed to write genesis block TD", "err", err)
|
||||
}
|
||||
if err := WriteBlock(bc.chainDb, genesis); err != nil {
|
||||
if err := WriteBlock(bc.db, genesis); err != nil {
|
||||
log.Crit("Failed to write genesis block", "err", err)
|
||||
}
|
||||
bc.genesisBlock = genesis
|
||||
bc.insert(bc.genesisBlock)
|
||||
bc.currentBlock = bc.genesisBlock
|
||||
bc.currentBlock.Store(bc.genesisBlock)
|
||||
bc.hc.SetGenesis(bc.genesisBlock.Header())
|
||||
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
|
||||
bc.currentFastBlock = bc.genesisBlock
|
||||
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())
|
||||
return bc.ExportN(w, uint64(0), bc.CurrentBlock().NumberU64())
|
||||
}
|
||||
|
||||
// ExportN writes a subset of the active chain to the given writer.
|
||||
@@ -454,25 +474,25 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||
// 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 := GetCanonicalHash(bc.chainDb, block.NumberU64()) != block.Hash()
|
||||
updateHeads := GetCanonicalHash(bc.db, block.NumberU64()) != block.Hash()
|
||||
|
||||
// Add the block to the canonical chain number scheme and mark as the head
|
||||
if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil {
|
||||
if err := WriteCanonicalHash(bc.db, block.Hash(), block.NumberU64()); err != nil {
|
||||
log.Crit("Failed to insert block number", "err", err)
|
||||
}
|
||||
if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil {
|
||||
if err := WriteHeadBlockHash(bc.db, block.Hash()); err != nil {
|
||||
log.Crit("Failed to insert head block hash", "err", err)
|
||||
}
|
||||
bc.currentBlock = block
|
||||
bc.currentBlock.Store(block)
|
||||
|
||||
// If the block is better than out head or is on a different chain, force update heads
|
||||
// If the block is better than our head or is on a different chain, force update heads
|
||||
if updateHeads {
|
||||
bc.hc.SetCurrentHeader(block.Header())
|
||||
|
||||
if err := WriteHeadFastBlockHash(bc.chainDb, block.Hash()); err != nil {
|
||||
if err := WriteHeadFastBlockHash(bc.db, block.Hash()); err != nil {
|
||||
log.Crit("Failed to insert head fast block hash", "err", err)
|
||||
}
|
||||
bc.currentFastBlock = block
|
||||
bc.currentFastBlock.Store(block)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +509,7 @@ func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
|
||||
body := cached.(*types.Body)
|
||||
return body
|
||||
}
|
||||
body := GetBody(bc.chainDb, hash, bc.hc.GetBlockNumber(hash))
|
||||
body := GetBody(bc.db, hash, bc.hc.GetBlockNumber(hash))
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -505,7 +525,7 @@ func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
|
||||
if cached, ok := bc.bodyRLPCache.Get(hash); ok {
|
||||
return cached.(rlp.RawValue)
|
||||
}
|
||||
body := GetBodyRLP(bc.chainDb, hash, bc.hc.GetBlockNumber(hash))
|
||||
body := GetBodyRLP(bc.db, hash, bc.hc.GetBlockNumber(hash))
|
||||
if len(body) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -519,21 +539,25 @@ func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
|
||||
if bc.blockCache.Contains(hash) {
|
||||
return true
|
||||
}
|
||||
ok, _ := bc.chainDb.Has(blockBodyKey(hash, number))
|
||||
ok, _ := bc.db.Has(blockBodyKey(hash, number))
|
||||
return ok
|
||||
}
|
||||
|
||||
// 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) bool {
|
||||
func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
|
||||
// Check first that the block itself is known
|
||||
block := bc.GetBlockByHash(hash)
|
||||
block := bc.GetBlock(hash, number)
|
||||
if block == nil {
|
||||
return false
|
||||
}
|
||||
// Ensure the associated state is also present
|
||||
_, err := bc.stateCache.OpenTrie(block.Root())
|
||||
return err == nil
|
||||
return bc.HasState(block.Root())
|
||||
}
|
||||
|
||||
// GetBlock retrieves a block from the database by hash and number,
|
||||
@@ -543,7 +567,7 @@ func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
|
||||
if block, ok := bc.blockCache.Get(hash); ok {
|
||||
return block.(*types.Block)
|
||||
}
|
||||
block := GetBlock(bc.chainDb, hash, number)
|
||||
block := GetBlock(bc.db, hash, number)
|
||||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -560,13 +584,18 @@ func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
|
||||
// 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 := GetCanonicalHash(bc.chainDb, number)
|
||||
hash := GetCanonicalHash(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 {
|
||||
return GetBlockReceipts(bc.db, hash, GetBlockNumber(bc.db, hash))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -594,6 +623,12 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.
|
||||
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() {
|
||||
@@ -606,6 +641,32 @@ func (bc *BlockChain) Stop() {
|
||||
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), common.Hash{})
|
||||
}
|
||||
if size := triedb.Size(); size != 0 {
|
||||
log.Error("Dangling trie nodes after full cleanup")
|
||||
}
|
||||
}
|
||||
log.Info("Blockchain manager stopped")
|
||||
}
|
||||
|
||||
@@ -648,13 +709,15 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
|
||||
if currentHeader.Hash() == hash {
|
||||
bc.hc.SetCurrentHeader(bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1))
|
||||
}
|
||||
if bc.currentFastBlock.Hash() == hash {
|
||||
bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), bc.currentFastBlock.NumberU64()-1)
|
||||
WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash())
|
||||
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash {
|
||||
newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1)
|
||||
bc.currentFastBlock.Store(newFastBlock)
|
||||
WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
|
||||
}
|
||||
if bc.currentBlock.Hash() == hash {
|
||||
bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), bc.currentBlock.NumberU64()-1)
|
||||
WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash())
|
||||
if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash {
|
||||
newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1)
|
||||
bc.currentBlock.Store(newBlock)
|
||||
WriteHeadBlockHash(bc.db, newBlock.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -677,9 +740,9 @@ func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts ty
|
||||
}
|
||||
// The used gas can be calculated based on previous receipts
|
||||
if j == 0 {
|
||||
receipts[j].GasUsed = new(big.Int).Set(receipts[j].CumulativeGasUsed)
|
||||
receipts[j].GasUsed = receipts[j].CumulativeGasUsed
|
||||
} else {
|
||||
receipts[j].GasUsed = new(big.Int).Sub(receipts[j].CumulativeGasUsed, receipts[j-1].CumulativeGasUsed)
|
||||
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++ {
|
||||
@@ -713,7 +776,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
stats = struct{ processed, ignored int32 }{}
|
||||
start = time.Now()
|
||||
bytes = 0
|
||||
batch = bc.chainDb.NewBatch()
|
||||
batch = bc.db.NewBatch()
|
||||
)
|
||||
for i, block := range blockChain {
|
||||
receipts := receiptChain[i]
|
||||
@@ -731,7 +794,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
continue
|
||||
}
|
||||
// Compute all the non-consensus fields of the receipts
|
||||
SetReceiptsData(bc.config, block, receipts)
|
||||
SetReceiptsData(bc.chainConfig, block, receipts)
|
||||
// Write all the data out into the database
|
||||
if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil {
|
||||
return i, fmt.Errorf("failed to write block body: %v", err)
|
||||
@@ -749,7 +812,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
return 0, err
|
||||
}
|
||||
bytes += batch.ValueSize()
|
||||
batch = bc.chainDb.NewBatch()
|
||||
batch.Reset()
|
||||
}
|
||||
}
|
||||
if batch.ValueSize() > 0 {
|
||||
@@ -763,11 +826,12 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
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
|
||||
if bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()).Cmp(td) < 0 {
|
||||
if err := WriteHeadFastBlockHash(bc.chainDb, head.Hash()); err != nil {
|
||||
currentFastBlock := bc.CurrentFastBlock()
|
||||
if bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()).Cmp(td) < 0 {
|
||||
if err := WriteHeadFastBlockHash(bc.db, head.Hash()); err != nil {
|
||||
log.Crit("Failed to update head fast block hash", "err", err)
|
||||
}
|
||||
bc.currentFastBlock = head
|
||||
bc.currentFastBlock.Store(head)
|
||||
}
|
||||
}
|
||||
bc.mu.Unlock()
|
||||
@@ -775,15 +839,33 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
log.Info("Imported new block receipts",
|
||||
"count", stats.processed,
|
||||
"elapsed", common.PrettyDuration(time.Since(start)),
|
||||
"bytes", bytes,
|
||||
"number", head.Number(),
|
||||
"hash", head.Hash(),
|
||||
"size", common.StorageSize(bytes),
|
||||
"ignored", stats.ignored)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// WriteBlock writes the block to the chain.
|
||||
func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
|
||||
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
|
||||
}
|
||||
if err := WriteBlock(bc.db, block); err != nil {
|
||||
return err
|
||||
}
|
||||
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()
|
||||
|
||||
@@ -796,7 +878,8 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
|
||||
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
|
||||
@@ -804,29 +887,82 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
|
||||
return NonStatTy, err
|
||||
}
|
||||
// Write other block data using a batch.
|
||||
batch := bc.chainDb.NewBatch()
|
||||
batch := bc.db.NewBatch()
|
||||
if err := WriteBlock(batch, block); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
if _, err := state.CommitTo(batch, bc.config.IsEIP158(block.Number())); err != nil {
|
||||
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, -float32(block.NumberU64()))
|
||||
|
||||
if current := block.NumberU64(); current > triesInMemory {
|
||||
// Find the next state trie we need to commit
|
||||
header := bc.GetHeaderByNumber(current - triesInMemory)
|
||||
chosen := header.Number.Uint64()
|
||||
|
||||
// Only write to disk if we exceeded our memory allowance *and* also have at
|
||||
// least a given number of tries gapped.
|
||||
var (
|
||||
size = triedb.Size()
|
||||
limit = common.StorageSize(bc.cacheConfig.TrieNodeLimit) * 1024 * 1024
|
||||
)
|
||||
if size > limit || 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 {
|
||||
switch {
|
||||
case size >= 2*limit:
|
||||
log.Warn("State memory usage too high, committing", "size", size, "limit", limit, "optimum", float64(chosen-lastWrite)/triesInMemory)
|
||||
case 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)
|
||||
}
|
||||
}
|
||||
// If optimum or critical limits reached, write to disk
|
||||
if chosen >= lastWrite+triesInMemory || size >= 2*limit || bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
|
||||
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), common.Hash{})
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
|
||||
// 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 at random
|
||||
reorg = block.NumberU64() < bc.currentBlock.NumberU64() || (block.NumberU64() == bc.currentBlock.NumberU64() && mrand.Float64() < 0.5)
|
||||
reorg = block.NumberU64() < currentBlock.NumberU64() || (block.NumberU64() == currentBlock.NumberU64() && mrand.Float64() < 0.5)
|
||||
}
|
||||
if reorg {
|
||||
// Reorganise the chain if the parent is not the head block
|
||||
if block.ParentHash() != bc.currentBlock.Hash() {
|
||||
if err := bc.reorg(bc.currentBlock, block); err != nil {
|
||||
if block.ParentHash() != currentBlock.Hash() {
|
||||
if err := bc.reorg(currentBlock, block); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
}
|
||||
@@ -835,7 +971,7 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
|
||||
return NonStatTy, err
|
||||
}
|
||||
// Write hash preimages
|
||||
if err := WritePreimages(bc.chainDb, block.NumberU64(), state.Preimages()); err != nil {
|
||||
if err := WritePreimages(bc.db, block.NumberU64(), state.Preimages()); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
status = CanonStatTy
|
||||
@@ -927,31 +1063,65 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
|
||||
if err == nil {
|
||||
err = bc.Validator().ValidateBody(block)
|
||||
}
|
||||
if err != nil {
|
||||
if err == ErrKnownBlock {
|
||||
switch {
|
||||
case err == ErrKnownBlock:
|
||||
// Block and state both already known. However if the current block is below
|
||||
// this number we did a rollback and we should reimport it nonetheless.
|
||||
if bc.CurrentBlock().NumberU64() >= block.NumberU64() {
|
||||
stats.ignored++
|
||||
continue
|
||||
}
|
||||
|
||||
if err == consensus.ErrFutureBlock {
|
||||
// Allow up to MaxFuture second in the future blocks. If this limit
|
||||
// is exceeded the chain is discarded and processed at a later time
|
||||
// if given.
|
||||
max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
|
||||
if block.Time().Cmp(max) > 0 {
|
||||
return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max)
|
||||
case err == consensus.ErrFutureBlock:
|
||||
// Allow up to MaxFuture second in the future blocks. If this limit is exceeded
|
||||
// the chain is discarded and processed at a later time if given.
|
||||
max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
|
||||
if block.Time().Cmp(max) > 0 {
|
||||
return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max)
|
||||
}
|
||||
bc.futureBlocks.Add(block.Hash(), block)
|
||||
stats.queued++
|
||||
continue
|
||||
|
||||
case err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash()):
|
||||
bc.futureBlocks.Add(block.Hash(), block)
|
||||
stats.queued++
|
||||
continue
|
||||
|
||||
case err == consensus.ErrPrunedAncestor:
|
||||
// Block competing with the canonical chain, store in the db, but don't process
|
||||
// until the competitor TD goes above the canonical TD
|
||||
currentBlock := bc.CurrentBlock()
|
||||
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
|
||||
externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty())
|
||||
if localTd.Cmp(externTd) > 0 {
|
||||
if err = bc.WriteBlockWithoutState(block, externTd); err != nil {
|
||||
return i, events, coalescedLogs, err
|
||||
}
|
||||
bc.futureBlocks.Add(block.Hash(), block)
|
||||
stats.queued++
|
||||
continue
|
||||
}
|
||||
// Competitor chain beat canonical, gather all blocks from the common ancestor
|
||||
var winner []*types.Block
|
||||
|
||||
if err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash()) {
|
||||
bc.futureBlocks.Add(block.Hash(), block)
|
||||
stats.queued++
|
||||
continue
|
||||
parent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
for !bc.HasState(parent.Root()) {
|
||||
winner = append(winner, parent)
|
||||
parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1)
|
||||
}
|
||||
for j := 0; j < len(winner)/2; j++ {
|
||||
winner[j], winner[len(winner)-1-j] = winner[len(winner)-1-j], winner[j]
|
||||
}
|
||||
// Import all the pruned blocks to make the state available
|
||||
bc.chainmu.Unlock()
|
||||
_, evs, logs, err := bc.insertChain(winner)
|
||||
bc.chainmu.Lock()
|
||||
events, coalescedLogs = evs, logs
|
||||
|
||||
if err != nil {
|
||||
return i, events, coalescedLogs, err
|
||||
}
|
||||
|
||||
case err != nil:
|
||||
bc.reportBlock(block, nil, err)
|
||||
return i, events, coalescedLogs, err
|
||||
}
|
||||
@@ -979,8 +1149,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
|
||||
bc.reportBlock(block, receipts, err)
|
||||
return i, events, coalescedLogs, err
|
||||
}
|
||||
proctime := time.Since(bstart)
|
||||
|
||||
// Write the block to the chain and get the status.
|
||||
status, err := bc.WriteBlockAndState(block, receipts, state)
|
||||
status, err := bc.WriteBlockWithState(block, receipts, state)
|
||||
if err != nil {
|
||||
return i, events, coalescedLogs, err
|
||||
}
|
||||
@@ -994,6 +1166,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
|
||||
events = append(events, ChainEvent{block, block.Hash(), logs})
|
||||
lastCanon = block
|
||||
|
||||
// Only count canonical blocks for GC processing time
|
||||
bc.gcproc += proctime
|
||||
|
||||
case SideStatTy:
|
||||
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",
|
||||
common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))
|
||||
@@ -1002,11 +1177,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
|
||||
events = append(events, ChainSideEvent{block})
|
||||
}
|
||||
stats.processed++
|
||||
stats.usedGas += usedGas.Uint64()
|
||||
stats.report(chain, i)
|
||||
stats.usedGas += usedGas
|
||||
stats.report(chain, i, bc.stateCache.TrieDB().Size())
|
||||
}
|
||||
// Append a single chain head event if we've progressed the chain
|
||||
if lastCanon != nil && bc.LastBlockHash() == lastCanon.Hash() {
|
||||
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
|
||||
events = append(events, ChainHeadEvent{lastCanon})
|
||||
}
|
||||
return 0, events, coalescedLogs, nil
|
||||
@@ -1026,7 +1201,7 @@ 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) {
|
||||
func (st *insertStats) report(chain []*types.Block, index int, cache common.StorageSize) {
|
||||
// Fetch the timings for the batch
|
||||
var (
|
||||
now = mclock.Now()
|
||||
@@ -1041,7 +1216,7 @@ func (st *insertStats) report(chain []*types.Block, index int) {
|
||||
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(),
|
||||
"number", end.Number(), "hash", end.Hash(), "cache", cache,
|
||||
}
|
||||
if st.queued > 0 {
|
||||
context = append(context, []interface{}{"queued", st.queued}...)
|
||||
@@ -1077,7 +1252,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||
// These logs are later announced as deleted.
|
||||
collectLogs = func(h common.Hash) {
|
||||
// Coalesce logs and set 'Removed'.
|
||||
receipts := GetBlockReceipts(bc.chainDb, h, bc.hc.GetBlockNumber(h))
|
||||
receipts := GetBlockReceipts(bc.db, h, bc.hc.GetBlockNumber(h))
|
||||
for _, receipt := range receipts {
|
||||
for _, log := range receipt.Logs {
|
||||
del := *log
|
||||
@@ -1140,24 +1315,23 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||
} else {
|
||||
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
|
||||
}
|
||||
// Insert the new chain, taking care of the proper incremental order
|
||||
var addedTxs types.Transactions
|
||||
// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
|
||||
for _, block := range newChain {
|
||||
for i := len(newChain) - 1; i >= 0; i-- {
|
||||
// insert the block in the canonical way, re-writing history
|
||||
bc.insert(block)
|
||||
bc.insert(newChain[i])
|
||||
// write lookup entries for hash based transaction/receipt searches
|
||||
if err := WriteTxLookupEntries(bc.chainDb, block); err != nil {
|
||||
if err := WriteTxLookupEntries(bc.db, newChain[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
addedTxs = append(addedTxs, block.Transactions()...)
|
||||
addedTxs = append(addedTxs, newChain[i].Transactions()...)
|
||||
}
|
||||
|
||||
// calculate the difference between deleted and added transactions
|
||||
diff := types.TxDifference(deletedTxs, addedTxs)
|
||||
// When transactions get deleted from the database that means the
|
||||
// receipts that were created in the fork must also be deleted
|
||||
for _, tx := range diff {
|
||||
DeleteTxLookupEntry(bc.chainDb, tx.Hash())
|
||||
DeleteTxLookupEntry(bc.db, tx.Hash())
|
||||
}
|
||||
if len(deletedLogs) > 0 {
|
||||
go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
|
||||
@@ -1196,10 +1370,11 @@ func (bc *BlockChain) PostChainEvents(events []interface{}, logs []*types.Log) {
|
||||
}
|
||||
|
||||
func (bc *BlockChain) update() {
|
||||
futureTimer := time.Tick(5 * time.Second)
|
||||
futureTimer := time.NewTicker(5 * time.Second)
|
||||
defer futureTimer.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-futureTimer:
|
||||
case <-futureTimer.C:
|
||||
bc.procFutureBlocks()
|
||||
case <-bc.quit:
|
||||
return
|
||||
@@ -1248,7 +1423,7 @@ Hash: 0x%x
|
||||
|
||||
Error: %v
|
||||
##############################
|
||||
`, bc.config, block.Number(), block.Hash(), receiptString, err))
|
||||
`, bc.chainConfig, block.Number(), block.Hash(), receiptString, err))
|
||||
}
|
||||
|
||||
// InsertHeaderChain attempts to insert the given header chain in to the local
|
||||
@@ -1306,9 +1481,6 @@ func (bc *BlockChain) writeHeader(header *types.Header) error {
|
||||
// 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 {
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
return bc.hc.CurrentHeader()
|
||||
}
|
||||
|
||||
@@ -1355,7 +1527,7 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||
}
|
||||
|
||||
// Config retrieves the blockchain's chain configuration.
|
||||
func (bc *BlockChain) Config() *params.ChainConfig { return bc.config }
|
||||
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 }
|
||||
|
||||
+310
-169
@@ -34,30 +34,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// newTestBlockChain creates a blockchain without validation.
|
||||
func newTestBlockChain(fake bool) *BlockChain {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Difficulty: big.NewInt(1),
|
||||
}
|
||||
gspec.MustCommit(db)
|
||||
engine := ethash.NewFullFaker()
|
||||
if !fake {
|
||||
engine = ethash.NewTester()
|
||||
}
|
||||
blockchain, err := NewBlockChain(db, gspec.Config, engine, vm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
blockchain.SetValidator(bproc{})
|
||||
return blockchain
|
||||
}
|
||||
|
||||
// Test fork of length N starting from block i
|
||||
func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) {
|
||||
// Copy old chain up to #i into a new db
|
||||
db, blockchain2, err := newCanonical(i, full)
|
||||
db, blockchain2, err := newCanonical(ethash.NewFaker(), i, full)
|
||||
if err != nil {
|
||||
t.Fatal("could not make new canonical in testFork", err)
|
||||
}
|
||||
@@ -81,12 +61,12 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara
|
||||
headerChainB []*types.Header
|
||||
)
|
||||
if full {
|
||||
blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, db, forkSeed)
|
||||
blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed)
|
||||
if _, err := blockchain2.InsertChain(blockChainB); err != nil {
|
||||
t.Fatalf("failed to insert forking chain: %v", err)
|
||||
}
|
||||
} else {
|
||||
headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, db, forkSeed)
|
||||
headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, ethash.NewFaker(), db, forkSeed)
|
||||
if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil {
|
||||
t.Fatalf("failed to insert forking chain: %v", err)
|
||||
}
|
||||
@@ -148,9 +128,9 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
return err
|
||||
}
|
||||
blockchain.mu.Lock()
|
||||
WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
||||
WriteBlock(blockchain.chainDb, block)
|
||||
statedb.CommitTo(blockchain.chainDb, false)
|
||||
WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
||||
WriteBlock(blockchain.db, block)
|
||||
statedb.Commit(false)
|
||||
blockchain.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
@@ -166,8 +146,8 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
|
||||
}
|
||||
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
|
||||
blockchain.mu.Lock()
|
||||
WriteTd(blockchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
|
||||
WriteHeader(blockchain.chainDb, header)
|
||||
WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
|
||||
WriteHeader(blockchain.db, header)
|
||||
blockchain.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
@@ -183,13 +163,18 @@ func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *
|
||||
}
|
||||
|
||||
func TestLastBlock(t *testing.T) {
|
||||
bchain := newTestBlockChain(false)
|
||||
defer bchain.Stop()
|
||||
_, blockchain, err := newCanonical(ethash.NewFaker(), 0, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pristine chain: %v", err)
|
||||
}
|
||||
defer blockchain.Stop()
|
||||
|
||||
block := makeBlockChain(bchain.CurrentBlock(), 1, bchain.chainDb, 0)[0]
|
||||
bchain.insert(block)
|
||||
if block.Hash() != GetHeadBlockHash(bchain.chainDb) {
|
||||
t.Errorf("Write/Get HeadBlockHash failed")
|
||||
blocks := makeBlockChain(blockchain.CurrentBlock(), 1, ethash.NewFullFaker(), blockchain.db, 0)
|
||||
if _, err := blockchain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("Failed to insert block: %v", err)
|
||||
}
|
||||
if blocks[len(blocks)-1].Hash() != GetHeadBlockHash(blockchain.db) {
|
||||
t.Fatalf("Write/Get HeadBlockHash failed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +187,7 @@ func testExtendCanonical(t *testing.T, full bool) {
|
||||
length := 5
|
||||
|
||||
// Make first chain starting from genesis
|
||||
_, processor, err := newCanonical(length, full)
|
||||
_, processor, err := newCanonical(ethash.NewFaker(), length, full)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make new canonical chain: %v", err)
|
||||
}
|
||||
@@ -230,7 +215,7 @@ func testShorterFork(t *testing.T, full bool) {
|
||||
length := 10
|
||||
|
||||
// Make first chain starting from genesis
|
||||
_, processor, err := newCanonical(length, full)
|
||||
_, processor, err := newCanonical(ethash.NewFaker(), length, full)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make new canonical chain: %v", err)
|
||||
}
|
||||
@@ -260,7 +245,7 @@ func testLongerFork(t *testing.T, full bool) {
|
||||
length := 10
|
||||
|
||||
// Make first chain starting from genesis
|
||||
_, processor, err := newCanonical(length, full)
|
||||
_, processor, err := newCanonical(ethash.NewFaker(), length, full)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make new canonical chain: %v", err)
|
||||
}
|
||||
@@ -290,7 +275,7 @@ func testEqualFork(t *testing.T, full bool) {
|
||||
length := 10
|
||||
|
||||
// Make first chain starting from genesis
|
||||
_, processor, err := newCanonical(length, full)
|
||||
_, processor, err := newCanonical(ethash.NewFaker(), length, full)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make new canonical chain: %v", err)
|
||||
}
|
||||
@@ -317,7 +302,7 @@ func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) }
|
||||
|
||||
func testBrokenChain(t *testing.T, full bool) {
|
||||
// Make chain starting from genesis
|
||||
db, blockchain, err := newCanonical(10, full)
|
||||
db, blockchain, err := newCanonical(ethash.NewFaker(), 10, full)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make new canonical chain: %v", err)
|
||||
}
|
||||
@@ -325,67 +310,25 @@ func testBrokenChain(t *testing.T, full bool) {
|
||||
|
||||
// Create a forked chain, and try to insert with a missing link
|
||||
if full {
|
||||
chain := makeBlockChain(blockchain.CurrentBlock(), 5, db, forkSeed)[1:]
|
||||
chain := makeBlockChain(blockchain.CurrentBlock(), 5, ethash.NewFaker(), db, forkSeed)[1:]
|
||||
if err := testBlockChainImport(chain, blockchain); err == nil {
|
||||
t.Errorf("broken block chain not reported")
|
||||
}
|
||||
} else {
|
||||
chain := makeHeaderChain(blockchain.CurrentHeader(), 5, db, forkSeed)[1:]
|
||||
chain := makeHeaderChain(blockchain.CurrentHeader(), 5, ethash.NewFaker(), db, forkSeed)[1:]
|
||||
if err := testHeaderChainImport(chain, blockchain); err == nil {
|
||||
t.Errorf("broken header chain not reported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type bproc struct{}
|
||||
|
||||
func (bproc) ValidateBody(*types.Block) error { return nil }
|
||||
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
|
||||
return nil
|
||||
}
|
||||
func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) {
|
||||
return nil, nil, new(big.Int), nil
|
||||
}
|
||||
|
||||
func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header {
|
||||
blocks := makeBlockChainWithDiff(genesis, d, seed)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block {
|
||||
var chain []*types.Block
|
||||
for i, difficulty := range d {
|
||||
header := &types.Header{
|
||||
Coinbase: common.Address{seed},
|
||||
Number: big.NewInt(int64(i + 1)),
|
||||
Difficulty: big.NewInt(int64(difficulty)),
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
TxHash: types.EmptyRootHash,
|
||||
ReceiptHash: types.EmptyRootHash,
|
||||
Time: big.NewInt(int64(i) + 1),
|
||||
}
|
||||
if i == 0 {
|
||||
header.ParentHash = genesis.Hash()
|
||||
} else {
|
||||
header.ParentHash = chain[i-1].Hash()
|
||||
}
|
||||
block := types.NewBlockWithHeader(header)
|
||||
chain = append(chain, block)
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
// Tests that reorganising a long difficult chain after a short easy one
|
||||
// overwrites the canonical numbers and links in the database.
|
||||
func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) }
|
||||
func TestReorgLongBlocks(t *testing.T) { testReorgLong(t, true) }
|
||||
|
||||
func testReorgLong(t *testing.T, full bool) {
|
||||
testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10, full)
|
||||
testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 393280, full)
|
||||
}
|
||||
|
||||
// Tests that reorganising a short difficult chain after a long easy one
|
||||
@@ -394,45 +337,82 @@ func TestReorgShortHeaders(t *testing.T) { testReorgShort(t, false) }
|
||||
func TestReorgShortBlocks(t *testing.T) { testReorgShort(t, true) }
|
||||
|
||||
func testReorgShort(t *testing.T, full bool) {
|
||||
testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11, full)
|
||||
// Create a long easy chain vs. a short heavy one. Due to difficulty adjustment
|
||||
// we need a fairly long chain of blocks with different difficulties for a short
|
||||
// one to become heavyer than a long one. The 96 is an empirical value.
|
||||
easy := make([]int64, 96)
|
||||
for i := 0; i < len(easy); i++ {
|
||||
easy[i] = 60
|
||||
}
|
||||
diff := make([]int64, len(easy)-1)
|
||||
for i := 0; i < len(diff); i++ {
|
||||
diff[i] = -9
|
||||
}
|
||||
testReorg(t, easy, diff, 12615120, full)
|
||||
}
|
||||
|
||||
func testReorg(t *testing.T, first, second []int, td int64, full bool) {
|
||||
bc := newTestBlockChain(true)
|
||||
defer bc.Stop()
|
||||
func testReorg(t *testing.T, first, second []int64, td int64, full bool) {
|
||||
// Create a pristine chain and database
|
||||
db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pristine chain: %v", err)
|
||||
}
|
||||
defer blockchain.Stop()
|
||||
|
||||
// Insert an easy and a difficult chain afterwards
|
||||
easyBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), ethash.NewFaker(), db, len(first), func(i int, b *BlockGen) {
|
||||
b.OffsetTime(first[i])
|
||||
})
|
||||
diffBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), ethash.NewFaker(), db, len(second), func(i int, b *BlockGen) {
|
||||
b.OffsetTime(second[i])
|
||||
})
|
||||
if full {
|
||||
bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, first, 11))
|
||||
bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, second, 22))
|
||||
if _, err := blockchain.InsertChain(easyBlocks); err != nil {
|
||||
t.Fatalf("failed to insert easy chain: %v", err)
|
||||
}
|
||||
if _, err := blockchain.InsertChain(diffBlocks); err != nil {
|
||||
t.Fatalf("failed to insert difficult chain: %v", err)
|
||||
}
|
||||
} else {
|
||||
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1)
|
||||
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1)
|
||||
easyHeaders := make([]*types.Header, len(easyBlocks))
|
||||
for i, block := range easyBlocks {
|
||||
easyHeaders[i] = block.Header()
|
||||
}
|
||||
diffHeaders := make([]*types.Header, len(diffBlocks))
|
||||
for i, block := range diffBlocks {
|
||||
diffHeaders[i] = block.Header()
|
||||
}
|
||||
if _, err := blockchain.InsertHeaderChain(easyHeaders, 1); err != nil {
|
||||
t.Fatalf("failed to insert easy chain: %v", err)
|
||||
}
|
||||
if _, err := blockchain.InsertHeaderChain(diffHeaders, 1); err != nil {
|
||||
t.Fatalf("failed to insert difficult chain: %v", err)
|
||||
}
|
||||
}
|
||||
// Check that the chain is valid number and link wise
|
||||
if full {
|
||||
prev := bc.CurrentBlock()
|
||||
for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) {
|
||||
prev := blockchain.CurrentBlock()
|
||||
for block := blockchain.GetBlockByNumber(blockchain.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, blockchain.GetBlockByNumber(block.NumberU64()-1) {
|
||||
if prev.ParentHash() != block.Hash() {
|
||||
t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash(), block.Hash())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prev := bc.CurrentHeader()
|
||||
for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) {
|
||||
prev := blockchain.CurrentHeader()
|
||||
for header := blockchain.GetHeaderByNumber(blockchain.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, blockchain.GetHeaderByNumber(header.Number.Uint64()-1) {
|
||||
if prev.ParentHash != header.Hash() {
|
||||
t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure the chain total difficulty is the correct one
|
||||
want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td))
|
||||
want := new(big.Int).Add(blockchain.genesisBlock.Difficulty(), big.NewInt(td))
|
||||
if full {
|
||||
if have := bc.GetTdByHash(bc.CurrentBlock().Hash()); have.Cmp(want) != 0 {
|
||||
if have := blockchain.GetTdByHash(blockchain.CurrentBlock().Hash()); have.Cmp(want) != 0 {
|
||||
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
|
||||
}
|
||||
} else {
|
||||
if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 {
|
||||
if have := blockchain.GetTdByHash(blockchain.CurrentHeader().Hash()); have.Cmp(want) != 0 {
|
||||
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
|
||||
}
|
||||
}
|
||||
@@ -443,19 +423,28 @@ func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false) }
|
||||
func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }
|
||||
|
||||
func testBadHashes(t *testing.T, full bool) {
|
||||
bc := newTestBlockChain(true)
|
||||
defer bc.Stop()
|
||||
// Create a pristine chain and database
|
||||
db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pristine chain: %v", err)
|
||||
}
|
||||
defer blockchain.Stop()
|
||||
|
||||
// Create a chain, ban a hash and try to import
|
||||
var err error
|
||||
if full {
|
||||
blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
|
||||
blocks := makeBlockChain(blockchain.CurrentBlock(), 3, ethash.NewFaker(), db, 10)
|
||||
|
||||
BadHashes[blocks[2].Header().Hash()] = true
|
||||
_, err = bc.InsertChain(blocks)
|
||||
defer func() { delete(BadHashes, blocks[2].Header().Hash()) }()
|
||||
|
||||
_, err = blockchain.InsertChain(blocks)
|
||||
} else {
|
||||
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
|
||||
headers := makeHeaderChain(blockchain.CurrentHeader(), 3, ethash.NewFaker(), db, 10)
|
||||
|
||||
BadHashes[headers[2].Hash()] = true
|
||||
_, err = bc.InsertHeaderChain(headers, 1)
|
||||
defer func() { delete(BadHashes, headers[2].Hash()) }()
|
||||
|
||||
_, err = blockchain.InsertHeaderChain(headers, 1)
|
||||
}
|
||||
if err != ErrBlacklistedHash {
|
||||
t.Errorf("error mismatch: have: %v, want: %v", err, ErrBlacklistedHash)
|
||||
@@ -468,52 +457,54 @@ func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false) }
|
||||
func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }
|
||||
|
||||
func testReorgBadHashes(t *testing.T, full bool) {
|
||||
bc := newTestBlockChain(true)
|
||||
defer bc.Stop()
|
||||
|
||||
// Create a pristine chain and database
|
||||
db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pristine chain: %v", err)
|
||||
}
|
||||
// Create a chain, import and ban afterwards
|
||||
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
|
||||
blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
|
||||
headers := makeHeaderChain(blockchain.CurrentHeader(), 4, ethash.NewFaker(), db, 10)
|
||||
blocks := makeBlockChain(blockchain.CurrentBlock(), 4, ethash.NewFaker(), db, 10)
|
||||
|
||||
if full {
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to import blocks: %v", err)
|
||||
if _, err = blockchain.InsertChain(blocks); err != nil {
|
||||
t.Errorf("failed to import blocks: %v", err)
|
||||
}
|
||||
if bc.CurrentBlock().Hash() != blocks[3].Hash() {
|
||||
t.Errorf("last block hash mismatch: have: %x, want %x", bc.CurrentBlock().Hash(), blocks[3].Header().Hash())
|
||||
if blockchain.CurrentBlock().Hash() != blocks[3].Hash() {
|
||||
t.Errorf("last block hash mismatch: have: %x, want %x", blockchain.CurrentBlock().Hash(), blocks[3].Header().Hash())
|
||||
}
|
||||
BadHashes[blocks[3].Header().Hash()] = true
|
||||
defer func() { delete(BadHashes, blocks[3].Header().Hash()) }()
|
||||
} else {
|
||||
if _, err := bc.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Fatalf("failed to import headers: %v", err)
|
||||
if _, err = blockchain.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Errorf("failed to import headers: %v", err)
|
||||
}
|
||||
if bc.CurrentHeader().Hash() != headers[3].Hash() {
|
||||
t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash())
|
||||
if blockchain.CurrentHeader().Hash() != headers[3].Hash() {
|
||||
t.Errorf("last header hash mismatch: have: %x, want %x", blockchain.CurrentHeader().Hash(), headers[3].Hash())
|
||||
}
|
||||
BadHashes[headers[3].Hash()] = true
|
||||
defer func() { delete(BadHashes, headers[3].Hash()) }()
|
||||
}
|
||||
blockchain.Stop()
|
||||
|
||||
// Create a new BlockChain and check that it rolled back the state.
|
||||
ncm, err := NewBlockChain(bc.chainDb, bc.config, ethash.NewFaker(), vm.Config{})
|
||||
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new chain manager: %v", err)
|
||||
}
|
||||
defer ncm.Stop()
|
||||
|
||||
if full {
|
||||
if ncm.CurrentBlock().Hash() != blocks[2].Header().Hash() {
|
||||
t.Errorf("last block hash mismatch: have: %x, want %x", ncm.CurrentBlock().Hash(), blocks[2].Header().Hash())
|
||||
}
|
||||
if blocks[2].Header().GasLimit.Cmp(ncm.GasLimit()) != 0 {
|
||||
t.Errorf("last block gasLimit mismatch: have: %x, want %x", ncm.GasLimit(), blocks[2].Header().GasLimit)
|
||||
if blocks[2].Header().GasLimit != ncm.GasLimit() {
|
||||
t.Errorf("last block gasLimit mismatch: have: %d, want %d", ncm.GasLimit(), blocks[2].Header().GasLimit)
|
||||
}
|
||||
} else {
|
||||
if ncm.CurrentHeader().Hash() != headers[2].Hash() {
|
||||
t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash())
|
||||
}
|
||||
}
|
||||
ncm.Stop()
|
||||
}
|
||||
|
||||
// Tests chain insertions in the face of one entity containing an invalid nonce.
|
||||
@@ -523,7 +514,7 @@ func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) }
|
||||
func testInsertNonceError(t *testing.T, full bool) {
|
||||
for i := 1; i < 25 && !t.Failed(); i++ {
|
||||
// Create a pristine chain and database
|
||||
db, blockchain, err := newCanonical(0, full)
|
||||
db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pristine chain: %v", err)
|
||||
}
|
||||
@@ -536,7 +527,7 @@ func testInsertNonceError(t *testing.T, full bool) {
|
||||
failNum uint64
|
||||
)
|
||||
if full {
|
||||
blocks := makeBlockChain(blockchain.CurrentBlock(), i, db, 0)
|
||||
blocks := makeBlockChain(blockchain.CurrentBlock(), i, ethash.NewFaker(), db, 0)
|
||||
|
||||
failAt = rand.Int() % len(blocks)
|
||||
failNum = blocks[failAt].NumberU64()
|
||||
@@ -544,7 +535,7 @@ func testInsertNonceError(t *testing.T, full bool) {
|
||||
blockchain.engine = ethash.NewFakeFailer(failNum)
|
||||
failRes, err = blockchain.InsertChain(blocks)
|
||||
} else {
|
||||
headers := makeHeaderChain(blockchain.CurrentHeader(), i, db, 0)
|
||||
headers := makeHeaderChain(blockchain.CurrentHeader(), i, ethash.NewFaker(), db, 0)
|
||||
|
||||
failAt = rand.Int() % len(headers)
|
||||
failNum = headers[failAt].Number.Uint64()
|
||||
@@ -588,13 +579,13 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
)
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, gendb, 1024, func(i int, block *BlockGen) {
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, 1024, func(i int, block *BlockGen) {
|
||||
block.SetCoinbase(common.Address{0x00})
|
||||
|
||||
// If the block number is multiple of 3, send a few bonus transactions to the miner
|
||||
if i%3 == 2 {
|
||||
for j := 0; j < i%4+1; j++ {
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), bigTxGas, nil, nil), signer, key)
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -609,7 +600,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
// Import the chain as an archive node for the comparison baseline
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(archiveDb)
|
||||
archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer archive.Stop()
|
||||
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
@@ -618,7 +609,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
// Fast import the chain as a non-archive node to test
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer fast.Stop()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
@@ -673,7 +664,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
)
|
||||
height := uint64(1024)
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, gendb, int(height), nil)
|
||||
blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), nil)
|
||||
|
||||
// Configure a subchain to roll back
|
||||
remove := []common.Hash{}
|
||||
@@ -696,7 +687,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(archiveDb)
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
}
|
||||
@@ -709,7 +700,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer fast.Stop()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
@@ -730,7 +721,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
lightDb, _ := ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(lightDb)
|
||||
|
||||
light, _ := NewBlockChain(lightDb, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||
}
|
||||
@@ -767,8 +758,8 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
// Create two transactions shared between the chains:
|
||||
// - postponed: transaction included at a later block in the forked chain
|
||||
// - swapped: transaction included at the same block number in the forked chain
|
||||
postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
|
||||
swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
|
||||
postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
||||
swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
||||
|
||||
// Create two transactions that will be dropped by the forked chain:
|
||||
// - pastDrop: transaction dropped retroactively from a past block
|
||||
@@ -781,16 +772,16 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
// - futureAdd: transaction added after the reorg has already finished
|
||||
var pastAdd, freshAdd, futureAdd *types.Transaction
|
||||
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, gen *BlockGen) {
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
||||
|
||||
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
|
||||
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
|
||||
|
||||
case 2:
|
||||
freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||
freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
||||
|
||||
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
|
||||
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
|
||||
@@ -799,28 +790,28 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
}
|
||||
})
|
||||
// Import the chain. This runs all block validation rules.
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
||||
}
|
||||
defer blockchain.Stop()
|
||||
|
||||
// overwrite the old chain
|
||||
chain, _ = GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
chain, _ = GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
||||
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
|
||||
|
||||
case 2:
|
||||
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
|
||||
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
|
||||
|
||||
freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||
freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
||||
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
|
||||
|
||||
case 3:
|
||||
futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||
futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
||||
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
|
||||
}
|
||||
})
|
||||
@@ -870,14 +861,14 @@ func TestLogReorgs(t *testing.T) {
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
rmLogsCh := make(chan RemovedLogsEvent)
|
||||
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) {
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
||||
if i == 1 {
|
||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code), signer, key1)
|
||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tx: %v", err)
|
||||
}
|
||||
@@ -888,7 +879,7 @@ func TestLogReorgs(t *testing.T) {
|
||||
t.Fatalf("failed to insert chain: %v", err)
|
||||
}
|
||||
|
||||
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||
chain, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
|
||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert forked chain: %v", err)
|
||||
}
|
||||
@@ -917,16 +908,16 @@ func TestReorgSideEvent(t *testing.T) {
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
|
||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert chain: %v", err)
|
||||
}
|
||||
|
||||
replacementBlocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, gen *BlockGen) {
|
||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil), signer, key1)
|
||||
replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) {
|
||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1)
|
||||
if i == 2 {
|
||||
gen.OffsetTime(-9)
|
||||
}
|
||||
@@ -989,10 +980,13 @@ done:
|
||||
|
||||
// Tests if the canonical block can be fetched from the database during chain insertion.
|
||||
func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
bc := newTestBlockChain(true)
|
||||
defer bc.Stop()
|
||||
_, blockchain, err := newCanonical(ethash.NewFaker(), 0, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pristine chain: %v", err)
|
||||
}
|
||||
defer blockchain.Stop()
|
||||
|
||||
chain, _ := GenerateChain(bc.config, bc.genesisBlock, bc.chainDb, 10, func(i int, gen *BlockGen) {})
|
||||
chain, _ := GenerateChain(blockchain.chainConfig, blockchain.genesisBlock, ethash.NewFaker(), blockchain.db, 10, func(i int, gen *BlockGen) {})
|
||||
|
||||
var pend sync.WaitGroup
|
||||
pend.Add(len(chain))
|
||||
@@ -1003,14 +997,14 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
|
||||
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
|
||||
for {
|
||||
ch := GetCanonicalHash(bc.chainDb, block.NumberU64())
|
||||
ch := GetCanonicalHash(blockchain.db, block.NumberU64())
|
||||
if ch == (common.Hash{}) {
|
||||
continue // busy wait for canonical hash to be written
|
||||
}
|
||||
if ch != block.Hash() {
|
||||
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
|
||||
}
|
||||
fb := GetBlock(bc.chainDb, ch, block.NumberU64())
|
||||
fb := GetBlock(blockchain.db, ch, block.NumberU64())
|
||||
if fb == nil {
|
||||
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
|
||||
}
|
||||
@@ -1021,7 +1015,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
}
|
||||
}(chain[i])
|
||||
|
||||
if _, err := bc.InsertChain(types.Blocks{chain[i]}); err != nil {
|
||||
if _, err := blockchain.InsertChain(types.Blocks{chain[i]}); err != nil {
|
||||
t.Fatalf("failed to insert block %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
@@ -1043,15 +1037,15 @@ func TestEIP155Transition(t *testing.T) {
|
||||
genesis = gspec.MustCommit(db)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, block *BlockGen) {
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
basicTx = func(signer types.Signer) (*types.Transaction, error) {
|
||||
return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
|
||||
return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
||||
}
|
||||
)
|
||||
switch i {
|
||||
@@ -1109,12 +1103,12 @@ func TestEIP155Transition(t *testing.T) {
|
||||
|
||||
// generate an invalid chain id transaction
|
||||
config := ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) {
|
||||
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
basicTx = func(signer types.Signer) (*types.Transaction, error) {
|
||||
return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
|
||||
return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
||||
}
|
||||
)
|
||||
switch i {
|
||||
@@ -1151,10 +1145,10 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
)
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, block *BlockGen) {
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
@@ -1162,11 +1156,11 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
|
||||
tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
||||
case 1:
|
||||
tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
|
||||
tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
||||
case 2:
|
||||
tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil), signer, key)
|
||||
tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1197,3 +1191,150 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
||||
t.Error("account should not exist")
|
||||
}
|
||||
}
|
||||
|
||||
// This is a regression test (i.e. as weird as it is, don't delete it ever), which
|
||||
// tests that under weird reorg conditions the blockchain and its internal header-
|
||||
// chain return the same latest block/header.
|
||||
//
|
||||
// https://github.com/ethereum/go-ethereum/pull/15941
|
||||
func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
||||
forks := make([]*types.Block, len(blocks))
|
||||
for i := 0; i < len(forks); i++ {
|
||||
parent := genesis
|
||||
if i > 0 {
|
||||
parent = blocks[i-1]
|
||||
}
|
||||
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
||||
forks[i] = fork[0]
|
||||
}
|
||||
// Import the canonical and fork chain side by side, verifying the current block
|
||||
// and current header consistency
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
|
||||
}
|
||||
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
|
||||
t.Errorf("block %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
||||
}
|
||||
if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
|
||||
t.Fatalf(" fork %d: failed to insert into chain: %v", i, err)
|
||||
}
|
||||
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
|
||||
t.Errorf(" fork %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that importing small side forks doesn't leave junk in the trie database
|
||||
// cache (which would eventually cause memory issues).
|
||||
func TestTrieForkGC(t *testing.T) {
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
||||
forks := make([]*types.Block, len(blocks))
|
||||
for i := 0; i < len(forks); i++ {
|
||||
parent := genesis
|
||||
if i > 0 {
|
||||
parent = blocks[i-1]
|
||||
}
|
||||
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
||||
forks[i] = fork[0]
|
||||
}
|
||||
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
|
||||
}
|
||||
if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
|
||||
t.Fatalf("fork %d: failed to insert into chain: %v", i, err)
|
||||
}
|
||||
}
|
||||
// Dereference all the recent tries and ensure no past trie is left in
|
||||
for i := 0; i < triesInMemory; i++ {
|
||||
chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root(), common.Hash{})
|
||||
chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root(), common.Hash{})
|
||||
}
|
||||
if len(chain.stateCache.TrieDB().Nodes()) > 0 {
|
||||
t.Fatalf("stale tries still alive after garbase collection")
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that doing large reorgs works even if the state associated with the
|
||||
// forking point is not available any more.
|
||||
func TestLargeReorgTrieGC(t *testing.T) {
|
||||
// Generate the original common chain segment and the two competing forks
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
|
||||
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
||||
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
||||
|
||||
// Import the shared chain and the original canonical one
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
if _, err := chain.InsertChain(shared); err != nil {
|
||||
t.Fatalf("failed to insert shared chain: %v", err)
|
||||
}
|
||||
if _, err := chain.InsertChain(original); err != nil {
|
||||
t.Fatalf("failed to insert shared chain: %v", err)
|
||||
}
|
||||
// Ensure that the state associated with the forking point is pruned away
|
||||
if node, _ := chain.stateCache.TrieDB().Node(shared[len(shared)-1].Root()); node != nil {
|
||||
t.Fatalf("common-but-old ancestor still cache")
|
||||
}
|
||||
// Import the competitor chain without exceeding the canonical's TD and ensure
|
||||
// we have not processed any of the blocks (protection against malicious blocks)
|
||||
if _, err := chain.InsertChain(competitor[:len(competitor)-2]); err != nil {
|
||||
t.Fatalf("failed to insert competitor chain: %v", err)
|
||||
}
|
||||
for i, block := range competitor[:len(competitor)-2] {
|
||||
if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
|
||||
t.Fatalf("competitor %d: low TD chain became processed", i)
|
||||
}
|
||||
}
|
||||
// Import the head of the competitor chain, triggering the reorg and ensure we
|
||||
// successfully reprocess all the stashed away blocks.
|
||||
if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
|
||||
t.Fatalf("failed to finalize competitor chain: %v", err)
|
||||
}
|
||||
for i, block := range competitor[:len(competitor)-triesInMemory] {
|
||||
if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
|
||||
t.Fatalf("competitor %d: competing chain state missing", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -203,6 +203,9 @@ func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainE
|
||||
if header.ParentHash != prevHash {
|
||||
// Reorg to the common ancestor (might not exist in light sync mode, skip reorg then)
|
||||
// TODO(karalabe, zsfelfoldi): This seems a bit brittle, can we detect this case explicitly?
|
||||
|
||||
// TODO(karalabe): This operation is expensive and might block, causing the event system to
|
||||
// potentially also lock up. We need to do with on a different thread somehow.
|
||||
if h := FindCommonAncestor(c.chainDb, prevHeader, header); h != nil {
|
||||
c.newHead(h.Number.Uint64(), true)
|
||||
}
|
||||
@@ -230,7 +233,7 @@ func (c *ChainIndexer) newHead(head uint64, reorg bool) {
|
||||
if changed < c.storedSections {
|
||||
c.setValidSections(changed)
|
||||
}
|
||||
// Update the new head number to te finalized section end and notify children
|
||||
// Update the new head number to the finalized section end and notify children
|
||||
head = changed * c.sectionSize
|
||||
|
||||
if head < c.cascadedHead {
|
||||
|
||||
+48
-34
@@ -21,7 +21,7 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@@ -39,11 +39,12 @@ var (
|
||||
// BlockGen creates blocks for testing.
|
||||
// See GenerateChain for a detailed explanation.
|
||||
type BlockGen struct {
|
||||
i int
|
||||
parent *types.Block
|
||||
chain []*types.Block
|
||||
header *types.Header
|
||||
statedb *state.StateDB
|
||||
i int
|
||||
parent *types.Block
|
||||
chain []*types.Block
|
||||
chainReader consensus.ChainReader
|
||||
header *types.Header
|
||||
statedb *state.StateDB
|
||||
|
||||
gasPool *GasPool
|
||||
txs []*types.Transaction
|
||||
@@ -51,6 +52,7 @@ type BlockGen struct {
|
||||
uncles []*types.Header
|
||||
|
||||
config *params.ChainConfig
|
||||
engine consensus.Engine
|
||||
}
|
||||
|
||||
// SetCoinbase sets the coinbase of the generated block.
|
||||
@@ -84,7 +86,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
|
||||
b.SetCoinbase(common.Address{})
|
||||
}
|
||||
b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
|
||||
receipt, _, err := ApplyTransaction(b.config, nil, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
|
||||
receipt, _, err := ApplyTransaction(b.config, nil, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -141,7 +143,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
||||
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
|
||||
panic("block time out of range")
|
||||
}
|
||||
b.header.Difficulty = ethash.CalcDifficulty(b.config, b.header.Time.Uint64(), b.parent.Header())
|
||||
b.header.Difficulty = b.engine.CalcDifficulty(b.chainReader, b.header.Time.Uint64(), b.parent.Header())
|
||||
}
|
||||
|
||||
// GenerateChain creates a chain of n blocks. The first block's
|
||||
@@ -156,44 +158,57 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
||||
// Blocks created by GenerateChain do not contain valid proof of work
|
||||
// values. Inserting them into BlockChain requires use of FakePow or
|
||||
// a similar non-validating proof of work implementation.
|
||||
func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
||||
func GenerateChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
||||
if config == nil {
|
||||
config = params.TestChainConfig
|
||||
}
|
||||
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
|
||||
genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config}
|
||||
genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||
// TODO(karalabe): This is needed for clique, which depends on multiple blocks.
|
||||
// It's nonetheless ugly to spin up a blockchain here. Get rid of this somehow.
|
||||
blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine}
|
||||
b.header = makeHeader(b.chainReader, parent, statedb, b.engine)
|
||||
|
||||
// Mutate the state and block according to any hard-fork specs
|
||||
if daoBlock := config.DAOForkBlock; daoBlock != nil {
|
||||
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
|
||||
if h.Number.Cmp(daoBlock) >= 0 && h.Number.Cmp(limit) < 0 {
|
||||
if b.header.Number.Cmp(daoBlock) >= 0 && b.header.Number.Cmp(limit) < 0 {
|
||||
if config.DAOForkSupport {
|
||||
h.Extra = common.CopyBytes(params.DAOForkBlockExtra)
|
||||
b.header.Extra = common.CopyBytes(params.DAOForkBlockExtra)
|
||||
}
|
||||
}
|
||||
}
|
||||
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(h.Number) == 0 {
|
||||
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
|
||||
misc.ApplyDAOHardFork(statedb)
|
||||
}
|
||||
// Execute any user modifications to the block and finalize it
|
||||
if gen != nil {
|
||||
gen(i, b)
|
||||
}
|
||||
ethash.AccumulateRewards(config, statedb, h, b.uncles)
|
||||
root, err := statedb.CommitTo(db, config.IsEIP158(h.Number))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("state write error: %v", err))
|
||||
|
||||
if b.engine != nil {
|
||||
block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts)
|
||||
// Write state changes to db
|
||||
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("state write error: %v", err))
|
||||
}
|
||||
if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
|
||||
panic(fmt.Sprintf("trie write error: %v", err))
|
||||
}
|
||||
return block, b.receipts
|
||||
}
|
||||
h.Root = root
|
||||
return types.NewBlock(h, b.txs, b.uncles, b.receipts), b.receipts
|
||||
return nil, nil
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
statedb, err := state.New(parent.Root(), state.NewDatabase(db))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
header := makeHeader(config, parent, statedb)
|
||||
block, receipt := genblock(i, header, statedb)
|
||||
block, receipt := genblock(i, parent, statedb)
|
||||
blocks[i] = block
|
||||
receipts[i] = receipt
|
||||
parent = block
|
||||
@@ -201,7 +216,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Dat
|
||||
return blocks, receipts
|
||||
}
|
||||
|
||||
func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.StateDB) *types.Header {
|
||||
func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header {
|
||||
var time *big.Int
|
||||
if parent.Time() == nil {
|
||||
time = big.NewInt(10)
|
||||
@@ -210,17 +225,16 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
|
||||
}
|
||||
|
||||
return &types.Header{
|
||||
Root: state.IntermediateRoot(config.IsEIP158(parent.Number())),
|
||||
Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())),
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
Difficulty: ethash.CalcDifficulty(config, time.Uint64(), &types.Header{
|
||||
Difficulty: engine.CalcDifficulty(chain, time.Uint64(), &types.Header{
|
||||
Number: parent.Number(),
|
||||
Time: new(big.Int).Sub(time, big.NewInt(10)),
|
||||
Difficulty: parent.Difficulty(),
|
||||
UncleHash: parent.UncleHash(),
|
||||
}),
|
||||
GasLimit: CalcGasLimit(parent),
|
||||
GasUsed: new(big.Int),
|
||||
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||
Time: time,
|
||||
}
|
||||
@@ -229,32 +243,32 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
|
||||
// newCanonical creates a chain database, and injects a deterministic canonical
|
||||
// chain. Depending on the full flag, if creates either a full block chain or a
|
||||
// header only chain.
|
||||
func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||
func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
gspec := new(Genesis)
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis := gspec.MustCommit(db)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, params.AllEthashProtocolChanges, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{})
|
||||
// Create and inject the requested chain
|
||||
if n == 0 {
|
||||
return db, blockchain, nil
|
||||
}
|
||||
if full {
|
||||
// Full block-chain requested
|
||||
blocks := makeBlockChain(genesis, n, db, canonicalSeed)
|
||||
blocks := makeBlockChain(genesis, n, engine, db, canonicalSeed)
|
||||
_, err := blockchain.InsertChain(blocks)
|
||||
return db, blockchain, err
|
||||
}
|
||||
// Header-only chain requested
|
||||
headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed)
|
||||
headers := makeHeaderChain(genesis.Header(), n, engine, db, canonicalSeed)
|
||||
_, err := blockchain.InsertHeaderChain(headers, 1)
|
||||
return db, blockchain, err
|
||||
}
|
||||
|
||||
// makeHeaderChain creates a deterministic chain of headers rooted at parent.
|
||||
func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header {
|
||||
blocks := makeBlockChain(types.NewBlockWithHeader(parent), n, db, seed)
|
||||
func makeHeaderChain(parent *types.Header, n int, engine consensus.Engine, db ethdb.Database, seed int) []*types.Header {
|
||||
blocks := makeBlockChain(types.NewBlockWithHeader(parent), n, engine, db, seed)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
@@ -263,8 +277,8 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [
|
||||
}
|
||||
|
||||
// makeBlockChain creates a deterministic chain of blocks rooted at parent.
|
||||
func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block {
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, parent, db, n, func(i int, b *BlockGen) {
|
||||
func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethdb.Database, seed int) []*types.Block {
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, parent, engine, db, n, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
|
||||
})
|
||||
return blocks
|
||||
|
||||
+5
-5
@@ -50,17 +50,17 @@ func ExampleGenerateChain() {
|
||||
// each block and adds different features to gen based on the
|
||||
// block index.
|
||||
signer := types.HomesteadSigner{}
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, addr1 sends addr2 some ether.
|
||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), bigTxGas, nil, nil), signer, key1)
|
||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
|
||||
gen.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, addr1 sends some more ether to addr2.
|
||||
// addr2 passes it on to addr3.
|
||||
tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||
tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
||||
gen.AddTx(tx1)
|
||||
gen.AddTx(tx2)
|
||||
case 2:
|
||||
@@ -79,7 +79,7 @@ func ExampleGenerateChain() {
|
||||
})
|
||||
|
||||
// Import the chain. This runs all block validation rules.
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
|
||||
+25
-13
@@ -35,7 +35,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
gspec := new(Genesis)
|
||||
genesis := gspec.MustCommit(db)
|
||||
prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
||||
prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
||||
|
||||
// Create the concurrent, conflicting two nodes
|
||||
proDb, _ := ethdb.NewMemDatabase()
|
||||
@@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
proConf.DAOForkBlock = forkBlock
|
||||
proConf.DAOForkSupport = true
|
||||
|
||||
proBc, _ := NewBlockChain(proDb, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
defer proBc.Stop()
|
||||
|
||||
conDb, _ := ethdb.NewMemDatabase()
|
||||
@@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
conConf.DAOForkBlock = forkBlock
|
||||
conConf.DAOForkSupport = false
|
||||
|
||||
conBc, _ := NewBlockChain(conDb, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
defer conBc.Stop()
|
||||
|
||||
if _, err := proBc.InsertChain(prefix); err != nil {
|
||||
@@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
// Create a pro-fork block, and try to feed into the no-fork chain
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
||||
@@ -79,19 +79,22 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
|
||||
}
|
||||
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
|
||||
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
|
||||
}
|
||||
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if _, err := conBc.InsertChain(blocks); err == nil {
|
||||
t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0])
|
||||
}
|
||||
// Create a proper no-fork block for the contra-forker
|
||||
blocks, _ = GenerateChain(&conConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||
blocks, _ = GenerateChain(&conConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if _, err := conBc.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err)
|
||||
}
|
||||
// Create a no-fork block, and try to feed into the pro-fork chain
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
||||
@@ -101,12 +104,15 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
|
||||
}
|
||||
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
|
||||
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
|
||||
}
|
||||
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if _, err := proBc.InsertChain(blocks); err == nil {
|
||||
t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0])
|
||||
}
|
||||
// Create a proper pro-fork block for the pro-forker
|
||||
blocks, _ = GenerateChain(&proConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||
blocks, _ = GenerateChain(&proConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if _, err := proBc.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("pro-fork chain didn't accepted pro-fork block: %v", err)
|
||||
}
|
||||
@@ -114,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
||||
@@ -124,14 +130,17 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
|
||||
}
|
||||
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
|
||||
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
|
||||
}
|
||||
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if _, err := conBc.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err)
|
||||
}
|
||||
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
||||
@@ -141,7 +150,10 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
|
||||
}
|
||||
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
|
||||
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
|
||||
}
|
||||
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||
if _, err := proBc.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err)
|
||||
}
|
||||
|
||||
+22
-2
@@ -47,6 +47,7 @@ var (
|
||||
headHeaderKey = []byte("LastHeader")
|
||||
headBlockKey = []byte("LastBlock")
|
||||
headFastKey = []byte("LastFast")
|
||||
trieSyncKey = []byte("TrieSync")
|
||||
|
||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`).
|
||||
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||
@@ -70,8 +71,8 @@ var (
|
||||
|
||||
ErrChainConfigNotFound = errors.New("ChainConfig not found") // general config not found error
|
||||
|
||||
preimageCounter = metrics.NewCounter("db/preimage/total")
|
||||
preimageHitCounter = metrics.NewCounter("db/preimage/hits")
|
||||
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
|
||||
@@ -146,6 +147,16 @@ func GetHeadFastBlockHash(db DatabaseReader) common.Hash {
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// GetTrieSyncProgress retrieves the number of tries nodes fast synced to allow
|
||||
// reportinc correct numbers across restarts.
|
||||
func GetTrieSyncProgress(db DatabaseReader) uint64 {
|
||||
data, _ := db.Get(trieSyncKey)
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
return new(big.Int).SetBytes(data).Uint64()
|
||||
}
|
||||
|
||||
// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
|
||||
// if the header's not found.
|
||||
func GetHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
@@ -374,6 +385,15 @@ func WriteHeadFastBlockHash(db ethdb.Putter, hash common.Hash) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteTrieSyncProgress stores the fast sync trie process counter to support
|
||||
// retrieving it across restarts.
|
||||
func WriteTrieSyncProgress(db ethdb.Putter, count uint64) error {
|
||||
if err := db.Put(trieSyncKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
|
||||
log.Crit("Failed to store fast sync trie progress", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteHeader serializes a block header into the database.
|
||||
func WriteHeader(db ethdb.Putter, header *types.Header) error {
|
||||
data, err := rlp.EncodeToBytes(header)
|
||||
|
||||
+7
-7
@@ -290,9 +290,9 @@ func TestHeadStorage(t *testing.T) {
|
||||
func TestLookupStorage(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
||||
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), big.NewInt(1111), big.NewInt(11111), []byte{0x11, 0x11, 0x11})
|
||||
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), big.NewInt(2222), big.NewInt(22222), []byte{0x22, 0x22, 0x22})
|
||||
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), big.NewInt(3333), big.NewInt(33333), []byte{0x33, 0x33, 0x33})
|
||||
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)
|
||||
@@ -337,25 +337,25 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
|
||||
receipt1 := &types.Receipt{
|
||||
Status: types.ReceiptStatusFailed,
|
||||
CumulativeGasUsed: big.NewInt(1),
|
||||
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: big.NewInt(111111),
|
||||
GasUsed: 111111,
|
||||
}
|
||||
receipt2 := &types.Receipt{
|
||||
PostState: common.Hash{2}.Bytes(),
|
||||
CumulativeGasUsed: big.NewInt(2),
|
||||
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: big.NewInt(222222),
|
||||
GasUsed: 222222,
|
||||
}
|
||||
receipts := []*types.Receipt{receipt1, receipt2}
|
||||
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author
|
||||
BlockNumber: new(big.Int).Set(header.Number),
|
||||
Time: new(big.Int).Set(header.Time),
|
||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
GasLimit: new(big.Int).Set(header.GasLimit),
|
||||
GasLimit: header.GasLimit,
|
||||
GasPrice: new(big.Int).Set(msg.GasPrice()),
|
||||
}
|
||||
}
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var BlockReward = big.NewInt(5e+18)
|
||||
+21
-13
@@ -16,31 +16,39 @@
|
||||
|
||||
package core
|
||||
|
||||
import "math/big"
|
||||
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 big.Int
|
||||
// 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 *big.Int) *GasPool {
|
||||
i := (*big.Int)(gp)
|
||||
i.Add(i, amount)
|
||||
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 *big.Int) error {
|
||||
i := (*big.Int)(gp)
|
||||
if i.Cmp(amount) < 0 {
|
||||
func (gp *GasPool) SubGas(amount uint64) error {
|
||||
if uint64(*gp) < amount {
|
||||
return ErrGasLimitReached
|
||||
}
|
||||
i.Sub(i, amount)
|
||||
*(*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 (*big.Int)(gp).String()
|
||||
return fmt.Sprintf("%d", *gp)
|
||||
}
|
||||
|
||||
+4
-2
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var _ = (*genesisSpecMarshaling)(nil)
|
||||
|
||||
func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
@@ -54,7 +56,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData hexutil.Bytes `json:"extraData"`
|
||||
ExtraData *hexutil.Bytes `json:"extraData"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash *common.Hash `json:"mixHash"`
|
||||
@@ -78,7 +80,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||
g.Timestamp = uint64(*dec.Timestamp)
|
||||
}
|
||||
if dec.ExtraData != nil {
|
||||
g.ExtraData = dec.ExtraData
|
||||
g.ExtraData = *dec.ExtraData
|
||||
}
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gasLimit' for Genesis")
|
||||
|
||||
+4
-4
@@ -38,18 +38,18 @@ func (g GenesisAccount) MarshalJSON() ([]byte, error) {
|
||||
|
||||
func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
|
||||
type GenesisAccount struct {
|
||||
Code hexutil.Bytes `json:"code,omitempty"`
|
||||
Code *hexutil.Bytes `json:"code,omitempty"`
|
||||
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
||||
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||
PrivateKey hexutil.Bytes `json:"secretKey,omitempty"`
|
||||
PrivateKey *hexutil.Bytes `json:"secretKey,omitempty"`
|
||||
}
|
||||
var dec GenesisAccount
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Code != nil {
|
||||
g.Code = dec.Code
|
||||
g.Code = *dec.Code
|
||||
}
|
||||
if dec.Storage != nil {
|
||||
g.Storage = make(map[common.Hash]common.Hash, len(dec.Storage))
|
||||
@@ -65,7 +65,7 @@ func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
|
||||
g.Nonce = uint64(*dec.Nonce)
|
||||
}
|
||||
if dec.PrivateKey != nil {
|
||||
g.PrivateKey = dec.PrivateKey
|
||||
g.PrivateKey = *dec.PrivateKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+15
-13
@@ -169,10 +169,9 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
|
||||
|
||||
// Check whether the genesis block is already written.
|
||||
if genesis != nil {
|
||||
block, _ := genesis.ToBlock()
|
||||
hash := block.Hash()
|
||||
hash := genesis.ToBlock(nil).Hash()
|
||||
if hash != stored {
|
||||
return genesis.Config, block.Hash(), &GenesisMismatchError{stored, hash}
|
||||
return genesis.Config, hash, &GenesisMismatchError{stored, hash}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,9 +219,12 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// ToBlock creates the block and state of a genesis specification.
|
||||
func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
// 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)
|
||||
@@ -239,8 +241,8 @@ func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
|
||||
Time: new(big.Int).SetUint64(g.Timestamp),
|
||||
ParentHash: g.ParentHash,
|
||||
Extra: g.ExtraData,
|
||||
GasLimit: new(big.Int).SetUint64(g.GasLimit),
|
||||
GasUsed: new(big.Int).SetUint64(g.GasUsed),
|
||||
GasLimit: g.GasLimit,
|
||||
GasUsed: g.GasUsed,
|
||||
Difficulty: g.Difficulty,
|
||||
MixDigest: g.Mixhash,
|
||||
Coinbase: g.Coinbase,
|
||||
@@ -252,19 +254,19 @@ func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
|
||||
if g.Difficulty == nil {
|
||||
head.Difficulty = params.GenesisDifficulty
|
||||
}
|
||||
return types.NewBlock(head, nil, nil, nil), statedb
|
||||
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, statedb := g.ToBlock()
|
||||
block := g.ToBlock(db)
|
||||
if block.Number().Sign() != 0 {
|
||||
return nil, fmt.Errorf("can't commit genesis block with number > 0")
|
||||
}
|
||||
if _, err := statedb.CommitTo(db, false); err != nil {
|
||||
return nil, fmt.Errorf("cannot write state: %v", err)
|
||||
}
|
||||
if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+1
File diff suppressed because one or more lines are too long
+7
-5
@@ -30,11 +30,11 @@ import (
|
||||
)
|
||||
|
||||
func TestDefaultGenesisBlock(t *testing.T) {
|
||||
block, _ := DefaultGenesisBlock().ToBlock()
|
||||
block := DefaultGenesisBlock().ToBlock(nil)
|
||||
if block.Hash() != params.MainnetGenesisHash {
|
||||
t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash)
|
||||
}
|
||||
block, _ = DefaultTestnetGenesisBlock().ToBlock()
|
||||
block = DefaultTestnetGenesisBlock().ToBlock(nil)
|
||||
if block.Hash() != params.TestnetGenesisHash {
|
||||
t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestnetGenesisHash)
|
||||
}
|
||||
@@ -118,10 +118,12 @@ func TestSetupGenesis(t *testing.T) {
|
||||
// Commit the 'old' genesis block with Homestead transition at #2.
|
||||
// Advance to block #4, past the homestead transition block of customg.
|
||||
genesis := oldcustomg.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
|
||||
|
||||
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
bc.SetValidator(bproc{})
|
||||
bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))
|
||||
|
||||
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)
|
||||
bc.InsertChain(blocks)
|
||||
bc.CurrentBlock()
|
||||
// This should return a compatibility error.
|
||||
return SetupGenesisBlock(db, &customg)
|
||||
|
||||
+21
-18
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -51,8 +52,8 @@ type HeaderChain struct {
|
||||
chainDb ethdb.Database
|
||||
genesisHeader *types.Header
|
||||
|
||||
currentHeader *types.Header // 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)
|
||||
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
|
||||
@@ -95,13 +96,13 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
|
||||
return nil, ErrNoGenesis
|
||||
}
|
||||
|
||||
hc.currentHeader = hc.genesisHeader
|
||||
hc.currentHeader.Store(hc.genesisHeader)
|
||||
if head := GetHeadBlockHash(chainDb); head != (common.Hash{}) {
|
||||
if chead := hc.GetHeaderByHash(head); chead != nil {
|
||||
hc.currentHeader = chead
|
||||
hc.currentHeader.Store(chead)
|
||||
}
|
||||
}
|
||||
hc.currentHeaderHash = hc.currentHeader.Hash()
|
||||
hc.currentHeaderHash = hc.CurrentHeader().Hash()
|
||||
|
||||
return hc, nil
|
||||
}
|
||||
@@ -139,7 +140,7 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||
if ptd == nil {
|
||||
return NonStatTy, consensus.ErrUnknownAncestor
|
||||
}
|
||||
localTd := hc.GetTd(hc.currentHeaderHash, hc.currentHeader.Number.Uint64())
|
||||
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
|
||||
@@ -181,7 +182,8 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
|
||||
log.Crit("Failed to insert head header hash", "err", err)
|
||||
}
|
||||
hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header)
|
||||
hc.currentHeaderHash = hash
|
||||
hc.currentHeader.Store(types.CopyHeader(header))
|
||||
|
||||
status = CanonStatTy
|
||||
} else {
|
||||
@@ -383,7 +385,7 @@ func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||
// 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
|
||||
return hc.currentHeader.Load().(*types.Header)
|
||||
}
|
||||
|
||||
// SetCurrentHeader sets the current head header of the canonical chain.
|
||||
@@ -391,7 +393,7 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
|
||||
if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil {
|
||||
log.Crit("Failed to insert head header hash", "err", err)
|
||||
}
|
||||
hc.currentHeader = head
|
||||
hc.currentHeader.Store(head)
|
||||
hc.currentHeaderHash = head.Hash()
|
||||
}
|
||||
|
||||
@@ -403,19 +405,20 @@ type DeleteCallback func(common.Hash, uint64)
|
||||
// will be deleted and the new one set.
|
||||
func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
|
||||
height := uint64(0)
|
||||
if hc.currentHeader != nil {
|
||||
height = hc.currentHeader.Number.Uint64()
|
||||
|
||||
if hdr := hc.CurrentHeader(); hdr != nil {
|
||||
height = hdr.Number.Uint64()
|
||||
}
|
||||
|
||||
for hc.currentHeader != nil && hc.currentHeader.Number.Uint64() > head {
|
||||
hash := hc.currentHeader.Hash()
|
||||
num := hc.currentHeader.Number.Uint64()
|
||||
for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() {
|
||||
hash := hdr.Hash()
|
||||
num := hdr.Number.Uint64()
|
||||
if delFn != nil {
|
||||
delFn(hash, num)
|
||||
}
|
||||
DeleteHeader(hc.chainDb, hash, num)
|
||||
DeleteTd(hc.chainDb, hash, num)
|
||||
hc.currentHeader = hc.GetHeader(hc.currentHeader.ParentHash, hc.currentHeader.Number.Uint64()-1)
|
||||
hc.currentHeader.Store(hc.GetHeader(hdr.ParentHash, hdr.Number.Uint64()-1))
|
||||
}
|
||||
// Roll back the canonical chain numbering
|
||||
for i := height; i > head; i-- {
|
||||
@@ -426,10 +429,10 @@ func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
|
||||
hc.tdCache.Purge()
|
||||
hc.numberCache.Purge()
|
||||
|
||||
if hc.currentHeader == nil {
|
||||
hc.currentHeader = hc.genesisHeader
|
||||
if hc.CurrentHeader() == nil {
|
||||
hc.currentHeader.Store(hc.genesisHeader)
|
||||
}
|
||||
hc.currentHeaderHash = hc.currentHeader.Hash()
|
||||
hc.currentHeaderHash = hc.CurrentHeader().Hash()
|
||||
|
||||
if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil {
|
||||
log.Crit("Failed to reset head header hash", "err", err)
|
||||
|
||||
+39
-12
@@ -40,16 +40,23 @@ const (
|
||||
|
||||
// Database wraps access to tries and contract code.
|
||||
type Database interface {
|
||||
// Accessing tries:
|
||||
// OpenTrie opens the main account trie.
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
OpenTrie(root common.Hash) (Trie, error)
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
|
||||
// Accessing contract code:
|
||||
ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
|
||||
ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
|
||||
|
||||
// CopyTrie returns an independent copy of the given trie.
|
||||
CopyTrie(Trie) Trie
|
||||
|
||||
// ContractCode retrieves a particular contract's code.
|
||||
ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
|
||||
|
||||
// ContractCodeSize retrieves a particular contracts code's size.
|
||||
ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
|
||||
|
||||
// TrieDB retrieves the low level trie database used for data storage.
|
||||
TrieDB() *trie.Database
|
||||
}
|
||||
|
||||
// Trie is a Ethereum Merkle Trie.
|
||||
@@ -57,26 +64,33 @@ type Trie interface {
|
||||
TryGet(key []byte) ([]byte, error)
|
||||
TryUpdate(key, value []byte) error
|
||||
TryDelete(key []byte) error
|
||||
CommitTo(trie.DatabaseWriter) (common.Hash, error)
|
||||
Commit(onleaf trie.LeafCallback) (common.Hash, error)
|
||||
Hash() common.Hash
|
||||
NodeIterator(startKey []byte) trie.NodeIterator
|
||||
GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed
|
||||
Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error
|
||||
}
|
||||
|
||||
// NewDatabase creates a backing store for state. The returned database is safe for
|
||||
// concurrent use and retains cached trie nodes in memory.
|
||||
// concurrent use and retains cached trie nodes in memory. The pool is an optional
|
||||
// intermediate trie-node memory pool between the low level storage layer and the
|
||||
// high level trie abstraction.
|
||||
func NewDatabase(db ethdb.Database) Database {
|
||||
csc, _ := lru.New(codeSizeCacheSize)
|
||||
return &cachingDB{db: db, codeSizeCache: csc}
|
||||
return &cachingDB{
|
||||
db: trie.NewDatabase(db),
|
||||
codeSizeCache: csc,
|
||||
}
|
||||
}
|
||||
|
||||
type cachingDB struct {
|
||||
db ethdb.Database
|
||||
db *trie.Database
|
||||
mu sync.Mutex
|
||||
pastTries []*trie.SecureTrie
|
||||
codeSizeCache *lru.Cache
|
||||
}
|
||||
|
||||
// OpenTrie opens the main account trie.
|
||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
db.mu.Lock()
|
||||
defer db.mu.Unlock()
|
||||
@@ -105,10 +119,12 @@ func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
|
||||
}
|
||||
}
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
|
||||
return trie.NewSecure(root, db.db, 0)
|
||||
}
|
||||
|
||||
// CopyTrie returns an independent copy of the given trie.
|
||||
func (db *cachingDB) CopyTrie(t Trie) Trie {
|
||||
switch t := t.(type) {
|
||||
case cachedTrie:
|
||||
@@ -120,14 +136,16 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
|
||||
}
|
||||
}
|
||||
|
||||
// ContractCode retrieves a particular contract's code.
|
||||
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
|
||||
code, err := db.db.Get(codeHash[:])
|
||||
code, err := db.db.Node(codeHash)
|
||||
if err == nil {
|
||||
db.codeSizeCache.Add(codeHash, len(code))
|
||||
}
|
||||
return code, err
|
||||
}
|
||||
|
||||
// ContractCodeSize retrieves a particular contracts code's size.
|
||||
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
|
||||
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
|
||||
return cached.(int), nil
|
||||
@@ -139,16 +157,25 @@ func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, erro
|
||||
return len(code), err
|
||||
}
|
||||
|
||||
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||
func (db *cachingDB) TrieDB() *trie.Database {
|
||||
return db.db
|
||||
}
|
||||
|
||||
// cachedTrie inserts its trie into a cachingDB on commit.
|
||||
type cachedTrie struct {
|
||||
*trie.SecureTrie
|
||||
db *cachingDB
|
||||
}
|
||||
|
||||
func (m cachedTrie) CommitTo(dbw trie.DatabaseWriter) (common.Hash, error) {
|
||||
root, err := m.SecureTrie.CommitTo(dbw)
|
||||
func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) {
|
||||
root, err := m.SecureTrie.Commit(onleaf)
|
||||
if err == nil {
|
||||
m.db.pushTrie(m.SecureTrie)
|
||||
}
|
||||
return root, err
|
||||
}
|
||||
|
||||
func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
|
||||
return m.SecureTrie.Prove(key, fromLevel, proofDb)
|
||||
}
|
||||
|
||||
+11
-6
@@ -21,12 +21,13 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
// Tests that the node iterator indeed walks over the entire database contents.
|
||||
func TestNodeIteratorCoverage(t *testing.T) {
|
||||
// Create some arbitrary test state to iterate
|
||||
db, mem, root, _ := makeTestState()
|
||||
db, root, _ := makeTestState()
|
||||
|
||||
state, err := New(root, db)
|
||||
if err != nil {
|
||||
@@ -39,14 +40,18 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||
hashes[it.Hash] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Cross check the hashes and the database itself
|
||||
// Cross check the iterated hashes and the database/nodepool content
|
||||
for hash := range hashes {
|
||||
if _, err := mem.Get(hash.Bytes()); err != nil {
|
||||
t.Errorf("failed to retrieve reported node %x: %v", hash, err)
|
||||
if _, err := db.TrieDB().Node(hash); err != nil {
|
||||
t.Errorf("failed to retrieve reported node %x", hash)
|
||||
}
|
||||
}
|
||||
for _, key := range mem.Keys() {
|
||||
for _, hash := range db.TrieDB().Nodes() {
|
||||
if _, ok := hashes[hash]; !ok {
|
||||
t.Errorf("state entry not reported %x", hash)
|
||||
}
|
||||
}
|
||||
for _, key := range db.TrieDB().DiskDB().(*ethdb.MemDatabase).Keys() {
|
||||
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
||||
continue
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ type (
|
||||
|
||||
// Changes to other state values.
|
||||
refundChange struct {
|
||||
prev *big.Int
|
||||
prev uint64
|
||||
}
|
||||
addLogChange struct {
|
||||
txhash common.Hash
|
||||
|
||||
+2
-3
@@ -25,7 +25,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
var emptyCodeHash = crypto.Keccak256(nil)
|
||||
@@ -238,12 +237,12 @@ func (self *stateObject) updateRoot(db Database) {
|
||||
|
||||
// CommitTrie the storage trie of the object to dwb.
|
||||
// This updates the trie root.
|
||||
func (self *stateObject) CommitTrie(db Database, dbw trie.DatabaseWriter) error {
|
||||
func (self *stateObject) CommitTrie(db Database) error {
|
||||
self.updateTrie(db)
|
||||
if self.dbErr != nil {
|
||||
return self.dbErr
|
||||
}
|
||||
root, err := self.trie.CommitTo(dbw)
|
||||
root, err := self.trie.Commit(nil)
|
||||
if err == nil {
|
||||
self.data.Root = root
|
||||
}
|
||||
|
||||
+3
-3
@@ -48,7 +48,7 @@ func (s *StateSuite) TestDump(c *checker.C) {
|
||||
// write some of them to the trie
|
||||
s.state.updateStateObject(obj1)
|
||||
s.state.updateStateObject(obj2)
|
||||
s.state.CommitTo(s.db, false)
|
||||
s.state.Commit(false)
|
||||
|
||||
// check that dump contains the state objects that are in trie
|
||||
got := string(s.state.Dump())
|
||||
@@ -97,7 +97,7 @@ func (s *StateSuite) TestNull(c *checker.C) {
|
||||
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
||||
var value common.Hash
|
||||
s.state.SetState(address, common.Hash{}, value)
|
||||
s.state.CommitTo(s.db, false)
|
||||
s.state.Commit(false)
|
||||
value = s.state.GetState(address, common.Hash{})
|
||||
if !common.EmptyHash(value) {
|
||||
c.Errorf("expected empty hash. got %x", value)
|
||||
@@ -155,7 +155,7 @@ func TestSnapshot2(t *testing.T) {
|
||||
so0.deleted = false
|
||||
state.setStateObject(so0)
|
||||
|
||||
root, _ := state.CommitTo(db, false)
|
||||
root, _ := state.Commit(false)
|
||||
state.Reset(root)
|
||||
|
||||
// and one with deleted == true
|
||||
|
||||
+39
-18
@@ -36,6 +36,14 @@ type revision struct {
|
||||
journalIndex int
|
||||
}
|
||||
|
||||
var (
|
||||
// emptyState is the known hash of an empty state trie entry.
|
||||
emptyState = crypto.Keccak256Hash(nil)
|
||||
|
||||
// emptyCode is the known hash of the empty EVM bytecode.
|
||||
emptyCode = crypto.Keccak256Hash(nil)
|
||||
)
|
||||
|
||||
// StateDBs within the ethereum protocol are used to store anything
|
||||
// within the merkle trie. StateDBs take care of caching and storing
|
||||
// nested states. It's the general query interface to retrieve:
|
||||
@@ -57,7 +65,7 @@ type StateDB struct {
|
||||
dbErr error
|
||||
|
||||
// The refund counter, also used by state transitioning.
|
||||
refund *big.Int
|
||||
refund uint64
|
||||
|
||||
thash, bhash common.Hash
|
||||
txIndex int
|
||||
@@ -86,7 +94,6 @@ func New(root common.Hash, db Database) (*StateDB, error) {
|
||||
trie: tr,
|
||||
stateObjects: make(map[common.Address]*stateObject),
|
||||
stateObjectsDirty: make(map[common.Address]struct{}),
|
||||
refund: new(big.Int),
|
||||
logs: make(map[common.Hash][]*types.Log),
|
||||
preimages: make(map[common.Hash][]byte),
|
||||
}, nil
|
||||
@@ -161,9 +168,9 @@ func (self *StateDB) Preimages() map[common.Hash][]byte {
|
||||
return self.preimages
|
||||
}
|
||||
|
||||
func (self *StateDB) AddRefund(gas *big.Int) {
|
||||
self.journal = append(self.journal, refundChange{prev: new(big.Int).Set(self.refund)})
|
||||
self.refund.Add(self.refund, gas)
|
||||
func (self *StateDB) AddRefund(gas uint64) {
|
||||
self.journal = append(self.journal, refundChange{prev: self.refund})
|
||||
self.refund += gas
|
||||
}
|
||||
|
||||
// Exist reports whether the given account address exists in the state.
|
||||
@@ -236,6 +243,11 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
// Database retrieves the low level database supporting the lower level trie ops.
|
||||
func (self *StateDB) Database() Database {
|
||||
return self.db
|
||||
}
|
||||
|
||||
// StorageTrie returns the storage trie of an account.
|
||||
// The return value is a copy and is nil for non-existent accounts.
|
||||
func (self *StateDB) StorageTrie(a common.Address) Trie {
|
||||
@@ -453,10 +465,10 @@ func (self *StateDB) Copy() *StateDB {
|
||||
// Copy all the basic fields, initialize the memory ones
|
||||
state := &StateDB{
|
||||
db: self.db,
|
||||
trie: self.trie,
|
||||
trie: self.db.CopyTrie(self.trie),
|
||||
stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)),
|
||||
stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
|
||||
refund: new(big.Int).Set(self.refund),
|
||||
refund: self.refund,
|
||||
logs: make(map[common.Hash][]*types.Log, len(self.logs)),
|
||||
logSize: self.logSize,
|
||||
preimages: make(map[common.Hash][]byte),
|
||||
@@ -506,9 +518,7 @@ func (self *StateDB) RevertToSnapshot(revid int) {
|
||||
}
|
||||
|
||||
// GetRefund returns the current value of the refund counter.
|
||||
// The return value must not be modified by the caller and will become
|
||||
// invalid at the next call to AddRefund.
|
||||
func (self *StateDB) GetRefund() *big.Int {
|
||||
func (self *StateDB) GetRefund() uint64 {
|
||||
return self.refund
|
||||
}
|
||||
|
||||
@@ -568,11 +578,11 @@ func (s *StateDB) DeleteSuicides() {
|
||||
func (s *StateDB) clearJournalAndRefund() {
|
||||
s.journal = nil
|
||||
s.validRevisions = s.validRevisions[:0]
|
||||
s.refund = new(big.Int)
|
||||
s.refund = 0
|
||||
}
|
||||
|
||||
// CommitTo writes the state to the given database.
|
||||
func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) {
|
||||
// Commit writes the state to the underlying in-memory trie database.
|
||||
func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
|
||||
defer s.clearJournalAndRefund()
|
||||
|
||||
// Commit objects to the trie.
|
||||
@@ -586,13 +596,11 @@ func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (ro
|
||||
case isDirty:
|
||||
// Write any contract code associated with the state object
|
||||
if stateObject.code != nil && stateObject.dirtyCode {
|
||||
if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
s.db.TrieDB().Insert(common.BytesToHash(stateObject.CodeHash()), stateObject.code)
|
||||
stateObject.dirtyCode = false
|
||||
}
|
||||
// Write any storage changes in the state object to its storage trie.
|
||||
if err := stateObject.CommitTrie(s.db, dbw); err != nil {
|
||||
if err := stateObject.CommitTrie(s.db); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Update the object in the main account trie.
|
||||
@@ -601,7 +609,20 @@ func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (ro
|
||||
delete(s.stateObjectsDirty, addr)
|
||||
}
|
||||
// Write trie changes.
|
||||
root, err = s.trie.CommitTo(dbw)
|
||||
root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
|
||||
var account Account
|
||||
if err := rlp.DecodeBytes(leaf, &account); err != nil {
|
||||
return nil
|
||||
}
|
||||
if account.Root != emptyState {
|
||||
s.db.TrieDB().Reference(account.Root, parent)
|
||||
}
|
||||
code := common.BytesToHash(account.CodeHash)
|
||||
if code != emptyCode {
|
||||
s.db.TrieDB().Reference(code, parent)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
|
||||
return root, err
|
||||
}
|
||||
|
||||
+57
-8
@@ -97,10 +97,10 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
}
|
||||
|
||||
// Commit and cross check the databases.
|
||||
if _, err := transState.CommitTo(transDb, false); err != nil {
|
||||
if _, err := transState.Commit(false); err != nil {
|
||||
t.Fatalf("failed to commit transition state: %v", err)
|
||||
}
|
||||
if _, err := finalState.CommitTo(finalDb, false); err != nil {
|
||||
if _, err := finalState.Commit(false); err != nil {
|
||||
t.Fatalf("failed to commit final state: %v", err)
|
||||
}
|
||||
for _, key := range finalDb.Keys() {
|
||||
@@ -117,6 +117,57 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestCopy tests that copying a statedb object indeed makes the original and
|
||||
// the copy independent of each other. This test is a regression test against
|
||||
// https://github.com/ethereum/go-ethereum/pull/15549.
|
||||
func TestCopy(t *testing.T) {
|
||||
// Create a random state test to copy and modify "independently"
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
orig, _ := New(common.Hash{}, NewDatabase(db))
|
||||
|
||||
for i := byte(0); i < 255; i++ {
|
||||
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
obj.AddBalance(big.NewInt(int64(i)))
|
||||
orig.updateStateObject(obj)
|
||||
}
|
||||
orig.Finalise(false)
|
||||
|
||||
// Copy the state, modify both in-memory
|
||||
copy := orig.Copy()
|
||||
|
||||
for i := byte(0); i < 255; i++ {
|
||||
origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
|
||||
origObj.AddBalance(big.NewInt(2 * int64(i)))
|
||||
copyObj.AddBalance(big.NewInt(3 * int64(i)))
|
||||
|
||||
orig.updateStateObject(origObj)
|
||||
copy.updateStateObject(copyObj)
|
||||
}
|
||||
// Finalise the changes on both concurrently
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
orig.Finalise(true)
|
||||
close(done)
|
||||
}()
|
||||
copy.Finalise(true)
|
||||
<-done
|
||||
|
||||
// Verify that the two states have been updated independently
|
||||
for i := byte(0); i < 255; i++ {
|
||||
origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
|
||||
if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 {
|
||||
t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want)
|
||||
}
|
||||
if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 {
|
||||
t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshotRandom(t *testing.T) {
|
||||
config := &quick.Config{MaxCount: 1000}
|
||||
err := quick.Check((*snapshotTest).run, config)
|
||||
@@ -212,7 +263,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||
{
|
||||
name: "AddRefund",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.AddRefund(big.NewInt(a.args[0]))
|
||||
s.AddRefund(uint64(a.args[0]))
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
noAddr: true,
|
||||
@@ -295,11 +346,10 @@ func (test *snapshotTest) run() bool {
|
||||
}
|
||||
action.fn(action, state)
|
||||
}
|
||||
|
||||
// Revert all snapshots in reverse order. Each revert must yield a state
|
||||
// that is equivalent to fresh state with all actions up the snapshot applied.
|
||||
for sindex--; sindex >= 0; sindex-- {
|
||||
checkstate, _ := New(common.Hash{}, NewDatabase(db))
|
||||
checkstate, _ := New(common.Hash{}, state.Database())
|
||||
for _, action := range test.actions[:test.snapshots[sindex]] {
|
||||
action.fn(action, checkstate)
|
||||
}
|
||||
@@ -345,7 +395,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
}
|
||||
}
|
||||
|
||||
if state.GetRefund().Cmp(checkstate.GetRefund()) != 0 {
|
||||
if state.GetRefund() != checkstate.GetRefund() {
|
||||
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
|
||||
state.GetRefund(), checkstate.GetRefund())
|
||||
}
|
||||
@@ -358,7 +408,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
|
||||
func (s *StateSuite) TestTouchDelete(c *check.C) {
|
||||
s.state.GetOrNewStateObject(common.Address{})
|
||||
root, _ := s.state.CommitTo(s.db, false)
|
||||
root, _ := s.state.Commit(false)
|
||||
s.state.Reset(root)
|
||||
|
||||
snapshot := s.state.Snapshot()
|
||||
@@ -366,7 +416,6 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
|
||||
if len(s.state.stateObjectsDirty) != 1 {
|
||||
c.Fatal("expected one dirty state object")
|
||||
}
|
||||
|
||||
s.state.RevertToSnapshot(snapshot)
|
||||
if len(s.state.stateObjectsDirty) != 0 {
|
||||
c.Fatal("expected no dirty state object")
|
||||
|
||||
+22
-22
@@ -36,10 +36,10 @@ type testAccount struct {
|
||||
}
|
||||
|
||||
// makeTestState create a sample test state to test node-wise reconstruction.
|
||||
func makeTestState() (Database, *ethdb.MemDatabase, common.Hash, []*testAccount) {
|
||||
func makeTestState() (Database, common.Hash, []*testAccount) {
|
||||
// Create an empty state
|
||||
mem, _ := ethdb.NewMemDatabase()
|
||||
db := NewDatabase(mem)
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
db := NewDatabase(diskdb)
|
||||
state, _ := New(common.Hash{}, db)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
@@ -61,10 +61,10 @@ func makeTestState() (Database, *ethdb.MemDatabase, common.Hash, []*testAccount)
|
||||
state.updateStateObject(obj)
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
root, _ := state.CommitTo(mem, false)
|
||||
root, _ := state.Commit(false)
|
||||
|
||||
// Return the generated state
|
||||
return db, mem, root, accounts
|
||||
return db, root, accounts
|
||||
}
|
||||
|
||||
// checkStateAccounts cross references a reconstructed state with an expected
|
||||
@@ -96,7 +96,7 @@ func checkTrieConsistency(db ethdb.Database, root common.Hash) error {
|
||||
if v, _ := db.Get(root[:]); v == nil {
|
||||
return nil // Consider a non existent state consistent.
|
||||
}
|
||||
trie, err := trie.New(root, db)
|
||||
trie, err := trie.New(root, trie.NewDatabase(db))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -138,7 +138,7 @@ func TestIterativeStateSyncBatched(t *testing.T) { testIterativeStateSync(t,
|
||||
|
||||
func testIterativeStateSync(t *testing.T, batch int) {
|
||||
// Create a random state to copy
|
||||
_, srcMem, srcRoot, srcAccounts := makeTestState()
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
@@ -148,9 +148,9 @@ func testIterativeStateSync(t *testing.T, batch int) {
|
||||
for len(queue) > 0 {
|
||||
results := make([]trie.SyncResult, len(queue))
|
||||
for i, hash := range queue {
|
||||
data, err := srcMem.Get(hash.Bytes())
|
||||
data, err := srcDb.TrieDB().Node(hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
results[i] = trie.SyncResult{Hash: hash, Data: data}
|
||||
}
|
||||
@@ -170,7 +170,7 @@ func testIterativeStateSync(t *testing.T, batch int) {
|
||||
// partial results are returned, and the others sent only later.
|
||||
func TestIterativeDelayedStateSync(t *testing.T) {
|
||||
// Create a random state to copy
|
||||
_, srcMem, srcRoot, srcAccounts := makeTestState()
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
@@ -181,9 +181,9 @@ func TestIterativeDelayedStateSync(t *testing.T) {
|
||||
// Sync only half of the scheduled nodes
|
||||
results := make([]trie.SyncResult, len(queue)/2+1)
|
||||
for i, hash := range queue[:len(results)] {
|
||||
data, err := srcMem.Get(hash.Bytes())
|
||||
data, err := srcDb.TrieDB().Node(hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
results[i] = trie.SyncResult{Hash: hash, Data: data}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ func TestIterativeRandomStateSyncBatched(t *testing.T) { testIterativeRandomS
|
||||
|
||||
func testIterativeRandomStateSync(t *testing.T, batch int) {
|
||||
// Create a random state to copy
|
||||
_, srcMem, srcRoot, srcAccounts := makeTestState()
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
@@ -221,9 +221,9 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
|
||||
// Fetch all the queued nodes in a random order
|
||||
results := make([]trie.SyncResult, 0, len(queue))
|
||||
for hash := range queue {
|
||||
data, err := srcMem.Get(hash.Bytes())
|
||||
data, err := srcDb.TrieDB().Node(hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
results = append(results, trie.SyncResult{Hash: hash, Data: data})
|
||||
}
|
||||
@@ -247,7 +247,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
|
||||
// partial results are returned (Even those randomly), others sent only later.
|
||||
func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||
// Create a random state to copy
|
||||
_, srcMem, srcRoot, srcAccounts := makeTestState()
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
@@ -263,9 +263,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||
for hash := range queue {
|
||||
delete(queue, hash)
|
||||
|
||||
data, err := srcMem.Get(hash.Bytes())
|
||||
data, err := srcDb.TrieDB().Node(hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
results = append(results, trie.SyncResult{Hash: hash, Data: data})
|
||||
|
||||
@@ -292,9 +292,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||
// the database.
|
||||
func TestIncompleteStateSync(t *testing.T) {
|
||||
// Create a random state to copy
|
||||
_, srcMem, srcRoot, srcAccounts := makeTestState()
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
checkTrieConsistency(srcMem, srcRoot)
|
||||
checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb, _ := ethdb.NewMemDatabase()
|
||||
@@ -306,9 +306,9 @@ func TestIncompleteStateSync(t *testing.T) {
|
||||
// Fetch a batch of state nodes
|
||||
results := make([]trie.SyncResult, len(queue))
|
||||
for i, hash := range queue {
|
||||
data, err := srcMem.Get(hash.Bytes())
|
||||
data, err := srcDb.TrieDB().Node(hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", hash, err)
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
results[i] = trie.SyncResult{Hash: hash, Data: data}
|
||||
}
|
||||
|
||||
+15
-19
@@ -17,8 +17,6 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
@@ -55,13 +53,13 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
|
||||
// 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, *big.Int, error) {
|
||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
|
||||
var (
|
||||
receipts types.Receipts
|
||||
totalUsedGas = big.NewInt(0)
|
||||
header = block.Header()
|
||||
allLogs []*types.Log
|
||||
gp = new(GasPool).AddGas(block.GasLimit())
|
||||
receipts types.Receipts
|
||||
usedGas = new(uint64)
|
||||
header = block.Header()
|
||||
allLogs []*types.Log
|
||||
gp = new(GasPool).AddGas(block.GasLimit())
|
||||
)
|
||||
// Mutate the 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 {
|
||||
@@ -70,9 +68,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
// 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, totalUsedGas, cfg)
|
||||
receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
receipts = append(receipts, receipt)
|
||||
allLogs = append(allLogs, receipt.Logs...)
|
||||
@@ -80,17 +78,17 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
// 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, totalUsedGas, nil
|
||||
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 *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) {
|
||||
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, 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, nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
// Create a new context to be used in the EVM environment
|
||||
context := NewEVMContext(msg, header, bc, author)
|
||||
@@ -100,9 +98,8 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
|
||||
// Apply the transaction to the current state (included in the env)
|
||||
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Update the state with pending changes
|
||||
var root []byte
|
||||
if config.IsByzantium(header.Number) {
|
||||
@@ -110,18 +107,17 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
|
||||
} else {
|
||||
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
||||
}
|
||||
usedGas.Add(usedGas, gas)
|
||||
*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 wether the root touch-delete accounts.
|
||||
receipt := types.NewReceipt(root, failed, usedGas)
|
||||
receipt := types.NewReceipt(root, failed, *usedGas)
|
||||
receipt.TxHash = tx.Hash()
|
||||
receipt.GasUsed = new(big.Int).Set(gas)
|
||||
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})
|
||||
|
||||
+62
-70
@@ -18,17 +18,16 @@ package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
Big0 = big.NewInt(0)
|
||||
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
|
||||
)
|
||||
|
||||
@@ -54,7 +53,7 @@ type StateTransition struct {
|
||||
msg Message
|
||||
gas uint64
|
||||
gasPrice *big.Int
|
||||
initialGas *big.Int
|
||||
initialGas uint64
|
||||
value *big.Int
|
||||
data []byte
|
||||
state vm.StateDB
|
||||
@@ -68,7 +67,7 @@ type Message interface {
|
||||
To() *common.Address
|
||||
|
||||
GasPrice() *big.Int
|
||||
Gas() *big.Int
|
||||
Gas() uint64
|
||||
Value() *big.Int
|
||||
|
||||
Nonce() uint64
|
||||
@@ -76,45 +75,49 @@ type Message interface {
|
||||
Data() []byte
|
||||
}
|
||||
|
||||
// IntrinsicGas computes the 'intrinsic gas' for a message
|
||||
// with the given data.
|
||||
//
|
||||
// TODO convert to uint64
|
||||
func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
|
||||
igas := new(big.Int)
|
||||
// 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 {
|
||||
igas.SetUint64(params.TxGasContractCreation)
|
||||
gas = params.TxGasContractCreation
|
||||
} else {
|
||||
igas.SetUint64(params.TxGas)
|
||||
gas = params.TxGas
|
||||
}
|
||||
// Bump the required gas by the amount of transactional data
|
||||
if len(data) > 0 {
|
||||
var nz int64
|
||||
// Zero and non-zero bytes are priced differently
|
||||
var nz uint64
|
||||
for _, byt := range data {
|
||||
if byt != 0 {
|
||||
nz++
|
||||
}
|
||||
}
|
||||
m := big.NewInt(nz)
|
||||
m.Mul(m, new(big.Int).SetUint64(params.TxDataNonZeroGas))
|
||||
igas.Add(igas, m)
|
||||
m.SetInt64(int64(len(data)) - nz)
|
||||
m.Mul(m, new(big.Int).SetUint64(params.TxDataZeroGas))
|
||||
igas.Add(igas, m)
|
||||
// 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 igas
|
||||
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(),
|
||||
initialGas: new(big.Int),
|
||||
value: msg.Value(),
|
||||
data: msg.Data(),
|
||||
state: evm.StateDB,
|
||||
gp: gp,
|
||||
evm: evm,
|
||||
msg: msg,
|
||||
gasPrice: msg.GasPrice(),
|
||||
value: msg.Value(),
|
||||
data: msg.Data(),
|
||||
state: evm.StateDB,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,11 +128,8 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
|
||||
// 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, *big.Int, bool, error) {
|
||||
st := NewStateTransition(evm, msg, gp)
|
||||
|
||||
ret, _, gasUsed, failed, err := st.TransitionDb()
|
||||
return ret, gasUsed, failed, err
|
||||
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) {
|
||||
return NewStateTransition(evm, msg, gp).TransitionDb()
|
||||
}
|
||||
|
||||
func (st *StateTransition) from() vm.AccountRef {
|
||||
@@ -166,26 +166,20 @@ func (st *StateTransition) useGas(amount uint64) error {
|
||||
}
|
||||
|
||||
func (st *StateTransition) buyGas() error {
|
||||
mgas := st.msg.Gas()
|
||||
if mgas.BitLen() > 64 {
|
||||
return vm.ErrOutOfGas
|
||||
}
|
||||
|
||||
mgval := new(big.Int).Mul(mgas, st.gasPrice)
|
||||
|
||||
var (
|
||||
state = st.state
|
||||
sender = st.from()
|
||||
)
|
||||
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
|
||||
if state.GetBalance(sender.Address()).Cmp(mgval) < 0 {
|
||||
return errInsufficientBalanceForGas
|
||||
}
|
||||
if err := st.gp.SubGas(mgas); err != nil {
|
||||
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
|
||||
return err
|
||||
}
|
||||
st.gas += mgas.Uint64()
|
||||
st.gas += st.msg.Gas()
|
||||
|
||||
st.initialGas.Set(mgas)
|
||||
st.initialGas = st.msg.Gas()
|
||||
state.SubBalance(sender.Address(), mgval)
|
||||
return nil
|
||||
}
|
||||
@@ -206,10 +200,10 @@ func (st *StateTransition) preCheck() error {
|
||||
return st.buyGas()
|
||||
}
|
||||
|
||||
// TransitionDb will transition the state by applying the current message and returning the result
|
||||
// including the required gas for the operation as well as the used gas. It returns an error if it
|
||||
// TransitionDb will transition the state by applying the current message and
|
||||
// returning the result including the the used gas. It returns an error if it
|
||||
// failed. An error indicates a consensus issue.
|
||||
func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, failed bool, err error) {
|
||||
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
|
||||
if err = st.preCheck(); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -220,13 +214,12 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
|
||||
contractCreation := msg.To() == nil
|
||||
|
||||
// Pay intrinsic gas
|
||||
// TODO convert to uint64
|
||||
intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead)
|
||||
if intrinsicGas.BitLen() > 64 {
|
||||
return nil, nil, nil, false, vm.ErrOutOfGas
|
||||
gas, err := IntrinsicGas(st.data, contractCreation, homestead)
|
||||
if err != nil {
|
||||
return nil, 0, false, err
|
||||
}
|
||||
if err = st.useGas(intrinsicGas.Uint64()); err != nil {
|
||||
return nil, nil, nil, false, err
|
||||
if err = st.useGas(gas); err != nil {
|
||||
return nil, 0, false, err
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -249,36 +242,35 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
|
||||
// sufficient balance to make the transfer happen. The first
|
||||
// balance transfer may never fail.
|
||||
if vmerr == vm.ErrInsufficientBalance {
|
||||
return nil, nil, nil, false, vmerr
|
||||
return nil, 0, false, vmerr
|
||||
}
|
||||
}
|
||||
requiredGas = new(big.Int).Set(st.gasUsed())
|
||||
|
||||
st.refundGas()
|
||||
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice))
|
||||
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
||||
|
||||
return ret, requiredGas, st.gasUsed(), vmerr != nil, err
|
||||
return ret, st.gasUsed(), vmerr != nil, err
|
||||
}
|
||||
|
||||
func (st *StateTransition) refundGas() {
|
||||
// Return eth for remaining gas to the sender account,
|
||||
// exchanged at the original rate.
|
||||
sender := st.from() // err already checked
|
||||
// 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.
|
||||
sender := st.from()
|
||||
|
||||
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
|
||||
st.state.AddBalance(sender.Address(), remaining)
|
||||
|
||||
// Apply refund counter, capped to half of the used gas.
|
||||
uhalf := remaining.Div(st.gasUsed(), common.Big2)
|
||||
refund := math.BigMin(uhalf, st.state.GetRefund())
|
||||
st.gas += refund.Uint64()
|
||||
|
||||
st.state.AddBalance(sender.Address(), refund.Mul(refund, st.gasPrice))
|
||||
|
||||
// Also return remaining gas to the block gas counter so it is
|
||||
// available for the next transaction.
|
||||
st.gp.AddGas(new(big.Int).SetUint64(st.gas))
|
||||
st.gp.AddGas(st.gas)
|
||||
}
|
||||
|
||||
func (st *StateTransition) gasUsed() *big.Int {
|
||||
return new(big.Int).Sub(st.initialGas, new(big.Int).SetUint64(st.gas))
|
||||
// gasUsed returns the amount of gas used up by the state transition.
|
||||
func (st *StateTransition) gasUsed() uint64 {
|
||||
return st.initialGas - st.gas
|
||||
}
|
||||
|
||||
+6
-7
@@ -224,7 +224,7 @@ type txList struct {
|
||||
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 *big.Int // Gas limit of the highest spending transaction (reset only if exceeds block limit)
|
||||
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,
|
||||
@@ -234,7 +234,6 @@ func newTxList(strict bool) *txList {
|
||||
strict: strict,
|
||||
txs: newTxSortedMap(),
|
||||
costcap: new(big.Int),
|
||||
gascap: new(big.Int),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +265,7 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
|
||||
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
|
||||
l.costcap = cost
|
||||
}
|
||||
if gas := tx.Gas(); l.gascap.Cmp(gas) < 0 {
|
||||
if gas := tx.Gas(); l.gascap < gas {
|
||||
l.gascap = gas
|
||||
}
|
||||
return true, old
|
||||
@@ -288,16 +287,16 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
|
||||
// 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, gasLimit *big.Int) (types.Transactions, types.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.Cmp(gasLimit) <= 0 {
|
||||
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 = new(big.Int).Set(gasLimit)
|
||||
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().Cmp(gasLimit) > 0 })
|
||||
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
|
||||
|
||||
+1
-2
@@ -17,7 +17,6 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
@@ -33,7 +32,7 @@ func TestStrictTxListAdd(t *testing.T) {
|
||||
|
||||
txs := make(types.Transactions, 1024)
|
||||
for i := 0; i < len(txs); i++ {
|
||||
txs[i] = transaction(uint64(i), new(big.Int), key)
|
||||
txs[i] = transaction(uint64(i), 0, key)
|
||||
}
|
||||
// Insert the transactions in a random order
|
||||
list := newTxList(true)
|
||||
|
||||
+22
-19
@@ -87,23 +87,23 @@ var (
|
||||
|
||||
var (
|
||||
// Metrics for the pending pool
|
||||
pendingDiscardCounter = metrics.NewCounter("txpool/pending/discard")
|
||||
pendingReplaceCounter = metrics.NewCounter("txpool/pending/replace")
|
||||
pendingRateLimitCounter = metrics.NewCounter("txpool/pending/ratelimit") // Dropped due to rate limiting
|
||||
pendingNofundsCounter = metrics.NewCounter("txpool/pending/nofunds") // Dropped due to out-of-funds
|
||||
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.NewCounter("txpool/queued/discard")
|
||||
queuedReplaceCounter = metrics.NewCounter("txpool/queued/replace")
|
||||
queuedRateLimitCounter = metrics.NewCounter("txpool/queued/ratelimit") // Dropped due to rate limiting
|
||||
queuedNofundsCounter = metrics.NewCounter("txpool/queued/nofunds") // Dropped due to out-of-funds
|
||||
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.NewCounter("txpool/invalid")
|
||||
underpricedTxCounter = metrics.NewCounter("txpool/underpriced")
|
||||
invalidTxCounter = metrics.NewRegisteredCounter("txpool/invalid", nil)
|
||||
underpricedTxCounter = metrics.NewRegisteredCounter("txpool/underpriced", nil)
|
||||
)
|
||||
|
||||
// TxStatus is the current status of a transaction as seen py the pool.
|
||||
// TxStatus is the current status of a transaction as seen by the pool.
|
||||
type TxStatus uint
|
||||
|
||||
const (
|
||||
@@ -197,9 +197,9 @@ type TxPool struct {
|
||||
|
||||
currentState *state.StateDB // Current state in the blockchain head
|
||||
pendingState *state.ManagedState // Pending state tracking virtual nonces
|
||||
currentMaxGas *big.Int // Current gas limit for transaction caps
|
||||
currentMaxGas uint64 // Current gas limit for transaction caps
|
||||
|
||||
locals *accountSet // Set of local transaction to exepmt from evicion rules
|
||||
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
|
||||
@@ -214,7 +214,7 @@ type TxPool struct {
|
||||
}
|
||||
|
||||
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
|
||||
// trnsactions from the network.
|
||||
// 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()
|
||||
@@ -360,7 +360,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
||||
newNum := newHead.Number.Uint64()
|
||||
|
||||
if depth := uint64(math.Abs(float64(oldNum) - float64(newNum))); depth > 64 {
|
||||
log.Warn("Skipping deep transaction reorg", "depth", depth)
|
||||
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
|
||||
@@ -564,7 +564,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||
return ErrNegativeValue
|
||||
}
|
||||
// Ensure the transaction doesn't exceed the current block limit gas.
|
||||
if pool.currentMaxGas.Cmp(tx.Gas()) < 0 {
|
||||
if pool.currentMaxGas < tx.Gas() {
|
||||
return ErrGasLimit
|
||||
}
|
||||
// Make sure the transaction is signed properly
|
||||
@@ -586,8 +586,11 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
|
||||
return ErrInsufficientFunds
|
||||
}
|
||||
intrGas := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
|
||||
if tx.Gas().Cmp(intrGas) < 0 {
|
||||
intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tx.Gas() < intrGas {
|
||||
return ErrIntrinsicGas
|
||||
}
|
||||
return nil
|
||||
@@ -838,7 +841,7 @@ func (pool *TxPool) Status(hashes []common.Hash) []TxStatus {
|
||||
for i, hash := range hashes {
|
||||
if tx := pool.all[hash]; tx != nil {
|
||||
from, _ := types.Sender(pool.signer, tx) // already validated
|
||||
if pool.pending[from].txs.items[tx.Nonce()] != nil {
|
||||
if pool.pending[from] != nil && pool.pending[from].txs.items[tx.Nonce()] != nil {
|
||||
status[i] = TxStatusPending
|
||||
} else {
|
||||
status[i] = TxStatusQueued
|
||||
|
||||
+172
-115
@@ -46,7 +46,7 @@ func init() {
|
||||
|
||||
type testBlockChain struct {
|
||||
statedb *state.StateDB
|
||||
gasLimit *big.Int
|
||||
gasLimit uint64
|
||||
chainHeadFeed *event.Feed
|
||||
}
|
||||
|
||||
@@ -68,19 +68,19 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) even
|
||||
return bc.chainHeadFeed.Subscribe(ch)
|
||||
}
|
||||
|
||||
func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
|
||||
func transaction(nonce uint64, gaslimit uint64, key *ecdsa.PrivateKey) *types.Transaction {
|
||||
return pricedTransaction(nonce, gaslimit, big.NewInt(1), key)
|
||||
}
|
||||
|
||||
func pricedTransaction(nonce uint64, gaslimit, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
|
||||
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) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(diskdb))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
@@ -184,10 +184,10 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
|
||||
|
||||
// setup pool with 2 transaction in it
|
||||
statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether))
|
||||
blockchain := &testChain{&testBlockChain{statedb, big.NewInt(1000000000), new(event.Feed)}, address, &trigger}
|
||||
blockchain := &testChain{&testBlockChain{statedb, 1000000000, new(event.Feed)}, address, &trigger}
|
||||
|
||||
tx0 := transaction(0, big.NewInt(100000), key)
|
||||
tx1 := transaction(1, big.NewInt(100000), key)
|
||||
tx0 := transaction(0, 100000, key)
|
||||
tx1 := transaction(1, 100000, key)
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
@@ -230,7 +230,7 @@ func TestInvalidTransactions(t *testing.T) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
tx := transaction(0, big.NewInt(100), key)
|
||||
tx := transaction(0, 100, key)
|
||||
from, _ := deriveSender(tx)
|
||||
|
||||
pool.currentState.AddBalance(from, big.NewInt(1))
|
||||
@@ -238,7 +238,7 @@ func TestInvalidTransactions(t *testing.T) {
|
||||
t.Error("expected", ErrInsufficientFunds)
|
||||
}
|
||||
|
||||
balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice()))
|
||||
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)
|
||||
@@ -246,12 +246,12 @@ func TestInvalidTransactions(t *testing.T) {
|
||||
|
||||
pool.currentState.SetNonce(from, 1)
|
||||
pool.currentState.AddBalance(from, big.NewInt(0xffffffffffffff))
|
||||
tx = transaction(0, big.NewInt(100000), key)
|
||||
tx = transaction(0, 100000, key)
|
||||
if err := pool.AddRemote(tx); err != ErrNonceTooLow {
|
||||
t.Error("expected", ErrNonceTooLow)
|
||||
}
|
||||
|
||||
tx = transaction(1, big.NewInt(100000), key)
|
||||
tx = transaction(1, 100000, key)
|
||||
pool.gasPrice = big.NewInt(1000)
|
||||
if err := pool.AddRemote(tx); err != ErrUnderpriced {
|
||||
t.Error("expected", ErrUnderpriced, "got", err)
|
||||
@@ -267,7 +267,7 @@ func TestTransactionQueue(t *testing.T) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
tx := transaction(0, big.NewInt(100), key)
|
||||
tx := transaction(0, 100, key)
|
||||
from, _ := deriveSender(tx)
|
||||
pool.currentState.AddBalance(from, big.NewInt(1000))
|
||||
pool.lockedReset(nil, nil)
|
||||
@@ -278,7 +278,7 @@ func TestTransactionQueue(t *testing.T) {
|
||||
t.Error("expected valid txs to be 1 is", len(pool.pending))
|
||||
}
|
||||
|
||||
tx = transaction(1, big.NewInt(100), key)
|
||||
tx = transaction(1, 100, key)
|
||||
from, _ = deriveSender(tx)
|
||||
pool.currentState.SetNonce(from, 2)
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
@@ -294,9 +294,9 @@ func TestTransactionQueue(t *testing.T) {
|
||||
pool, key = setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
tx1 := transaction(0, big.NewInt(100), key)
|
||||
tx2 := transaction(10, big.NewInt(100), key)
|
||||
tx3 := transaction(11, big.NewInt(100), key)
|
||||
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)
|
||||
@@ -321,7 +321,7 @@ func TestTransactionNegativeValue(t *testing.T) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil), types.HomesteadSigner{}, key)
|
||||
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 {
|
||||
@@ -341,12 +341,12 @@ func TestTransactionChainFork(t *testing.T) {
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
||||
|
||||
pool.chain = &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
pool.lockedReset(nil, nil)
|
||||
}
|
||||
resetState()
|
||||
|
||||
tx := transaction(0, big.NewInt(100000), key)
|
||||
tx := transaction(0, 100000, key)
|
||||
if _, err := pool.add(tx, false); err != nil {
|
||||
t.Error("didn't expect error", err)
|
||||
}
|
||||
@@ -371,15 +371,15 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
||||
|
||||
pool.chain = &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
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), big.NewInt(100000), big.NewInt(1), nil), signer, key)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(2), nil), signer, key)
|
||||
tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(1), nil), signer, key)
|
||||
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 {
|
||||
@@ -418,7 +418,7 @@ func TestTransactionMissingNonce(t *testing.T) {
|
||||
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
|
||||
tx := transaction(1, big.NewInt(100000), key)
|
||||
tx := transaction(1, 100000, key)
|
||||
if _, err := pool.add(tx, false); err != nil {
|
||||
t.Error("didn't expect error", err)
|
||||
}
|
||||
@@ -445,7 +445,7 @@ func TestTransactionNonceRecovery(t *testing.T) {
|
||||
pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
|
||||
pool.lockedReset(nil, nil)
|
||||
|
||||
tx := transaction(n, big.NewInt(100000), key)
|
||||
tx := transaction(n, 100000, key)
|
||||
if err := pool.AddRemote(tx); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -466,17 +466,17 @@ func TestTransactionDropping(t *testing.T) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
account, _ := deriveSender(transaction(0, big.NewInt(0), key))
|
||||
account, _ := deriveSender(transaction(0, 0, key))
|
||||
pool.currentState.AddBalance(account, big.NewInt(1000))
|
||||
|
||||
// Add some pending and some queued transactions
|
||||
var (
|
||||
tx0 = transaction(0, big.NewInt(100), key)
|
||||
tx1 = transaction(1, big.NewInt(200), key)
|
||||
tx2 = transaction(2, big.NewInt(300), key)
|
||||
tx10 = transaction(10, big.NewInt(100), key)
|
||||
tx11 = transaction(11, big.NewInt(200), key)
|
||||
tx12 = transaction(12, big.NewInt(300), key)
|
||||
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)
|
||||
@@ -531,7 +531,7 @@ func TestTransactionDropping(t *testing.T) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
|
||||
}
|
||||
// Reduce the block gas limit, check that invalidated transactions are dropped
|
||||
pool.chain.(*testBlockChain).gasLimit = big.NewInt(100)
|
||||
pool.chain.(*testBlockChain).gasLimit = 100
|
||||
pool.lockedReset(nil, nil)
|
||||
|
||||
if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
|
||||
@@ -561,7 +561,7 @@ func TestTransactionPostponing(t *testing.T) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
account, _ := deriveSender(transaction(0, big.NewInt(0), key))
|
||||
account, _ := deriveSender(transaction(0, 0, key))
|
||||
pool.currentState.AddBalance(account, big.NewInt(1000))
|
||||
|
||||
// Add a batch consecutive pending transactions for validation
|
||||
@@ -569,9 +569,9 @@ func TestTransactionPostponing(t *testing.T) {
|
||||
for i := 0; i < 100; i++ {
|
||||
var tx *types.Transaction
|
||||
if i%2 == 0 {
|
||||
tx = transaction(uint64(i), big.NewInt(100), key)
|
||||
tx = transaction(uint64(i), 100, key)
|
||||
} else {
|
||||
tx = transaction(uint64(i), big.NewInt(500), key)
|
||||
tx = transaction(uint64(i), 500, key)
|
||||
}
|
||||
pool.promoteTx(account, tx.Hash(), tx)
|
||||
txns = append(txns, tx)
|
||||
@@ -638,7 +638,7 @@ func TestTransactionGapFilling(t *testing.T) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
account, _ := deriveSender(transaction(0, big.NewInt(0), key))
|
||||
account, _ := deriveSender(transaction(0, 0, key))
|
||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
// Keep track of transaction events to ensure all executables get announced
|
||||
@@ -647,10 +647,10 @@ func TestTransactionGapFilling(t *testing.T) {
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
// Create a pending and a queued transaction with a nonce-gap in between
|
||||
if err := pool.AddRemote(transaction(0, big.NewInt(100000), key)); err != nil {
|
||||
if err := pool.AddRemote(transaction(0, 100000, key)); err != nil {
|
||||
t.Fatalf("failed to add pending transaction: %v", err)
|
||||
}
|
||||
if err := pool.AddRemote(transaction(2, big.NewInt(100000), key)); err != nil {
|
||||
if err := pool.AddRemote(transaction(2, 100000, key)); err != nil {
|
||||
t.Fatalf("failed to add queued transaction: %v", err)
|
||||
}
|
||||
pending, queued := pool.Stats()
|
||||
@@ -667,7 +667,7 @@ func TestTransactionGapFilling(t *testing.T) {
|
||||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// Fill the nonce gap and ensure all transactions become pending
|
||||
if err := pool.AddRemote(transaction(1, big.NewInt(100000), key)); err != nil {
|
||||
if err := pool.AddRemote(transaction(1, 100000, key)); err != nil {
|
||||
t.Fatalf("failed to add gapped transaction: %v", err)
|
||||
}
|
||||
pending, queued = pool.Stats()
|
||||
@@ -694,12 +694,12 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
account, _ := deriveSender(transaction(0, big.NewInt(0), key))
|
||||
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, big.NewInt(100000), key)); err != nil {
|
||||
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 {
|
||||
@@ -738,7 +738,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
config.NoLocals = nolocals
|
||||
@@ -763,7 +763,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
|
||||
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, big.NewInt(100000), key))
|
||||
txs = append(txs, transaction(nonces[addr]+1, 100000, key))
|
||||
nonces[addr]++
|
||||
}
|
||||
// Import the batch and verify that limits have been enforced
|
||||
@@ -782,7 +782,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
|
||||
// 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, big.NewInt(100000), local))
|
||||
txs = append(txs, transaction(i+1, 100000, local))
|
||||
}
|
||||
pool.AddLocals(txs)
|
||||
|
||||
@@ -827,7 +827,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
|
||||
// Create the pool to test the non-expiration enforcement
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
config.Lifetime = time.Second
|
||||
@@ -844,10 +844,10 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
|
||||
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, big.NewInt(100000), big.NewInt(1), local)); err != nil {
|
||||
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, big.NewInt(100000), big.NewInt(1), remote)); err != nil {
|
||||
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()
|
||||
@@ -891,7 +891,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
account, _ := deriveSender(transaction(0, big.NewInt(0), key))
|
||||
account, _ := deriveSender(transaction(0, 0, key))
|
||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
// Keep track of transaction events to ensure all executables get announced
|
||||
@@ -901,7 +901,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
|
||||
|
||||
// Keep queuing up transactions and make sure all above a limit are dropped
|
||||
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
|
||||
if err := pool.AddRemote(transaction(i, big.NewInt(100000), key)); err != nil {
|
||||
if err := pool.AddRemote(transaction(i, 100000, key)); err != nil {
|
||||
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
||||
}
|
||||
if pool.pending[account].Len() != int(i)+1 {
|
||||
@@ -934,11 +934,11 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
|
||||
pool1, key1 := setupTxPool()
|
||||
defer pool1.Stop()
|
||||
|
||||
account1, _ := deriveSender(transaction(0, big.NewInt(0), key1))
|
||||
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, big.NewInt(100000), key1)); err != nil {
|
||||
if err := pool1.AddRemote(transaction(origin+i, 100000, key1)); err != nil {
|
||||
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
||||
}
|
||||
}
|
||||
@@ -946,12 +946,12 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
|
||||
pool2, key2 := setupTxPool()
|
||||
defer pool2.Stop()
|
||||
|
||||
account2, _ := deriveSender(transaction(0, big.NewInt(0), key2))
|
||||
account2, _ := deriveSender(transaction(0, 0, key2))
|
||||
pool2.currentState.AddBalance(account2, big.NewInt(1000000))
|
||||
|
||||
txns := []*types.Transaction{}
|
||||
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
|
||||
txns = append(txns, transaction(origin+i, big.NewInt(100000), key2))
|
||||
txns = append(txns, transaction(origin+i, 100000, key2))
|
||||
}
|
||||
pool2.AddRemotes(txns)
|
||||
|
||||
@@ -982,7 +982,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
config.GlobalSlots = config.AccountSlots * 10
|
||||
@@ -1003,7 +1003,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
|
||||
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], big.NewInt(100000), key))
|
||||
txs = append(txs, transaction(nonces[addr], 100000, key))
|
||||
nonces[addr]++
|
||||
}
|
||||
}
|
||||
@@ -1029,7 +1029,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
config.AccountSlots = 2
|
||||
@@ -1046,7 +1046,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
|
||||
|
||||
txs := types.Transactions{}
|
||||
for j := 0; j < int(config.GlobalSlots)*2; j++ {
|
||||
txs = append(txs, transaction(uint64(j), big.NewInt(100000), key))
|
||||
txs = append(txs, transaction(uint64(j), 100000, key))
|
||||
}
|
||||
// Import the batch and verify that limits have been enforced
|
||||
pool.AddRemotes(txs)
|
||||
@@ -1064,7 +1064,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
config.GlobalSlots = 0
|
||||
@@ -1085,7 +1085,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
|
||||
for _, key := range keys {
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
for j := 0; j < int(config.AccountSlots)*2; j++ {
|
||||
txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
|
||||
txs = append(txs, transaction(nonces[addr], 100000, key))
|
||||
nonces[addr]++
|
||||
}
|
||||
}
|
||||
@@ -1113,7 +1113,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
|
||||
// Create the pool to test the pricing enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
@@ -1132,15 +1132,15 @@ func TestTransactionPoolRepricing(t *testing.T) {
|
||||
// Generate and queue a batch of transactions, both pending and queued
|
||||
txs := types.Transactions{}
|
||||
|
||||
txs = append(txs, pricedTransaction(0, big.NewInt(100000), big.NewInt(2), keys[0]))
|
||||
txs = append(txs, pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[0]))
|
||||
txs = append(txs, pricedTransaction(2, big.NewInt(100000), big.NewInt(2), keys[0]))
|
||||
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(2), keys[0]))
|
||||
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0]))
|
||||
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0]))
|
||||
|
||||
txs = append(txs, pricedTransaction(1, big.NewInt(100000), big.NewInt(2), keys[1]))
|
||||
txs = append(txs, pricedTransaction(2, big.NewInt(100000), big.NewInt(1), keys[1]))
|
||||
txs = append(txs, pricedTransaction(3, big.NewInt(100000), big.NewInt(2), keys[1]))
|
||||
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[1]))
|
||||
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[1]))
|
||||
txs = append(txs, pricedTransaction(3, 100000, big.NewInt(2), keys[1]))
|
||||
|
||||
ltx := pricedTransaction(0, big.NewInt(100000), big.NewInt(1), keys[2])
|
||||
ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[2])
|
||||
|
||||
// Import the batch and that both pending and queued transactions match up
|
||||
pool.AddRemotes(txs)
|
||||
@@ -1176,10 +1176,10 @@ func TestTransactionPoolRepricing(t *testing.T) {
|
||||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// Check that we can't add the old transactions back
|
||||
if err := pool.AddRemote(pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[0])); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); err != ErrUnderpriced {
|
||||
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(1), keys[1])); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
|
||||
t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
if err := validateEvents(events, 0); err != nil {
|
||||
@@ -1189,7 +1189,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
|
||||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// However we can add local underpriced transactions
|
||||
tx := pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[2])
|
||||
tx := pricedTransaction(1, 100000, big.NewInt(1), keys[2])
|
||||
if err := pool.AddLocal(tx); err != nil {
|
||||
t.Fatalf("failed to add underpriced local transaction: %v", err)
|
||||
}
|
||||
@@ -1212,7 +1212,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
|
||||
// Create the pool to test the pricing enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
@@ -1226,12 +1226,12 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
|
||||
// Create transaction (both pending and queued) with a linearly growing gasprice
|
||||
for i := uint64(0); i < 500; i++ {
|
||||
// Add pending
|
||||
p_tx := pricedTransaction(i, big.NewInt(100000), big.NewInt(int64(i)), keys[2])
|
||||
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, big.NewInt(100000), big.NewInt(int64(i)), keys[2])
|
||||
q_tx := pricedTransaction(i+501, 100000, big.NewInt(int64(i)), keys[2])
|
||||
if err := pool.AddLocal(q_tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1266,7 +1266,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
|
||||
|
||||
// Tests that when the pool reaches its global transaction limit, underpriced
|
||||
// transactions are gradually shifted out for more expensive ones and any gapped
|
||||
// pending transactions are moved into te queue.
|
||||
// pending transactions are moved into the queue.
|
||||
//
|
||||
// Note, local transactions are never allowed to be dropped.
|
||||
func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||
@@ -1275,7 +1275,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||
// Create the pool to test the pricing enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
config.GlobalSlots = 2
|
||||
@@ -1298,12 +1298,12 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||
// Generate and queue a batch of transactions, both pending and queued
|
||||
txs := types.Transactions{}
|
||||
|
||||
txs = append(txs, pricedTransaction(0, big.NewInt(100000), big.NewInt(1), keys[0]))
|
||||
txs = append(txs, pricedTransaction(1, big.NewInt(100000), big.NewInt(2), keys[0]))
|
||||
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[0]))
|
||||
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[0]))
|
||||
|
||||
txs = append(txs, pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[1]))
|
||||
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[1]))
|
||||
|
||||
ltx := pricedTransaction(0, big.NewInt(100000), big.NewInt(1), keys[2])
|
||||
ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[2])
|
||||
|
||||
// Import the batch and that both pending and queued transactions match up
|
||||
pool.AddRemotes(txs)
|
||||
@@ -1323,17 +1323,17 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// Ensure that adding an underpriced transaction on block limit fails
|
||||
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), keys[1])); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
|
||||
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
// Ensure that adding high priced transactions drops cheap ones, but not own
|
||||
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(3), keys[1])); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil {
|
||||
t.Fatalf("failed to add well priced transaction: %v", err)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(4), keys[1])); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(4), keys[1])); err != nil {
|
||||
t.Fatalf("failed to add well priced transaction: %v", err)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(3, big.NewInt(100000), big.NewInt(5), keys[1])); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(3, 100000, big.NewInt(5), keys[1])); err != nil {
|
||||
t.Fatalf("failed to add well priced transaction: %v", err)
|
||||
}
|
||||
pending, queued = pool.Stats()
|
||||
@@ -1350,7 +1350,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// Ensure that adding local transactions can push out even higher priced ones
|
||||
tx := pricedTransaction(1, big.NewInt(100000), big.NewInt(0), keys[2])
|
||||
tx := pricedTransaction(1, 100000, big.NewInt(0), keys[2])
|
||||
if err := pool.AddLocal(tx); err != nil {
|
||||
t.Fatalf("failed to add underpriced local transaction: %v", err)
|
||||
}
|
||||
@@ -1377,7 +1377,7 @@ func TestTransactionReplacement(t *testing.T) {
|
||||
// Create the pool to test the pricing enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
@@ -1395,49 +1395,49 @@ func TestTransactionReplacement(t *testing.T) {
|
||||
price := int64(100)
|
||||
threshold := (price * (100 + int64(testTxPoolConfig.PriceBump))) / 100
|
||||
|
||||
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), key)); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), key)); err != nil {
|
||||
t.Fatalf("failed to add original cheap pending transaction: %v", err)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100001), big.NewInt(1), key)); err != ErrReplaceUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100001, big.NewInt(1), key)); err != ErrReplaceUnderpriced {
|
||||
t.Fatalf("original cheap pending transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(2), key)); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(2), key)); err != nil {
|
||||
t.Fatalf("failed to replace original cheap pending transaction: %v", err)
|
||||
}
|
||||
if err := validateEvents(events, 2); err != nil {
|
||||
t.Fatalf("cheap replacement event firing failed: %v", err)
|
||||
}
|
||||
|
||||
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(price), key)); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(price), key)); err != nil {
|
||||
t.Fatalf("failed to add original proper pending transaction: %v", err)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100001), big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100001, big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
|
||||
t.Fatalf("original proper pending transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(threshold), key)); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(threshold), key)); err != nil {
|
||||
t.Fatalf("failed to replace original proper pending transaction: %v", err)
|
||||
}
|
||||
if err := validateEvents(events, 2); err != nil {
|
||||
t.Fatalf("proper replacement event firing failed: %v", err)
|
||||
}
|
||||
// Add queued transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
|
||||
if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(1), key)); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), key)); err != nil {
|
||||
t.Fatalf("failed to add original cheap queued transaction: %v", err)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100001), big.NewInt(1), key)); err != ErrReplaceUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100001, big.NewInt(1), key)); err != ErrReplaceUnderpriced {
|
||||
t.Fatalf("original cheap queued transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(2), key)); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(2), key)); err != nil {
|
||||
t.Fatalf("failed to replace original cheap queued transaction: %v", err)
|
||||
}
|
||||
|
||||
if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(price), key)); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(price), key)); err != nil {
|
||||
t.Fatalf("failed to add original proper queued transaction: %v", err)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100001), big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100001, big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
|
||||
t.Fatalf("original proper queued transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(2, big.NewInt(100000), big.NewInt(threshold), key)); err != nil {
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(threshold), key)); err != nil {
|
||||
t.Fatalf("failed to replace original proper queued transaction: %v", err)
|
||||
}
|
||||
|
||||
@@ -1472,7 +1472,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||
// Create the original pool to inject transaction into the journal
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
config.NoLocals = nolocals
|
||||
@@ -1489,16 +1489,16 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||
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, big.NewInt(100000), big.NewInt(1), local)); err != nil {
|
||||
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, big.NewInt(100000), big.NewInt(1), local)); err != nil {
|
||||
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, big.NewInt(100000), big.NewInt(1), local)); err != nil {
|
||||
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, big.NewInt(100000), big.NewInt(1), remote)); err != nil {
|
||||
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()
|
||||
@@ -1514,7 +1514,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||
// 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, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool = NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
|
||||
@@ -1541,7 +1541,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||
pool.Stop()
|
||||
|
||||
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
|
||||
blockchain = &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
pool = NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
|
||||
pending, queued = pool.Stats()
|
||||
@@ -1563,6 +1563,63 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||
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
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
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) }
|
||||
@@ -1574,11 +1631,11 @@ func benchmarkPendingDemotion(b *testing.B, size int) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
account, _ := deriveSender(transaction(0, big.NewInt(0), key))
|
||||
account, _ := deriveSender(transaction(0, 0, key))
|
||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
tx := transaction(uint64(i), big.NewInt(100000), key)
|
||||
tx := transaction(uint64(i), 100000, key)
|
||||
pool.promoteTx(account, tx.Hash(), tx)
|
||||
}
|
||||
// Benchmark the speed of pool validation
|
||||
@@ -1599,11 +1656,11 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
account, _ := deriveSender(transaction(0, big.NewInt(0), key))
|
||||
account, _ := deriveSender(transaction(0, 0, key))
|
||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
tx := transaction(uint64(1+i), big.NewInt(100000), key)
|
||||
tx := transaction(uint64(1+i), 100000, key)
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
}
|
||||
// Benchmark the speed of pool validation
|
||||
@@ -1619,12 +1676,12 @@ func BenchmarkPoolInsert(b *testing.B) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
account, _ := deriveSender(transaction(0, big.NewInt(0), key))
|
||||
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), big.NewInt(100000), key)
|
||||
txs[i] = transaction(uint64(i), 100000, key)
|
||||
}
|
||||
// Benchmark importing the transactions into the queue
|
||||
b.ResetTimer()
|
||||
@@ -1643,14 +1700,14 @@ func benchmarkPoolBatchInsert(b *testing.B, size int) {
|
||||
pool, key := setupTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
account, _ := deriveSender(transaction(0, big.NewInt(0), key))
|
||||
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), big.NewInt(100000), key)
|
||||
batches[i][j] = transaction(uint64(size*i+j), 100000, key)
|
||||
}
|
||||
}
|
||||
// Benchmark importing the transactions into the queue
|
||||
|
||||
+2
-4
@@ -17,8 +17,6 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@@ -34,7 +32,7 @@ type Validator interface {
|
||||
|
||||
// ValidateState validates the given statedb and optionally the receipts and
|
||||
// gas used.
|
||||
ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error
|
||||
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.
|
||||
@@ -44,5 +42,5 @@ type Validator interface {
|
||||
// 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, *big.Int, error)
|
||||
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error)
|
||||
}
|
||||
|
||||
+15
-12
@@ -25,6 +25,7 @@ import (
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -77,8 +78,8 @@ type Header struct {
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
|
||||
Number *big.Int `json:"number" gencodec:"required"`
|
||||
GasLimit *big.Int `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed *big.Int `json:"gasUsed" 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"`
|
||||
@@ -89,8 +90,8 @@ type Header struct {
|
||||
type headerMarshaling struct {
|
||||
Difficulty *hexutil.Big
|
||||
Number *hexutil.Big
|
||||
GasLimit *hexutil.Big
|
||||
GasUsed *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
|
||||
@@ -121,6 +122,12 @@ func (h *Header) HashNoNonce() common.Hash {
|
||||
})
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -243,12 +250,6 @@ func CopyHeader(h *Header) *Header {
|
||||
if cpy.Number = new(big.Int); h.Number != nil {
|
||||
cpy.Number.Set(h.Number)
|
||||
}
|
||||
if cpy.GasLimit = new(big.Int); h.GasLimit != nil {
|
||||
cpy.GasLimit.Set(h.GasLimit)
|
||||
}
|
||||
if cpy.GasUsed = new(big.Int); h.GasUsed != nil {
|
||||
cpy.GasUsed.Set(h.GasUsed)
|
||||
}
|
||||
if len(h.Extra) > 0 {
|
||||
cpy.Extra = make([]byte, len(h.Extra))
|
||||
copy(cpy.Extra, h.Extra)
|
||||
@@ -302,8 +303,8 @@ func (b *Block) Transaction(hash common.Hash) *Transaction {
|
||||
}
|
||||
|
||||
func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) }
|
||||
func (b *Block) GasLimit() *big.Int { return new(big.Int).Set(b.header.GasLimit) }
|
||||
func (b *Block) GasUsed() *big.Int { return new(big.Int).Set(b.header.GasUsed) }
|
||||
func (b *Block) 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) }
|
||||
|
||||
@@ -328,6 +329,8 @@ func (b *Block) HashNoNonce() common.Hash {
|
||||
return b.header.HashNoNonce()
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
+3
-3
@@ -41,8 +41,8 @@ func TestBlockEncoding(t *testing.T) {
|
||||
}
|
||||
}
|
||||
check("Difficulty", block.Difficulty(), big.NewInt(131072))
|
||||
check("GasLimit", block.GasLimit(), big.NewInt(3141592))
|
||||
check("GasUsed", block.GasUsed(), big.NewInt(21000))
|
||||
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"))
|
||||
@@ -51,7 +51,7 @@ func TestBlockEncoding(t *testing.T) {
|
||||
check("Time", block.Time(), big.NewInt(1426516743))
|
||||
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
|
||||
|
||||
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
|
||||
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())
|
||||
|
||||
+12
-10
@@ -11,6 +11,8 @@ import (
|
||||
"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"`
|
||||
@@ -22,8 +24,8 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit *hexutil.Big `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed" 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"`
|
||||
@@ -40,8 +42,8 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||
enc.Bloom = h.Bloom
|
||||
enc.Difficulty = (*hexutil.Big)(h.Difficulty)
|
||||
enc.Number = (*hexutil.Big)(h.Number)
|
||||
enc.GasLimit = (*hexutil.Big)(h.GasLimit)
|
||||
enc.GasUsed = (*hexutil.Big)(h.GasUsed)
|
||||
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
|
||||
@@ -61,10 +63,10 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit *hexutil.Big `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed" 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"`
|
||||
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
MixDigest *common.Hash `json:"mixHash" gencodec:"required"`
|
||||
Nonce *BlockNonce `json:"nonce" gencodec:"required"`
|
||||
}
|
||||
@@ -111,11 +113,11 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gasLimit' for Header")
|
||||
}
|
||||
h.GasLimit = (*big.Int)(dec.GasLimit)
|
||||
h.GasLimit = uint64(*dec.GasLimit)
|
||||
if dec.GasUsed == nil {
|
||||
return errors.New("missing required field 'gasUsed' for Header")
|
||||
}
|
||||
h.GasUsed = (*big.Int)(dec.GasUsed)
|
||||
h.GasUsed = uint64(*dec.GasUsed)
|
||||
if dec.Time == nil {
|
||||
return errors.New("missing required field 'timestamp' for Header")
|
||||
}
|
||||
@@ -123,7 +125,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
if dec.Extra == nil {
|
||||
return errors.New("missing required field 'extraData' for Header")
|
||||
}
|
||||
h.Extra = dec.Extra
|
||||
h.Extra = *dec.Extra
|
||||
if dec.MixDigest == nil {
|
||||
return errors.New("missing required field 'mixHash' for Header")
|
||||
}
|
||||
|
||||
+4
-2
@@ -10,6 +10,8 @@ import (
|
||||
"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"`
|
||||
@@ -39,7 +41,7 @@ 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"`
|
||||
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"`
|
||||
@@ -62,7 +64,7 @@ func (l *Log) UnmarshalJSON(input []byte) error {
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'data' for Log")
|
||||
}
|
||||
l.Data = dec.Data
|
||||
l.Data = *dec.Data
|
||||
if dec.BlockNumber != nil {
|
||||
l.BlockNumber = uint64(*dec.BlockNumber)
|
||||
}
|
||||
|
||||
+12
-11
@@ -5,52 +5,53 @@ package types
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
var _ = (*receiptMarshaling)(nil)
|
||||
|
||||
func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
type Receipt struct {
|
||||
PostState hexutil.Bytes `json:"root"`
|
||||
Status hexutil.Uint `json:"status"`
|
||||
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
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.Big `json:"gasUsed" gencodec:"required"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
}
|
||||
var enc Receipt
|
||||
enc.PostState = r.PostState
|
||||
enc.Status = hexutil.Uint(r.Status)
|
||||
enc.CumulativeGasUsed = (*hexutil.Big)(r.CumulativeGasUsed)
|
||||
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.Big)(r.GasUsed)
|
||||
enc.GasUsed = hexutil.Uint64(r.GasUsed)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
type Receipt struct {
|
||||
PostState hexutil.Bytes `json:"root"`
|
||||
PostState *hexutil.Bytes `json:"root"`
|
||||
Status *hexutil.Uint `json:"status"`
|
||||
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
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.Big `json:"gasUsed" gencodec:"required"`
|
||||
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
|
||||
r.PostState = *dec.PostState
|
||||
}
|
||||
if dec.Status != nil {
|
||||
r.Status = uint(*dec.Status)
|
||||
@@ -58,7 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
if dec.CumulativeGasUsed == nil {
|
||||
return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
|
||||
}
|
||||
r.CumulativeGasUsed = (*big.Int)(dec.CumulativeGasUsed)
|
||||
r.CumulativeGasUsed = uint64(*dec.CumulativeGasUsed)
|
||||
if dec.Bloom == nil {
|
||||
return errors.New("missing required field 'logsBloom' for Receipt")
|
||||
}
|
||||
@@ -77,6 +78,6 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
if dec.GasUsed == nil {
|
||||
return errors.New("missing required field 'gasUsed' for Receipt")
|
||||
}
|
||||
r.GasUsed = (*big.Int)(dec.GasUsed)
|
||||
r.GasUsed = uint64(*dec.GasUsed)
|
||||
return nil
|
||||
}
|
||||
|
||||
+8
-6
@@ -11,11 +11,13 @@ import (
|
||||
"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.Big `json:"gas" 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"`
|
||||
@@ -27,7 +29,7 @@ func (t txdata) MarshalJSON() ([]byte, error) {
|
||||
var enc txdata
|
||||
enc.AccountNonce = hexutil.Uint64(t.AccountNonce)
|
||||
enc.Price = (*hexutil.Big)(t.Price)
|
||||
enc.GasLimit = (*hexutil.Big)(t.GasLimit)
|
||||
enc.GasLimit = hexutil.Uint64(t.GasLimit)
|
||||
enc.Recipient = t.Recipient
|
||||
enc.Amount = (*hexutil.Big)(t.Amount)
|
||||
enc.Payload = t.Payload
|
||||
@@ -42,10 +44,10 @@ 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.Big `json:"gas" 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"`
|
||||
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"`
|
||||
@@ -66,7 +68,7 @@ func (t *txdata) UnmarshalJSON(input []byte) error {
|
||||
if dec.GasLimit == nil {
|
||||
return errors.New("missing required field 'gas' for txdata")
|
||||
}
|
||||
t.GasLimit = (*big.Int)(dec.GasLimit)
|
||||
t.GasLimit = uint64(*dec.GasLimit)
|
||||
if dec.Recipient != nil {
|
||||
t.Recipient = dec.Recipient
|
||||
}
|
||||
@@ -77,7 +79,7 @@ func (t *txdata) UnmarshalJSON(input []byte) error {
|
||||
if dec.Payload == nil {
|
||||
return errors.New("missing required field 'input' for txdata")
|
||||
}
|
||||
t.Payload = dec.Payload
|
||||
t.Payload = *dec.Payload
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' for txdata")
|
||||
}
|
||||
|
||||
+26
-14
@@ -20,7 +20,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -45,46 +45,46 @@ const (
|
||||
// Receipt represents the results of a transaction.
|
||||
type Receipt struct {
|
||||
// Consensus fields
|
||||
PostState []byte `json:"root"`
|
||||
Status uint `json:"status"`
|
||||
CumulativeGasUsed *big.Int `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Logs []*Log `json:"logs" gencodec:"required"`
|
||||
PostState []byte `json:"root"`
|
||||
Status uint `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 *big.Int `json:"gasUsed" gencodec:"required"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
}
|
||||
|
||||
type receiptMarshaling struct {
|
||||
PostState hexutil.Bytes
|
||||
Status hexutil.Uint
|
||||
CumulativeGasUsed *hexutil.Big
|
||||
GasUsed *hexutil.Big
|
||||
CumulativeGasUsed hexutil.Uint64
|
||||
GasUsed hexutil.Uint64
|
||||
}
|
||||
|
||||
// receiptRLP is the consensus encoding of a receipt.
|
||||
type receiptRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed *big.Int
|
||||
CumulativeGasUsed uint64
|
||||
Bloom Bloom
|
||||
Logs []*Log
|
||||
}
|
||||
|
||||
type receiptStorageRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed *big.Int
|
||||
CumulativeGasUsed uint64
|
||||
Bloom Bloom
|
||||
TxHash common.Hash
|
||||
ContractAddress common.Address
|
||||
Logs []*LogForStorage
|
||||
GasUsed *big.Int
|
||||
GasUsed uint64
|
||||
}
|
||||
|
||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||
func NewReceipt(root []byte, failed bool, cumulativeGasUsed *big.Int) *Receipt {
|
||||
r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)}
|
||||
func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
|
||||
r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed}
|
||||
if failed {
|
||||
r.Status = ReceiptStatusFailed
|
||||
} else {
|
||||
@@ -137,6 +137,18 @@ func (r *Receipt) statusEncoding() []byte {
|
||||
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
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (r *Receipt) String() string {
|
||||
if len(r.PostState) == 0 {
|
||||
|
||||
+36
-35
@@ -57,7 +57,7 @@ type Transaction struct {
|
||||
type txdata struct {
|
||||
AccountNonce uint64 `json:"nonce" gencodec:"required"`
|
||||
Price *big.Int `json:"gasPrice" gencodec:"required"`
|
||||
GasLimit *big.Int `json:"gas" 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"`
|
||||
@@ -74,7 +74,7 @@ type txdata struct {
|
||||
type txdataMarshaling struct {
|
||||
AccountNonce hexutil.Uint64
|
||||
Price *hexutil.Big
|
||||
GasLimit *hexutil.Big
|
||||
GasLimit hexutil.Uint64
|
||||
Amount *hexutil.Big
|
||||
Payload hexutil.Bytes
|
||||
V *hexutil.Big
|
||||
@@ -82,15 +82,15 @@ type txdataMarshaling struct {
|
||||
S *hexutil.Big
|
||||
}
|
||||
|
||||
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||
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, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||
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, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||
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)
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice
|
||||
Recipient: to,
|
||||
Payload: data,
|
||||
Amount: new(big.Int),
|
||||
GasLimit: new(big.Int),
|
||||
GasLimit: gasLimit,
|
||||
Price: new(big.Int),
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
@@ -108,9 +108,6 @@ func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice
|
||||
if amount != nil {
|
||||
d.Amount.Set(amount)
|
||||
}
|
||||
if gasLimit != nil {
|
||||
d.GasLimit.Set(gasLimit)
|
||||
}
|
||||
if gasPrice != nil {
|
||||
d.Price.Set(gasPrice)
|
||||
}
|
||||
@@ -137,7 +134,7 @@ func isProtectedV(V *big.Int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Encoder
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, &tx.data)
|
||||
}
|
||||
@@ -153,6 +150,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the web3 RPC transaction format.
|
||||
func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
||||
hash := tx.Hash()
|
||||
data := tx.data
|
||||
@@ -168,8 +166,8 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
||||
}
|
||||
var V byte
|
||||
if isProtectedV(dec.V) {
|
||||
chainId := deriveChainId(dec.V).Uint64()
|
||||
V = byte(dec.V.Uint64() - 35 - 2*chainId)
|
||||
chainID := deriveChainId(dec.V).Uint64()
|
||||
V = byte(dec.V.Uint64() - 35 - 2*chainID)
|
||||
} else {
|
||||
V = byte(dec.V.Uint64() - 27)
|
||||
}
|
||||
@@ -181,7 +179,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
||||
}
|
||||
|
||||
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
|
||||
func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) }
|
||||
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 }
|
||||
@@ -192,10 +190,9 @@ func (tx *Transaction) CheckNonce() bool { return true }
|
||||
func (tx *Transaction) To() *common.Address {
|
||||
if tx.data.Recipient == nil {
|
||||
return nil
|
||||
} else {
|
||||
to := *tx.data.Recipient
|
||||
return &to
|
||||
}
|
||||
to := *tx.data.Recipient
|
||||
return &to
|
||||
}
|
||||
|
||||
// Hash hashes the RLP encoding of tx.
|
||||
@@ -209,6 +206,8 @@ func (tx *Transaction) Hash() common.Hash {
|
||||
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)
|
||||
@@ -227,8 +226,8 @@ func (tx *Transaction) Size() common.StorageSize {
|
||||
func (tx *Transaction) AsMessage(s Signer) (Message, error) {
|
||||
msg := Message{
|
||||
nonce: tx.data.AccountNonce,
|
||||
price: new(big.Int).Set(tx.data.Price),
|
||||
gasLimit: new(big.Int).Set(tx.data.GasLimit),
|
||||
gasLimit: tx.data.GasLimit,
|
||||
gasPrice: new(big.Int).Set(tx.data.Price),
|
||||
to: tx.data.Recipient,
|
||||
amount: tx.data.Amount,
|
||||
data: tx.data.Payload,
|
||||
@@ -254,7 +253,7 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
|
||||
|
||||
// Cost returns amount + gasprice * gaslimit.
|
||||
func (tx *Transaction) Cost() *big.Int {
|
||||
total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit)
|
||||
total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit))
|
||||
total.Add(total, tx.data.Amount)
|
||||
return total
|
||||
}
|
||||
@@ -315,22 +314,22 @@ func (tx *Transaction) String() string {
|
||||
)
|
||||
}
|
||||
|
||||
// Transaction slice type for basic sorting.
|
||||
// Transactions is a Transaction slice type for basic sorting.
|
||||
type Transactions []*Transaction
|
||||
|
||||
// Len returns the length of s
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
|
||||
// Returns a new set t which is the difference between a to b
|
||||
// TxDifference returns a new set t which is the difference between a to b.
|
||||
func TxDifference(a, b Transactions) (keep Transactions) {
|
||||
keep = make(Transactions, 0, len(a))
|
||||
|
||||
@@ -378,7 +377,7 @@ func (s *TxByPrice) Pop() interface{} {
|
||||
}
|
||||
|
||||
// TransactionsByPriceAndNonce represents a set of transactions that can return
|
||||
// transactions in a profit-maximising sorted order, while supporting removing
|
||||
// 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
|
||||
@@ -440,22 +439,24 @@ func (t *TransactionsByPriceAndNonce) Pop() {
|
||||
//
|
||||
// NOTE: In a future PR this will be removed.
|
||||
type Message struct {
|
||||
to *common.Address
|
||||
from common.Address
|
||||
nonce uint64
|
||||
amount, price, gasLimit *big.Int
|
||||
data []byte
|
||||
checkNonce bool
|
||||
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, gasLimit, price *big.Int, data []byte, checkNonce bool) Message {
|
||||
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,
|
||||
price: price,
|
||||
gasLimit: gasLimit,
|
||||
gasPrice: gasPrice,
|
||||
data: data,
|
||||
checkNonce: checkNonce,
|
||||
}
|
||||
@@ -463,9 +464,9 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, g
|
||||
|
||||
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.price }
|
||||
func (m Message) GasPrice() *big.Int { return m.gasPrice }
|
||||
func (m Message) Value() *big.Int { return m.amount }
|
||||
func (m Message) Gas() *big.Int { return m.gasLimit }
|
||||
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 }
|
||||
|
||||
+4
-4
@@ -30,7 +30,7 @@ func TestEIP155Signing(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
signer := NewEIP155Signer(big.NewInt(18))
|
||||
tx, err := SignTx(NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil), signer, key)
|
||||
tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func TestEIP155ChainId(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
signer := NewEIP155Signer(big.NewInt(18))
|
||||
tx, err := SignTx(NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil), signer, key)
|
||||
tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -61,7 +61,7 @@ func TestEIP155ChainId(t *testing.T) {
|
||||
t.Error("expected chainId to be", signer.chainId, "got", tx.ChainId())
|
||||
}
|
||||
|
||||
tx = NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil)
|
||||
tx = NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil)
|
||||
tx, err = SignTx(tx, HomesteadSigner{}, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -118,7 +118,7 @@ func TestEIP155SigningVitalik(t *testing.T) {
|
||||
func TestChainId(t *testing.T) {
|
||||
key, _ := defaultTestKey()
|
||||
|
||||
tx := NewTransaction(0, common.Address{}, new(big.Int), new(big.Int), new(big.Int), nil)
|
||||
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)
|
||||
|
||||
+5
-5
@@ -34,7 +34,7 @@ var (
|
||||
emptyTx = NewTransaction(
|
||||
0,
|
||||
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
|
||||
big.NewInt(0), big.NewInt(0), big.NewInt(0),
|
||||
big.NewInt(0), 0, big.NewInt(0),
|
||||
nil,
|
||||
)
|
||||
|
||||
@@ -42,7 +42,7 @@ var (
|
||||
3,
|
||||
common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
|
||||
big.NewInt(10),
|
||||
big.NewInt(2000),
|
||||
2000,
|
||||
big.NewInt(1),
|
||||
common.FromHex("5544"),
|
||||
).WithSignature(
|
||||
@@ -139,7 +139,7 @@ func TestTransactionPriceNonceSort(t *testing.T) {
|
||||
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), big.NewInt(100), big.NewInt(int64(start+i)), nil), signer, key)
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -204,9 +204,9 @@ func TestTransactionJSON(t *testing.T) {
|
||||
var tx *Transaction
|
||||
switch i % 2 {
|
||||
case 0:
|
||||
tx = NewTransaction(i, common.Address{1}, common.Big0, common.Big1, common.Big2, []byte("abcdef"))
|
||||
tx = NewTransaction(i, common.Address{1}, common.Big0, 1, common.Big2, []byte("abcdef"))
|
||||
case 1:
|
||||
tx = NewContractCreation(i, common.Big0, common.Big1, common.Big2, []byte("abcdef"))
|
||||
tx = NewContractCreation(i, common.Big0, 1, common.Big2, []byte("abcdef"))
|
||||
}
|
||||
|
||||
tx, err := SignTx(tx, signer, key)
|
||||
|
||||
+6
-25
@@ -251,26 +251,12 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
|
||||
return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil
|
||||
}
|
||||
|
||||
var (
|
||||
// errNotOnCurve is returned if a point being unmarshalled as a bn256 elliptic
|
||||
// curve point is not on the curve.
|
||||
errNotOnCurve = errors.New("point not on elliptic curve")
|
||||
|
||||
// errInvalidCurvePoint is returned if a point being unmarshalled as a bn256
|
||||
// elliptic curve point is invalid.
|
||||
errInvalidCurvePoint = errors.New("invalid elliptic curve point")
|
||||
)
|
||||
|
||||
// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
|
||||
// returning it, or an error if the point is invalid.
|
||||
func newCurvePoint(blob []byte) (*bn256.G1, error) {
|
||||
p, onCurve := new(bn256.G1).Unmarshal(blob)
|
||||
if !onCurve {
|
||||
return nil, errNotOnCurve
|
||||
}
|
||||
gx, gy, _, _ := p.CurvePoints()
|
||||
if gx.Cmp(bn256.P) >= 0 || gy.Cmp(bn256.P) >= 0 {
|
||||
return nil, errInvalidCurvePoint
|
||||
p := new(bn256.G1)
|
||||
if _, err := p.Unmarshal(blob); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
@@ -278,14 +264,9 @@ func newCurvePoint(blob []byte) (*bn256.G1, error) {
|
||||
// newTwistPoint unmarshals a binary blob into a bn256 elliptic curve point,
|
||||
// returning it, or an error if the point is invalid.
|
||||
func newTwistPoint(blob []byte) (*bn256.G2, error) {
|
||||
p, onCurve := new(bn256.G2).Unmarshal(blob)
|
||||
if !onCurve {
|
||||
return nil, errNotOnCurve
|
||||
}
|
||||
x2, y2, _, _ := p.CurvePoints()
|
||||
if x2.Real().Cmp(bn256.P) >= 0 || x2.Imag().Cmp(bn256.P) >= 0 ||
|
||||
y2.Real().Cmp(bn256.P) >= 0 || y2.Imag().Cmp(bn256.P) >= 0 {
|
||||
return nil, errInvalidCurvePoint
|
||||
p := new(bn256.G2)
|
||||
if _, err := p.Unmarshal(blob); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
|
||||
+37
-12
@@ -19,6 +19,7 @@ package vm
|
||||
import (
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -38,7 +39,7 @@ type (
|
||||
)
|
||||
|
||||
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
|
||||
func run(evm *EVM, snapshot int, contract *Contract, input []byte) ([]byte, error) {
|
||||
func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
|
||||
if contract.CodeAddr != nil {
|
||||
precompiles := PrecompiledContractsHomestead
|
||||
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
|
||||
@@ -48,7 +49,7 @@ func run(evm *EVM, snapshot int, contract *Contract, input []byte) ([]byte, erro
|
||||
return RunPrecompiledContract(p, input, contract)
|
||||
}
|
||||
}
|
||||
return evm.interpreter.Run(snapshot, contract, input)
|
||||
return evm.interpreter.Run(contract, input)
|
||||
}
|
||||
|
||||
// Context provides the EVM with auxiliary information. Once provided
|
||||
@@ -68,7 +69,7 @@ type Context struct {
|
||||
|
||||
// Block information
|
||||
Coinbase common.Address // Provides information for COINBASE
|
||||
GasLimit *big.Int // Provides information for GASLIMIT
|
||||
GasLimit uint64 // Provides information for GASLIMIT
|
||||
BlockNumber *big.Int // Provides information for NUMBER
|
||||
Time *big.Int // Provides information for TIME
|
||||
Difficulty *big.Int // Provides information for DIFFICULTY
|
||||
@@ -104,6 +105,10 @@ type EVM struct {
|
||||
// abort is used to abort the EVM calling operations
|
||||
// NOTE: must be set atomically
|
||||
abort int32
|
||||
// callGasTemp holds the gas available for the current call. This is needed because the
|
||||
// available gas is calculated in gasCall* according to the 63/64 rule and later
|
||||
// applied in opCall*.
|
||||
callGasTemp uint64
|
||||
}
|
||||
|
||||
// NewEVM retutrns a new EVM . The returned EVM is not thread safe and should
|
||||
@@ -161,13 +166,23 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
}
|
||||
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
|
||||
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped environment for this execution context
|
||||
// only.
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
|
||||
ret, err = run(evm, snapshot, contract, input)
|
||||
start := time.Now()
|
||||
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
|
||||
|
||||
defer func() { // Lazy evaluation of the parameters
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
}()
|
||||
}
|
||||
ret, err = run(evm, contract, input)
|
||||
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in homestead this also counts for code storage gas errors.
|
||||
@@ -211,7 +226,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
|
||||
ret, err = run(evm, snapshot, contract, input)
|
||||
ret, err = run(evm, contract, input)
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != errExecutionReverted {
|
||||
@@ -244,7 +259,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||
contract := NewContract(caller, to, nil, gas).AsDelegate()
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
|
||||
ret, err = run(evm, snapshot, contract, input)
|
||||
ret, err = run(evm, contract, input)
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != errExecutionReverted {
|
||||
@@ -287,7 +302,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in Homestead this also counts for code storage gas errors.
|
||||
ret, err = run(evm, snapshot, contract, input)
|
||||
ret, err = run(evm, contract, input)
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != errExecutionReverted {
|
||||
@@ -334,7 +349,14 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
return nil, contractAddr, gas, nil
|
||||
}
|
||||
ret, err = run(evm, snapshot, contract, nil)
|
||||
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
ret, err = run(evm, contract, nil)
|
||||
|
||||
// check whether the max code size has been exceeded
|
||||
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
|
||||
// if the contract creation ran successfully and no errors were returned
|
||||
@@ -363,10 +385,13 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
||||
if maxCodeSizeExceeded && err == nil {
|
||||
err = errMaxCodeSizeExceeded
|
||||
}
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
}
|
||||
return ret, contractAddr, contract.Gas, err
|
||||
}
|
||||
|
||||
// ChainConfig returns the evmironment's chain configuration
|
||||
// ChainConfig returns the environment's chain configuration
|
||||
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
||||
|
||||
// Interpreter returns the EVM interpreter
|
||||
|
||||
+10
-42
@@ -17,8 +17,6 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
@@ -130,7 +128,7 @@ func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m
|
||||
// 0 => non 0
|
||||
return params.SstoreSetGas, nil
|
||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||
evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas))
|
||||
evm.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
|
||||
return params.SstoreClearGas, nil
|
||||
} else {
|
||||
@@ -342,19 +340,11 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called. This information is otherwise lost due to the dependency on *current*
|
||||
// available gas.
|
||||
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
@@ -374,19 +364,11 @@ func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack,
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called. This information is otherwise lost due to the dependency on *current*
|
||||
// available gas.
|
||||
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
@@ -421,7 +403,7 @@ func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack,
|
||||
}
|
||||
|
||||
if !evm.StateDB.HasSuicided(contract.Address()) {
|
||||
evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas))
|
||||
evm.StateDB.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
@@ -436,18 +418,11 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called.
|
||||
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
@@ -463,18 +438,11 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called.
|
||||
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
|
||||
+19
-15
@@ -11,19 +11,22 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
var _ = (*structLogMarshaling)(nil)
|
||||
|
||||
func (s StructLog) MarshalJSON() ([]byte, error) {
|
||||
type StructLog struct {
|
||||
Pc uint64 `json:"pc"`
|
||||
Op OpCode `json:"op"`
|
||||
Gas math.HexOrDecimal64 `json:"gas"`
|
||||
GasCost math.HexOrDecimal64 `json:"gasCost"`
|
||||
Memory hexutil.Bytes `json:"memory"`
|
||||
MemorySize int `json:"memSize"`
|
||||
Stack []*math.HexOrDecimal256 `json:"stack"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth int `json:"depth"`
|
||||
Err error `json:"error"`
|
||||
OpName string `json:"opName"`
|
||||
Pc uint64 `json:"pc"`
|
||||
Op OpCode `json:"op"`
|
||||
Gas math.HexOrDecimal64 `json:"gas"`
|
||||
GasCost math.HexOrDecimal64 `json:"gasCost"`
|
||||
Memory hexutil.Bytes `json:"memory"`
|
||||
MemorySize int `json:"memSize"`
|
||||
Stack []*math.HexOrDecimal256 `json:"stack"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth int `json:"depth"`
|
||||
Err error `json:"-"`
|
||||
OpName string `json:"opName"`
|
||||
ErrorString string `json:"error"`
|
||||
}
|
||||
var enc StructLog
|
||||
enc.Pc = s.Pc
|
||||
@@ -42,6 +45,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
|
||||
enc.Depth = s.Depth
|
||||
enc.Err = s.Err
|
||||
enc.OpName = s.OpName()
|
||||
enc.ErrorString = s.ErrorString()
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
@@ -51,12 +55,12 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
|
||||
Op *OpCode `json:"op"`
|
||||
Gas *math.HexOrDecimal64 `json:"gas"`
|
||||
GasCost *math.HexOrDecimal64 `json:"gasCost"`
|
||||
Memory hexutil.Bytes `json:"memory"`
|
||||
Memory *hexutil.Bytes `json:"memory"`
|
||||
MemorySize *int `json:"memSize"`
|
||||
Stack []*math.HexOrDecimal256 `json:"stack"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth *int `json:"depth"`
|
||||
Err *error `json:"error"`
|
||||
Err error `json:"-"`
|
||||
}
|
||||
var dec StructLog
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
@@ -75,7 +79,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
|
||||
s.GasCost = uint64(*dec.GasCost)
|
||||
}
|
||||
if dec.Memory != nil {
|
||||
s.Memory = dec.Memory
|
||||
s.Memory = *dec.Memory
|
||||
}
|
||||
if dec.MemorySize != nil {
|
||||
s.MemorySize = *dec.MemorySize
|
||||
@@ -93,7 +97,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
|
||||
s.Depth = *dec.Depth
|
||||
}
|
||||
if dec.Err != nil {
|
||||
s.Err = *dec.Err
|
||||
s.Err = dec.Err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+94
-44
@@ -302,6 +302,66 @@ func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// opSHL implements Shift Left
|
||||
// The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2,
|
||||
// and pushes on the stack arg2 shifted to the left by arg1 number of bits.
|
||||
func opSHL(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
|
||||
shift, value := math.U256(stack.pop()), math.U256(stack.peek())
|
||||
defer evm.interpreter.intPool.put(shift) // First operand back into the pool
|
||||
|
||||
if shift.Cmp(common.Big256) >= 0 {
|
||||
value.SetUint64(0)
|
||||
return nil, nil
|
||||
}
|
||||
n := uint(shift.Uint64())
|
||||
math.U256(value.Lsh(value, n))
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// opSHR implements Logical Shift Right
|
||||
// The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2,
|
||||
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
|
||||
func opSHR(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
|
||||
shift, value := math.U256(stack.pop()), math.U256(stack.peek())
|
||||
defer evm.interpreter.intPool.put(shift) // First operand back into the pool
|
||||
|
||||
if shift.Cmp(common.Big256) >= 0 {
|
||||
value.SetUint64(0)
|
||||
return nil, nil
|
||||
}
|
||||
n := uint(shift.Uint64())
|
||||
math.U256(value.Rsh(value, n))
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// opSAR implements Arithmetic Shift Right
|
||||
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
|
||||
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
|
||||
func opSAR(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one
|
||||
shift, value := math.U256(stack.pop()), math.S256(stack.pop())
|
||||
defer evm.interpreter.intPool.put(shift) // First operand back into the pool
|
||||
|
||||
if shift.Cmp(common.Big256) >= 0 {
|
||||
if value.Sign() > 0 {
|
||||
value.SetUint64(0)
|
||||
} else {
|
||||
value.SetInt64(-1)
|
||||
}
|
||||
stack.push(math.U256(value))
|
||||
return nil, nil
|
||||
}
|
||||
n := uint(shift.Uint64())
|
||||
value.Rsh(value, n)
|
||||
stack.push(math.U256(value))
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSha3(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
data := memory.Get(offset.Int64(), size.Int64())
|
||||
@@ -472,7 +532,7 @@ func opDifficulty(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stac
|
||||
}
|
||||
|
||||
func opGasLimit(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(math.U256(new(big.Int).Set(evm.GasLimit)))
|
||||
stack.push(math.U256(new(big.Int).SetUint64(evm.GasLimit)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -603,24 +663,20 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S
|
||||
}
|
||||
|
||||
func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
gas := stack.pop().Uint64()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
// Pop gas. The actual gas in in evm.callGasTemp.
|
||||
evm.interpreter.intPool.put(stack.pop())
|
||||
gas := evm.callGasTemp
|
||||
// Pop other call parameters.
|
||||
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr := common.BigToAddress(addr)
|
||||
value = math.U256(value)
|
||||
// pop input size and offset
|
||||
inOffset, inSize := stack.pop(), stack.pop()
|
||||
// pop return size and offset
|
||||
retOffset, retSize := stack.pop(), stack.pop()
|
||||
|
||||
address := common.BigToAddress(addr)
|
||||
|
||||
// Get the arguments from the memory
|
||||
// Get the arguments from the memory.
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
|
||||
if value.Sign() != 0 {
|
||||
gas += params.CallStipend
|
||||
}
|
||||
ret, returnGas, err := evm.Call(contract, address, args, gas, value)
|
||||
ret, returnGas, err := evm.Call(contract, toAddr, args, gas, value)
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
@@ -636,25 +692,20 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta
|
||||
}
|
||||
|
||||
func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
gas := stack.pop().Uint64()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
// Pop gas. The actual gas is in evm.callGasTemp.
|
||||
evm.interpreter.intPool.put(stack.pop())
|
||||
gas := evm.callGasTemp
|
||||
// Pop other call parameters.
|
||||
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr := common.BigToAddress(addr)
|
||||
value = math.U256(value)
|
||||
// pop input size and offset
|
||||
inOffset, inSize := stack.pop(), stack.pop()
|
||||
// pop return size and offset
|
||||
retOffset, retSize := stack.pop(), stack.pop()
|
||||
|
||||
address := common.BigToAddress(addr)
|
||||
|
||||
// Get the arguments from the memory
|
||||
// Get arguments from the memory.
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
|
||||
if value.Sign() != 0 {
|
||||
gas += params.CallStipend
|
||||
}
|
||||
|
||||
ret, returnGas, err := evm.CallCode(contract, address, args, gas, value)
|
||||
ret, returnGas, err := evm.CallCode(contract, toAddr, args, gas, value)
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
@@ -670,9 +721,13 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack
|
||||
}
|
||||
|
||||
func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
|
||||
toAddr := common.BigToAddress(to)
|
||||
// Pop gas. The actual gas is in evm.callGasTemp.
|
||||
evm.interpreter.intPool.put(stack.pop())
|
||||
gas := evm.callGasTemp
|
||||
// Pop other call parameters.
|
||||
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr := common.BigToAddress(addr)
|
||||
// Get arguments from the memory.
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
|
||||
ret, returnGas, err := evm.DelegateCall(contract, toAddr, args, gas)
|
||||
@@ -682,30 +737,25 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st
|
||||
stack.push(big.NewInt(1))
|
||||
}
|
||||
if err == nil || err == errExecutionReverted {
|
||||
memory.Set(outOffset.Uint64(), outSize.Uint64(), ret)
|
||||
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
contract.Gas += returnGas
|
||||
|
||||
evm.interpreter.intPool.put(to, inOffset, inSize, outOffset, outSize)
|
||||
evm.interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opStaticCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// pop gas
|
||||
gas := stack.pop().Uint64()
|
||||
// pop address
|
||||
addr := stack.pop()
|
||||
// pop input size and offset
|
||||
inOffset, inSize := stack.pop(), stack.pop()
|
||||
// pop return size and offset
|
||||
retOffset, retSize := stack.pop(), stack.pop()
|
||||
|
||||
address := common.BigToAddress(addr)
|
||||
|
||||
// Get the arguments from the memory
|
||||
// Pop gas. The actual gas is in evm.callGasTemp.
|
||||
evm.interpreter.intPool.put(stack.pop())
|
||||
gas := evm.callGasTemp
|
||||
// Pop other call parameters.
|
||||
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr := common.BigToAddress(addr)
|
||||
// Get arguments from the memory.
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
|
||||
ret, returnGas, err := evm.StaticCall(contract, address, args, gas)
|
||||
ret, returnGas, err := evm.StaticCall(contract, toAddr, args, gas)
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
|
||||
+169
@@ -1,3 +1,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
@@ -8,6 +24,48 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
type twoOperandTest struct {
|
||||
x string
|
||||
y string
|
||||
expected string
|
||||
}
|
||||
|
||||
func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)) {
|
||||
var (
|
||||
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
)
|
||||
for i, test := range tests {
|
||||
x := new(big.Int).SetBytes(common.Hex2Bytes(test.x))
|
||||
shift := new(big.Int).SetBytes(common.Hex2Bytes(test.y))
|
||||
expected := new(big.Int).SetBytes(common.Hex2Bytes(test.expected))
|
||||
stack.push(x)
|
||||
stack.push(shift)
|
||||
opFn(&pc, env, nil, nil, stack)
|
||||
actual := stack.pop()
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Errorf("Testcase %d, expected %v, got %v", i, expected, actual)
|
||||
}
|
||||
// Check pool usage
|
||||
// 1.pool is not allowed to contain anything on the stack
|
||||
// 2.pool is not allowed to contain the same pointers twice
|
||||
if env.interpreter.intPool.pool.len() > 0 {
|
||||
|
||||
poolvals := make(map[*big.Int]struct{})
|
||||
poolvals[actual] = struct{}{}
|
||||
|
||||
for env.interpreter.intPool.pool.len() > 0 {
|
||||
key := env.interpreter.intPool.get()
|
||||
if _, exist := poolvals[key]; exist {
|
||||
t.Errorf("Testcase %d, pool contains double-entry", i)
|
||||
}
|
||||
poolvals[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteOp(t *testing.T) {
|
||||
var (
|
||||
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
@@ -41,6 +99,98 @@ func TestByteOp(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSHL(t *testing.T) {
|
||||
// Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shl-shift-left
|
||||
tests := []twoOperandTest{
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000002"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "ff", "8000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "0101", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "8000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"},
|
||||
}
|
||||
testTwoOperandOp(t, tests, opSHL)
|
||||
}
|
||||
|
||||
func TestSHR(t *testing.T) {
|
||||
// Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shr-logical-shift-right
|
||||
tests := []twoOperandTest{
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000000", "01", "4000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000000", "ff", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000000", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000000", "0101", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
}
|
||||
testTwoOperandOp(t, tests, opSHR)
|
||||
}
|
||||
|
||||
func TestSAR(t *testing.T) {
|
||||
// Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#sar-arithmetic-shift-right
|
||||
tests := []twoOperandTest{
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000000", "01", "c000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000000", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000000", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000000", "0101", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"4000000000000000000000000000000000000000000000000000000000000000", "fe", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "f8", "000000000000000000000000000000000000000000000000000000000000007f"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "fe", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
}
|
||||
|
||||
testTwoOperandOp(t, tests, opSAR)
|
||||
}
|
||||
|
||||
func TestSGT(t *testing.T) {
|
||||
tests := []twoOperandTest{
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
}
|
||||
testTwoOperandOp(t, tests, opSgt)
|
||||
}
|
||||
|
||||
func TestSLT(t *testing.T) {
|
||||
tests := []twoOperandTest{
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"0000000000000000000000000000000000000000000000000000000000000001", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
|
||||
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"},
|
||||
}
|
||||
testTwoOperandOp(t, tests, opSlt)
|
||||
}
|
||||
|
||||
func opBenchmark(bench *testing.B, op func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error), args ...string) {
|
||||
var (
|
||||
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
@@ -243,3 +393,22 @@ func BenchmarkOpMulmod(b *testing.B) {
|
||||
|
||||
opBenchmark(b, opMulmod, x, y, z)
|
||||
}
|
||||
|
||||
func BenchmarkOpSHL(b *testing.B) {
|
||||
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
|
||||
y := "ff"
|
||||
|
||||
opBenchmark(b, opSHL, x, y)
|
||||
}
|
||||
func BenchmarkOpSHR(b *testing.B) {
|
||||
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
|
||||
y := "ff"
|
||||
|
||||
opBenchmark(b, opSHR, x, y)
|
||||
}
|
||||
func BenchmarkOpSAR(b *testing.B) {
|
||||
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
|
||||
y := "ff"
|
||||
|
||||
opBenchmark(b, opSAR, x, y)
|
||||
}
|
||||
|
||||
+2
-2
@@ -39,8 +39,8 @@ type StateDB interface {
|
||||
SetCode(common.Address, []byte)
|
||||
GetCodeSize(common.Address) int
|
||||
|
||||
AddRefund(*big.Int)
|
||||
GetRefund() *big.Int
|
||||
AddRefund(uint64)
|
||||
GetRefund() uint64
|
||||
|
||||
GetState(common.Address, common.Hash) common.Hash
|
||||
SetState(common.Address, common.Hash, common.Hash)
|
||||
|
||||
+30
-43
@@ -20,9 +20,7 @@ import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
@@ -39,8 +37,6 @@ type Config struct {
|
||||
// NoRecursion disabled Interpreter call, callcode,
|
||||
// delegate call and create.
|
||||
NoRecursion bool
|
||||
// Disable gas metering
|
||||
DisableGasMetering bool
|
||||
// Enable recording of SHA3/keccak preimages
|
||||
EnablePreimageRecording bool
|
||||
// JumpTable contains the EVM instruction table. This
|
||||
@@ -70,6 +66,8 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
|
||||
// we'll set the default jump table.
|
||||
if !cfg.JumpTable[STOP].valid {
|
||||
switch {
|
||||
case evm.ChainConfig().IsConstantinople(evm.BlockNumber):
|
||||
cfg.JumpTable = constantinopleInstructionSet
|
||||
case evm.ChainConfig().IsByzantium(evm.BlockNumber):
|
||||
cfg.JumpTable = byzantiumInstructionSet
|
||||
case evm.ChainConfig().IsHomestead(evm.BlockNumber):
|
||||
@@ -107,9 +105,9 @@ func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack
|
||||
// the return byte-slice and an error if one occurred.
|
||||
//
|
||||
// It's important to note that any errors returned by the interpreter should be
|
||||
// considered a revert-and-consume-all-gas operation. No error specific checks
|
||||
// should be handled to reduce complexity and errors further down the in.
|
||||
func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) {
|
||||
// considered a revert-and-consume-all-gas operation except for
|
||||
// errExecutionReverted which means revert-and-keep-gas-left.
|
||||
func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
// Increment the call depth which is restricted to 1024
|
||||
in.evm.depth++
|
||||
defer func() { in.evm.depth-- }()
|
||||
@@ -123,11 +121,6 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
codehash := contract.CodeHash // codehash is used when doing jump dest caching
|
||||
if codehash == (common.Hash{}) {
|
||||
codehash = crypto.Keccak256Hash(contract.Code)
|
||||
}
|
||||
|
||||
var (
|
||||
op OpCode // current opcode
|
||||
mem = NewMemory() // bound memory
|
||||
@@ -138,39 +131,36 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
|
||||
pc = uint64(0) // program counter
|
||||
cost uint64
|
||||
// copies used by tracer
|
||||
stackCopy = newstack() // stackCopy needed for Tracer since stack is mutated by 63/64 gas rule
|
||||
pcCopy uint64 // needed for the deferred Tracer
|
||||
gasCopy uint64 // for Tracer to log gas remaining before execution
|
||||
logged bool // deferred Tracer should ignore already logged steps
|
||||
pcCopy uint64 // needed for the deferred Tracer
|
||||
gasCopy uint64 // for Tracer to log gas remaining before execution
|
||||
logged bool // deferred Tracer should ignore already logged steps
|
||||
)
|
||||
contract.Input = input
|
||||
|
||||
defer func() {
|
||||
if err != nil && !logged && in.cfg.Debug {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if in.cfg.Debug {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if !logged {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
} else {
|
||||
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
// The Interpreter main run loop (contextual). This loop runs until either an
|
||||
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
|
||||
// the execution of one of the operations or until the done flag is set by the
|
||||
// parent context.
|
||||
for atomic.LoadInt32(&in.evm.abort) == 0 {
|
||||
// Get the memory location of pc
|
||||
op = contract.GetOp(pc)
|
||||
|
||||
if in.cfg.Debug {
|
||||
logged = false
|
||||
pcCopy = pc
|
||||
gasCopy = contract.Gas
|
||||
stackCopy = newstack()
|
||||
for _, val := range stack.data {
|
||||
stackCopy.push(val)
|
||||
}
|
||||
// Capture pre-execution values for tracing.
|
||||
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
||||
}
|
||||
|
||||
// Get the operation from the jump table matching the opcode and validate the
|
||||
// stack and make sure there enough stack items available to perform the operation
|
||||
// Get the operation from the jump table and validate the stack to ensure there are
|
||||
// enough stack items available to perform the operation.
|
||||
op = contract.GetOp(pc)
|
||||
operation := in.cfg.JumpTable[op]
|
||||
if !operation.valid {
|
||||
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
|
||||
@@ -197,21 +187,18 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
|
||||
return nil, errGasUintOverflow
|
||||
}
|
||||
}
|
||||
|
||||
if !in.cfg.DisableGasMetering {
|
||||
// consume the gas and return an error if not enough gas is available.
|
||||
// cost is explicitly set so that the capture state defer method cas get the proper cost
|
||||
cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
|
||||
if err != nil || !contract.UseGas(cost) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
// consume the gas and return an error if not enough gas is available.
|
||||
// cost is explicitly set so that the capture state defer method cas get the proper cost
|
||||
cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
|
||||
if err != nil || !contract.UseGas(cost) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
if memorySize > 0 {
|
||||
mem.Resize(memorySize)
|
||||
}
|
||||
|
||||
if in.cfg.Debug {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err)
|
||||
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
logged = true
|
||||
}
|
||||
|
||||
|
||||
+30
-3
@@ -51,11 +51,38 @@ type operation struct {
|
||||
}
|
||||
|
||||
var (
|
||||
frontierInstructionSet = NewFrontierInstructionSet()
|
||||
homesteadInstructionSet = NewHomesteadInstructionSet()
|
||||
byzantiumInstructionSet = NewByzantiumInstructionSet()
|
||||
frontierInstructionSet = NewFrontierInstructionSet()
|
||||
homesteadInstructionSet = NewHomesteadInstructionSet()
|
||||
byzantiumInstructionSet = NewByzantiumInstructionSet()
|
||||
constantinopleInstructionSet = NewConstantinopleInstructionSet()
|
||||
)
|
||||
|
||||
// NewConstantinopleInstructionSet returns the frontier, homestead
|
||||
// byzantium and contantinople instructions.
|
||||
func NewConstantinopleInstructionSet() [256]operation {
|
||||
// instructions that can be executed during the byzantium phase.
|
||||
instructionSet := NewByzantiumInstructionSet()
|
||||
instructionSet[SHL] = operation{
|
||||
execute: opSHL,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
}
|
||||
instructionSet[SHR] = operation{
|
||||
execute: opSHR,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
}
|
||||
instructionSet[SAR] = operation{
|
||||
execute: opSAR,
|
||||
gasCost: constGasFunc(GasFastestStep),
|
||||
validateStack: makeStackFunc(2, 1),
|
||||
valid: true,
|
||||
}
|
||||
return instructionSet
|
||||
}
|
||||
|
||||
// NewByzantiumInstructionSet returns the frontier, homestead and
|
||||
// byzantium instructions.
|
||||
func NewByzantiumInstructionSet() [256]operation {
|
||||
|
||||
+36
-14
@@ -62,29 +62,39 @@ type StructLog struct {
|
||||
Stack []*big.Int `json:"stack"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth int `json:"depth"`
|
||||
Err error `json:"error"`
|
||||
Err error `json:"-"`
|
||||
}
|
||||
|
||||
// overrides for gencodec
|
||||
type structLogMarshaling struct {
|
||||
Stack []*math.HexOrDecimal256
|
||||
Gas math.HexOrDecimal64
|
||||
GasCost math.HexOrDecimal64
|
||||
Memory hexutil.Bytes
|
||||
OpName string `json:"opName"`
|
||||
Stack []*math.HexOrDecimal256
|
||||
Gas math.HexOrDecimal64
|
||||
GasCost math.HexOrDecimal64
|
||||
Memory hexutil.Bytes
|
||||
OpName string `json:"opName"` // adds call to OpName() in MarshalJSON
|
||||
ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON
|
||||
}
|
||||
|
||||
func (s *StructLog) OpName() string {
|
||||
return s.Op.String()
|
||||
}
|
||||
|
||||
func (s *StructLog) ErrorString() string {
|
||||
if s.Err != nil {
|
||||
return s.Err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Tracer is used to collect execution traces from an EVM transaction
|
||||
// execution. CaptureState is called for each step of the VM with the
|
||||
// current VM state.
|
||||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type Tracer interface {
|
||||
CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
|
||||
}
|
||||
|
||||
@@ -98,6 +108,8 @@ type StructLogger struct {
|
||||
|
||||
logs []StructLog
|
||||
changedValues map[common.Address]Storage
|
||||
output []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// NewStructLogger returns a new logger
|
||||
@@ -111,6 +123,10 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||
return logger
|
||||
}
|
||||
|
||||
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// CaptureState also tracks SSTORE ops to track dirty values.
|
||||
@@ -161,19 +177,25 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
fmt.Printf("0x%x", output)
|
||||
if err != nil {
|
||||
fmt.Printf(" error: %v\n", err)
|
||||
}
|
||||
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StructLogs returns a list of captured log entries
|
||||
func (l *StructLogger) StructLogs() []StructLog {
|
||||
return l.logs
|
||||
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
l.output = output
|
||||
l.err = err
|
||||
return nil
|
||||
}
|
||||
|
||||
// StructLogs returns the captured log entries.
|
||||
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
||||
|
||||
// Error returns the VM error captured by the trace.
|
||||
func (l *StructLogger) Error() error { return l.err }
|
||||
|
||||
// Output returns the VM return value captured by the trace.
|
||||
func (l *StructLogger) Output() []byte { return l.output }
|
||||
|
||||
// WriteTrace writes a formatted trace to the given writer
|
||||
func WriteTrace(writer io.Writer, logs []StructLog) {
|
||||
for _, log := range logs {
|
||||
|
||||
+2
-2
@@ -55,8 +55,8 @@ func (NoopStateDB) GetCodeHash(common.Address) common.Hash
|
||||
func (NoopStateDB) GetCode(common.Address) []byte { return nil }
|
||||
func (NoopStateDB) SetCode(common.Address, []byte) {}
|
||||
func (NoopStateDB) GetCodeSize(common.Address) int { return 0 }
|
||||
func (NoopStateDB) AddRefund(*big.Int) {}
|
||||
func (NoopStateDB) GetRefund() *big.Int { return nil }
|
||||
func (NoopStateDB) AddRefund(uint64) {}
|
||||
func (NoopStateDB) GetRefund() uint64 { return 0 }
|
||||
func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
|
||||
func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {}
|
||||
func (NoopStateDB) Suicide(common.Address) bool { return false }
|
||||
|
||||
+9
@@ -63,6 +63,9 @@ const (
|
||||
XOR
|
||||
NOT
|
||||
BYTE
|
||||
SHL
|
||||
SHR
|
||||
SAR
|
||||
|
||||
SHA3 = 0x20
|
||||
)
|
||||
@@ -234,6 +237,9 @@ var opCodeToString = map[OpCode]string{
|
||||
OR: "OR",
|
||||
XOR: "XOR",
|
||||
BYTE: "BYTE",
|
||||
SHL: "SHL",
|
||||
SHR: "SHR",
|
||||
SAR: "SAR",
|
||||
ADDMOD: "ADDMOD",
|
||||
MULMOD: "MULMOD",
|
||||
|
||||
@@ -400,6 +406,9 @@ var stringToOp = map[string]OpCode{
|
||||
"OR": OR,
|
||||
"XOR": XOR,
|
||||
"BYTE": BYTE,
|
||||
"SHL": SHL,
|
||||
"SHR": SHR,
|
||||
"SAR": SAR,
|
||||
"ADDMOD": ADDMOD,
|
||||
"MULMOD": MULMOD,
|
||||
"SHA3": SHA3,
|
||||
|
||||
+1
-3
@@ -17,8 +17,6 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@@ -35,7 +33,7 @@ func NewEnv(cfg *Config) *vm.EVM {
|
||||
BlockNumber: cfg.BlockNumber,
|
||||
Time: cfg.Time,
|
||||
Difficulty: cfg.Difficulty,
|
||||
GasLimit: new(big.Int).SetUint64(cfg.GasLimit),
|
||||
GasLimit: cfg.GasLimit,
|
||||
GasPrice: cfg.GasPrice,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user