forked from cerc-io/ipld-eth-server
update vendor dep; constrain to revision of geth with UnpackIntoMap; updates to match
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
func lexAll(src string) []token {
|
||||
ch := Lex("test.asm", []byte(src), false)
|
||||
ch := Lex([]byte(src), false)
|
||||
|
||||
var tokens []token
|
||||
for i := range ch {
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ type lexer struct {
|
||||
|
||||
// lex lexes the program by name with the given source. It returns a
|
||||
// channel on which the tokens are delivered.
|
||||
func Lex(name string, source []byte, debug bool) <-chan token {
|
||||
func Lex(source []byte, debug bool) <-chan token {
|
||||
ch := make(chan token)
|
||||
l := &lexer{
|
||||
input: string(source),
|
||||
|
||||
+5
-5
@@ -150,14 +150,14 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||
// Create the database in memory or in a temporary directory.
|
||||
var db ethdb.Database
|
||||
if !disk {
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
} else {
|
||||
dir, err := ioutil.TempDir("", "eth-core-bench")
|
||||
if err != nil {
|
||||
b.Fatalf("cannot create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
db, err = ethdb.NewLDBDatabase(dir, 128, 128)
|
||||
db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "")
|
||||
if err != nil {
|
||||
b.Fatalf("cannot create temporary database: %v", err)
|
||||
}
|
||||
@@ -255,7 +255,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||
if err != nil {
|
||||
b.Fatalf("cannot create temporary directory: %v", err)
|
||||
}
|
||||
db, err := ethdb.NewLDBDatabase(dir, 128, 1024)
|
||||
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "")
|
||||
if err != nil {
|
||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||
}
|
||||
@@ -272,7 +272,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
db, err := ethdb.NewLDBDatabase(dir, 128, 1024)
|
||||
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "")
|
||||
if err != nil {
|
||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||
}
|
||||
@@ -283,7 +283,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
db, err := ethdb.NewLDBDatabase(dir, 128, 1024)
|
||||
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "")
|
||||
if err != nil {
|
||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||
}
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ 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 uint64) error {
|
||||
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
|
||||
header := block.Header()
|
||||
if block.GasUsed() != usedGas {
|
||||
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
|
||||
|
||||
+4
-4
@@ -22,9 +22,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
func TestHeaderVerification(t *testing.T) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb = ethdb.NewMemDatabase()
|
||||
testdb = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
||||
@@ -84,7 +84,7 @@ func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVeri
|
||||
func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb = ethdb.NewMemDatabase()
|
||||
testdb = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
||||
@@ -156,7 +156,7 @@ func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion
|
||||
func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
testdb = ethdb.NewMemDatabase()
|
||||
testdb = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
genesis = gspec.MustCommit(testdb)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil)
|
||||
|
||||
+134
-87
@@ -43,10 +43,20 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
var (
|
||||
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
|
||||
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
|
||||
accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil)
|
||||
accountCommitTimer = metrics.NewRegisteredTimer("chain/account/commits", nil)
|
||||
|
||||
storageReadTimer = metrics.NewRegisteredTimer("chain/storage/reads", nil)
|
||||
storageHashTimer = metrics.NewRegisteredTimer("chain/storage/hashes", nil)
|
||||
storageUpdateTimer = metrics.NewRegisteredTimer("chain/storage/updates", nil)
|
||||
storageCommitTimer = metrics.NewRegisteredTimer("chain/storage/commits", nil)
|
||||
|
||||
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
|
||||
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
|
||||
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
|
||||
@@ -65,7 +75,13 @@ const (
|
||||
triesInMemory = 128
|
||||
|
||||
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
|
||||
BlockChainVersion uint64 = 3
|
||||
//
|
||||
// During the process of upgrading the database version from 3 to 4,
|
||||
// the following incompatible database changes were added.
|
||||
// * the `BlockNumber`, `TxHash`, `TxIndex`, `BlockHash` and `Index` fields of log are deleted
|
||||
// * the `Bloom` field of receipt is deleted
|
||||
// * the `BlockIndex` and `TxIndex` fields of txlookup are deleted
|
||||
BlockChainVersion uint64 = 4
|
||||
)
|
||||
|
||||
// CacheConfig contains the configuration values for the trie caching/pruning
|
||||
@@ -105,10 +121,10 @@ type BlockChain struct {
|
||||
chainSideFeed event.Feed
|
||||
chainHeadFeed event.Feed
|
||||
logsFeed event.Feed
|
||||
blockProcFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
genesisBlock *types.Block
|
||||
|
||||
mu sync.RWMutex // global mutex for locking chain operations
|
||||
chainmu sync.RWMutex // blockchain insertion lock
|
||||
procmu sync.RWMutex // block processor lock
|
||||
|
||||
@@ -281,11 +297,11 @@ func (bc *BlockChain) loadLastState() error {
|
||||
func (bc *BlockChain) SetHead(head uint64) error {
|
||||
log.Warn("Rewinding blockchain", "target", head)
|
||||
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
bc.chainmu.Lock()
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
// Rewind the header chain, deleting all block bodies until then
|
||||
delFn := func(db rawdb.DatabaseDeleter, hash common.Hash, num uint64) {
|
||||
delFn := func(db ethdb.Writer, hash common.Hash, num uint64) {
|
||||
rawdb.DeleteBody(db, hash, num)
|
||||
}
|
||||
bc.hc.SetHead(head, delFn)
|
||||
@@ -336,13 +352,13 @@ 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.stateCache.TrieDB(), 0); err != nil {
|
||||
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil {
|
||||
return err
|
||||
}
|
||||
// If all checks out, manually set the head block
|
||||
bc.mu.Lock()
|
||||
bc.chainmu.Lock()
|
||||
bc.currentBlock.Store(block)
|
||||
bc.mu.Unlock()
|
||||
bc.chainmu.Unlock()
|
||||
|
||||
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
||||
return nil
|
||||
@@ -420,8 +436,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
||||
if err := bc.SetHead(0); err != nil {
|
||||
return err
|
||||
}
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
bc.chainmu.Lock()
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
// Prepare the genesis block and reinitialise the chain
|
||||
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
|
||||
@@ -457,7 +473,7 @@ func (bc *BlockChain) repair(head **types.Block) error {
|
||||
if block == nil {
|
||||
return fmt.Errorf("missing block %d [%x]", (*head).NumberU64()-1, (*head).ParentHash())
|
||||
}
|
||||
(*head) = block
|
||||
*head = block
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,8 +484,8 @@ func (bc *BlockChain) Export(w io.Writer) error {
|
||||
|
||||
// ExportN writes a subset of the active chain to the given writer.
|
||||
func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
bc.chainmu.RLock()
|
||||
defer bc.chainmu.RUnlock()
|
||||
|
||||
if first > last {
|
||||
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
|
||||
@@ -490,7 +506,6 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||
reported = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -646,6 +661,9 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
return nil
|
||||
}
|
||||
receipts := rawdb.ReadReceipts(bc.db, hash, *number)
|
||||
if receipts == nil {
|
||||
return nil
|
||||
}
|
||||
bc.receiptsCache.Add(hash, receipts)
|
||||
return receipts
|
||||
}
|
||||
@@ -756,8 +774,8 @@ const (
|
||||
// Rollback is designed to remove a chain of links from the database that aren't
|
||||
// certain enough to be valid.
|
||||
func (bc *BlockChain) Rollback(chain []common.Hash) {
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
bc.chainmu.Lock()
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
for i := len(chain) - 1; i >= 0; i-- {
|
||||
hash := chain[i]
|
||||
@@ -792,6 +810,11 @@ func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts ty
|
||||
// The transaction hash can be retrieved from the transaction itself
|
||||
receipts[j].TxHash = transactions[j].Hash()
|
||||
|
||||
// block location fields
|
||||
receipts[j].BlockHash = block.Hash()
|
||||
receipts[j].BlockNumber = block.Number()
|
||||
receipts[j].TransactionIndex = uint(j)
|
||||
|
||||
// The contract address can be derived from the transaction itself
|
||||
if transactions[j].To() == nil {
|
||||
// Deriving the signer is expensive, only do if it's actually needed
|
||||
@@ -881,7 +904,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
}
|
||||
|
||||
// Update the head fast sync block if better
|
||||
bc.mu.Lock()
|
||||
bc.chainmu.Lock()
|
||||
head := blockChain[len(blockChain)-1]
|
||||
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
|
||||
currentFastBlock := bc.CurrentFastBlock()
|
||||
@@ -890,7 +913,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
bc.currentFastBlock.Store(head)
|
||||
}
|
||||
}
|
||||
bc.mu.Unlock()
|
||||
bc.chainmu.Unlock()
|
||||
|
||||
context := []interface{}{
|
||||
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
|
||||
@@ -924,6 +947,15 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e
|
||||
|
||||
// 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.chainmu.Lock()
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
return bc.writeBlockWithState(block, receipts, state)
|
||||
}
|
||||
|
||||
// writeBlockWithState writes the block and all associated state to the database,
|
||||
// but is expects the chain mutex to be held.
|
||||
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()
|
||||
|
||||
@@ -933,9 +965,6 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||
return NonStatTy, consensus.ErrUnknownAncestor
|
||||
}
|
||||
// Make sure no inconsistent state is leaked during insertion
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
currentBlock := bc.CurrentBlock()
|
||||
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
|
||||
externTd := new(big.Int).Add(block.Difficulty(), ptd)
|
||||
@@ -1077,15 +1106,25 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
if len(chain) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
bc.blockProcFeed.Send(true)
|
||||
defer bc.blockProcFeed.Send(false)
|
||||
|
||||
// Remove already known canon-blocks
|
||||
var (
|
||||
block, prev *types.Block
|
||||
)
|
||||
// Do a sanity check that the provided chain is actually ordered and linked
|
||||
for i := 1; i < len(chain); i++ {
|
||||
if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() {
|
||||
block = chain[i]
|
||||
prev = chain[i-1]
|
||||
if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
|
||||
// Chain broke ancestry, log a message (programming error) and skip insertion
|
||||
log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(),
|
||||
"parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash())
|
||||
log.Error("Non contiguous block insert", "number", block.Number(), "hash", block.Hash(),
|
||||
"parent", block.ParentHash(), "prevnumber", prev.Number(), "prevhash", prev.Hash())
|
||||
|
||||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(),
|
||||
chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])
|
||||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, prev.NumberU64(),
|
||||
prev.Hash().Bytes()[:4], i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4])
|
||||
}
|
||||
}
|
||||
// Pre-checks passed, start the full block imports
|
||||
@@ -1139,6 +1178,22 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||
it := newInsertIterator(chain, results, bc.Validator())
|
||||
|
||||
block, err := it.next()
|
||||
|
||||
// Left-trim all the known blocks
|
||||
if err == ErrKnownBlock {
|
||||
// First block (and state) is known
|
||||
// 1. We did a roll-back, and should now do a re-import
|
||||
// 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
|
||||
// from the canonical chain, which has not been verified.
|
||||
// Skip all known blocks that are behind us
|
||||
current := bc.CurrentBlock().NumberU64()
|
||||
for block != nil && err == ErrKnownBlock && current >= block.NumberU64() {
|
||||
stats.ignored++
|
||||
block, err = it.next()
|
||||
}
|
||||
// Falls through to the block import
|
||||
}
|
||||
|
||||
switch {
|
||||
// First block is pruned, insert as sidechain and reorg only if TD grows enough
|
||||
case err == consensus.ErrPrunedAncestor:
|
||||
@@ -1158,20 +1213,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||
// If there are any still remaining, mark as ignored
|
||||
return it.index, events, coalescedLogs, err
|
||||
|
||||
// First block (and state) is known
|
||||
// 1. We did a roll-back, and should now do a re-import
|
||||
// 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
|
||||
// from the canonical chain, which has not been verified.
|
||||
case err == ErrKnownBlock:
|
||||
// Skip all known blocks that behind us
|
||||
current := bc.CurrentBlock().NumberU64()
|
||||
|
||||
for block != nil && err == ErrKnownBlock && current >= block.NumberU64() {
|
||||
stats.ignored++
|
||||
block, err = it.next()
|
||||
}
|
||||
// Falls through to the block import
|
||||
|
||||
// Some other error occurred, abort
|
||||
case err != nil:
|
||||
stats.ignored += len(it.chain)
|
||||
@@ -1195,38 +1236,58 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||
|
||||
parent := it.previous()
|
||||
if parent == nil {
|
||||
parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||
}
|
||||
state, err := state.New(parent.Root(), bc.stateCache)
|
||||
state, err := state.New(parent.Root, bc.stateCache)
|
||||
if err != nil {
|
||||
return it.index, events, coalescedLogs, err
|
||||
}
|
||||
// Process block using the parent state as reference point.
|
||||
t0 := time.Now()
|
||||
substart := time.Now()
|
||||
receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)
|
||||
t1 := time.Now()
|
||||
if err != nil {
|
||||
bc.reportBlock(block, receipts, err)
|
||||
return it.index, events, coalescedLogs, err
|
||||
}
|
||||
// Update the metrics touched during block processing
|
||||
accountReadTimer.Update(state.AccountReads) // Account reads are complete, we can mark them
|
||||
storageReadTimer.Update(state.StorageReads) // Storage reads are complete, we can mark them
|
||||
accountUpdateTimer.Update(state.AccountUpdates) // Account updates are complete, we can mark them
|
||||
storageUpdateTimer.Update(state.StorageUpdates) // Storage updates are complete, we can mark them
|
||||
|
||||
triehash := state.AccountHashes + state.StorageHashes // Save to not double count in validation
|
||||
trieproc := state.AccountReads + state.AccountUpdates
|
||||
trieproc += state.StorageReads + state.StorageUpdates
|
||||
|
||||
blockExecutionTimer.Update(time.Since(substart) - trieproc - triehash)
|
||||
|
||||
// Validate the state using the default validator
|
||||
if err := bc.Validator().ValidateState(block, parent, state, receipts, usedGas); err != nil {
|
||||
substart = time.Now()
|
||||
if err := bc.Validator().ValidateState(block, state, receipts, usedGas); err != nil {
|
||||
bc.reportBlock(block, receipts, err)
|
||||
return it.index, events, coalescedLogs, err
|
||||
}
|
||||
t2 := time.Now()
|
||||
proctime := time.Since(start)
|
||||
|
||||
// Update the metrics touched during block validation
|
||||
accountHashTimer.Update(state.AccountHashes) // Account hashes are complete, we can mark them
|
||||
storageHashTimer.Update(state.StorageHashes) // Storage hashes are complete, we can mark them
|
||||
|
||||
blockValidationTimer.Update(time.Since(substart) - (state.AccountHashes + state.StorageHashes - triehash))
|
||||
|
||||
// Write the block to the chain and get the status.
|
||||
status, err := bc.WriteBlockWithState(block, receipts, state)
|
||||
t3 := time.Now()
|
||||
substart = time.Now()
|
||||
status, err := bc.writeBlockWithState(block, receipts, state)
|
||||
if err != nil {
|
||||
return it.index, events, coalescedLogs, err
|
||||
}
|
||||
// Update the metrics touched during block commit
|
||||
accountCommitTimer.Update(state.AccountCommits) // Account commits are complete, we can mark them
|
||||
storageCommitTimer.Update(state.StorageCommits) // Storage commits are complete, we can mark them
|
||||
|
||||
blockWriteTimer.Update(time.Since(substart) - state.AccountCommits - state.StorageCommits)
|
||||
blockInsertTimer.UpdateSince(start)
|
||||
blockExecutionTimer.Update(t1.Sub(t0))
|
||||
blockValidationTimer.Update(t2.Sub(t1))
|
||||
blockWriteTimer.Update(t3.Sub(t2))
|
||||
|
||||
switch status {
|
||||
case CanonStatTy:
|
||||
log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
|
||||
@@ -1248,12 +1309,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||
"root", block.Root())
|
||||
events = append(events, ChainSideEvent{block})
|
||||
}
|
||||
blockInsertTimer.UpdateSince(start)
|
||||
stats.processed++
|
||||
stats.usedGas += usedGas
|
||||
|
||||
cache, _ := bc.stateCache.TrieDB().Size()
|
||||
stats.report(chain, it.index, cache)
|
||||
dirty, _ := bc.stateCache.TrieDB().Size()
|
||||
stats.report(chain, it.index, dirty)
|
||||
}
|
||||
// Any blocks remaining here? The only ones we care about are the future ones
|
||||
if block != nil && err == consensus.ErrFutureBlock {
|
||||
@@ -1287,7 +1347,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||
func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (int, []interface{}, []*types.Log, error) {
|
||||
var (
|
||||
externTd *big.Int
|
||||
current = bc.CurrentBlock().NumberU64()
|
||||
current = bc.CurrentBlock()
|
||||
)
|
||||
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
||||
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining
|
||||
@@ -1296,8 +1356,13 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i
|
||||
err := consensus.ErrPrunedAncestor
|
||||
for ; block != nil && (err == consensus.ErrPrunedAncestor); block, err = it.next() {
|
||||
// Check the canonical state root for that number
|
||||
if number := block.NumberU64(); current >= number {
|
||||
if canonical := bc.GetBlockByNumber(number); canonical != nil && canonical.Root() == block.Root() {
|
||||
if number := block.NumberU64(); current.NumberU64() >= number {
|
||||
canonical := bc.GetBlockByNumber(number)
|
||||
if canonical != nil && canonical.Hash() == block.Hash() {
|
||||
// Not a sidechain block, this is a re-import of a canon block which has it's state pruned
|
||||
continue
|
||||
}
|
||||
if canonical != nil && canonical.Root() == block.Root() {
|
||||
// This is most likely a shadow-state attack. When a fork is imported into the
|
||||
// database, and it eventually reaches a block height which is not pruned, we
|
||||
// just found that the state already exist! This means that the sidechain block
|
||||
@@ -1335,9 +1400,9 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i
|
||||
//
|
||||
// If the externTd was larger than our local TD, we now need to reimport the previous
|
||||
// blocks to regenerate the required state
|
||||
localTd := bc.GetTd(bc.CurrentBlock().Hash(), current)
|
||||
localTd := bc.GetTd(current.Hash(), current.NumberU64())
|
||||
if localTd.Cmp(externTd) > 0 {
|
||||
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().NumberU64(), "sidetd", externTd, "localtd", localTd)
|
||||
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
|
||||
return it.index, nil, nil, err
|
||||
}
|
||||
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
||||
@@ -1345,7 +1410,7 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i
|
||||
hashes []common.Hash
|
||||
numbers []uint64
|
||||
)
|
||||
parent := bc.GetHeader(it.previous().Hash(), it.previous().NumberU64())
|
||||
parent := it.previous()
|
||||
for parent != nil && !bc.HasState(parent.Root) {
|
||||
hashes = append(hashes, parent.Hash())
|
||||
numbers = append(numbers, parent.Number.Uint64())
|
||||
@@ -1603,36 +1668,12 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
|
||||
defer bc.wg.Done()
|
||||
|
||||
whFunc := func(header *types.Header) error {
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
_, err := bc.hc.WriteHeader(header)
|
||||
return err
|
||||
}
|
||||
|
||||
return bc.hc.InsertHeaderChain(chain, whFunc, start)
|
||||
}
|
||||
|
||||
// writeHeader writes a header into the local chain, given that its parent is
|
||||
// already known. If the total difficulty of the newly inserted header becomes
|
||||
// greater than the current known TD, the canonical chain is re-routed.
|
||||
//
|
||||
// Note: This method is not concurrent-safe with inserting blocks simultaneously
|
||||
// into the chain, as side effects caused by reorganisations cannot be emulated
|
||||
// without the real blocks. Hence, writing headers directly should only be done
|
||||
// in two scenarios: pure-header mode of operation (light clients), or properly
|
||||
// separated header/block phases (non-archive clients).
|
||||
func (bc *BlockChain) writeHeader(header *types.Header) error {
|
||||
bc.wg.Add(1)
|
||||
defer bc.wg.Done()
|
||||
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
_, err := bc.hc.WriteHeader(header)
|
||||
return err
|
||||
}
|
||||
|
||||
// CurrentHeader retrieves the current head header of the canonical chain. The
|
||||
// header is retrieved from the HeaderChain's internal cache.
|
||||
func (bc *BlockChain) CurrentHeader() *types.Header {
|
||||
@@ -1681,8 +1722,8 @@ func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []com
|
||||
//
|
||||
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
|
||||
func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
|
||||
bc.chainmu.Lock()
|
||||
defer bc.chainmu.Unlock()
|
||||
bc.chainmu.RLock()
|
||||
defer bc.chainmu.RUnlock()
|
||||
|
||||
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
||||
}
|
||||
@@ -1723,3 +1764,9 @@ func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Su
|
||||
func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||
return bc.scope.Track(bc.logsFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// SubscribeBlockProcessingEvent registers a subscription of bool where true means
|
||||
// block processing has started while false means it has stopped.
|
||||
func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscription {
|
||||
return bc.scope.Track(bc.blockProcFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
+5
-5
@@ -39,7 +39,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, cache common.StorageSize) {
|
||||
func (st *insertStats) report(chain []*types.Block, index int, dirty common.StorageSize) {
|
||||
// Fetch the timings for the batch
|
||||
var (
|
||||
now = mclock.Now()
|
||||
@@ -63,7 +63,7 @@ func (st *insertStats) report(chain []*types.Block, index int, cache common.Stor
|
||||
if timestamp := time.Unix(end.Time().Int64(), 0); time.Since(timestamp) > time.Minute {
|
||||
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
|
||||
}
|
||||
context = append(context, []interface{}{"cache", cache}...)
|
||||
context = append(context, []interface{}{"dirty", dirty}...)
|
||||
|
||||
if st.queued > 0 {
|
||||
context = append(context, []interface{}{"queued", st.queued}...)
|
||||
@@ -111,12 +111,12 @@ func (it *insertIterator) next() (*types.Block, error) {
|
||||
return it.chain[it.index], it.validator.ValidateBody(it.chain[it.index])
|
||||
}
|
||||
|
||||
// previous returns the previous block was being processed, or nil
|
||||
func (it *insertIterator) previous() *types.Block {
|
||||
// previous returns the previous header that was being processed, or nil.
|
||||
func (it *insertIterator) previous() *types.Header {
|
||||
if it.index < 1 {
|
||||
return nil
|
||||
}
|
||||
return it.chain[it.index-1]
|
||||
return it.chain[it.index-1].Header()
|
||||
}
|
||||
|
||||
// first returns the first block in the it.
|
||||
|
||||
+111
-46
@@ -17,7 +17,6 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"sync"
|
||||
@@ -47,7 +46,7 @@ var (
|
||||
// header only chain.
|
||||
func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||
var (
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
genesis = new(Genesis).MustCommit(db)
|
||||
)
|
||||
|
||||
@@ -126,13 +125,6 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara
|
||||
comparator(tdPre, tdPost)
|
||||
}
|
||||
|
||||
func printChain(bc *BlockChain) {
|
||||
for i := bc.CurrentBlock().Number().Uint64(); i > 0; i-- {
|
||||
b := bc.GetBlockByNumber(uint64(i))
|
||||
fmt.Printf("\t%x %v\n", b.Hash(), b.Difficulty())
|
||||
}
|
||||
}
|
||||
|
||||
// testBlockChainImport tries to process a chain of blocks, writing them into
|
||||
// the database if successful.
|
||||
func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
@@ -157,16 +149,16 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
}
|
||||
err = blockchain.validator.ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas)
|
||||
err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas)
|
||||
if err != nil {
|
||||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
}
|
||||
blockchain.mu.Lock()
|
||||
blockchain.chainmu.Lock()
|
||||
rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
||||
rawdb.WriteBlock(blockchain.db, block)
|
||||
statedb.Commit(false)
|
||||
blockchain.mu.Unlock()
|
||||
blockchain.chainmu.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -180,23 +172,14 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
|
||||
return err
|
||||
}
|
||||
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
|
||||
blockchain.mu.Lock()
|
||||
blockchain.chainmu.Lock()
|
||||
rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
|
||||
rawdb.WriteHeader(blockchain.db, header)
|
||||
blockchain.mu.Unlock()
|
||||
blockchain.chainmu.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *testing.T) {
|
||||
_, err := blockchain.InsertChain(chain)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
done <- true
|
||||
}
|
||||
|
||||
func TestLastBlock(t *testing.T) {
|
||||
_, blockchain, err := newCanonical(ethash.NewFaker(), 0, true)
|
||||
if err != nil {
|
||||
@@ -603,7 +586,7 @@ func testInsertNonceError(t *testing.T, full bool) {
|
||||
func TestFastVsFullChains(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
gendb = ethdb.NewMemDatabase()
|
||||
gendb = rawdb.NewMemoryDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
@@ -633,7 +616,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
}
|
||||
})
|
||||
// Import the chain as an archive node for the comparison baseline
|
||||
archiveDb := ethdb.NewMemDatabase()
|
||||
archiveDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(archiveDb)
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer archive.Stop()
|
||||
@@ -642,7 +625,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
}
|
||||
// Fast import the chain as a non-archive node to test
|
||||
fastDb := ethdb.NewMemDatabase()
|
||||
fastDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer fast.Stop()
|
||||
@@ -691,7 +674,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
gendb = ethdb.NewMemDatabase()
|
||||
gendb = rawdb.NewMemoryDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
@@ -719,7 +702,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
}
|
||||
}
|
||||
// Import the chain as an archive node and ensure all pointers are updated
|
||||
archiveDb := ethdb.NewMemDatabase()
|
||||
archiveDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(archiveDb)
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||
@@ -733,7 +716,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
assert(t, "archive", archive, height/2, height/2, height/2)
|
||||
|
||||
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||
fastDb := ethdb.NewMemDatabase()
|
||||
fastDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer fast.Stop()
|
||||
@@ -753,7 +736,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
assert(t, "fast", fast, height/2, height/2, 0)
|
||||
|
||||
// Import the chain as a light node and ensure all pointers are updated
|
||||
lightDb := ethdb.NewMemDatabase()
|
||||
lightDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(lightDb)
|
||||
|
||||
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||
@@ -776,7 +759,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
GasLimit: 3141592,
|
||||
@@ -888,7 +871,7 @@ func TestLogReorgs(t *testing.T) {
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
// this code generates a log
|
||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||
@@ -932,7 +915,7 @@ func TestLogReorgs(t *testing.T) {
|
||||
|
||||
func TestReorgSideEvent(t *testing.T) {
|
||||
var (
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
gspec = &Genesis{
|
||||
@@ -1060,7 +1043,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
func TestEIP155Transition(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
@@ -1163,7 +1146,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||
func TestEIP161AccountRemoval(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
@@ -1235,7 +1218,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
@@ -1251,7 +1234,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||
}
|
||||
// Import the canonical and fork chain side by side, verifying the current block
|
||||
// and current header consistency
|
||||
diskdb := ethdb.NewMemDatabase()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
@@ -1280,7 +1263,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
@@ -1295,7 +1278,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||
forks[i] = fork[0]
|
||||
}
|
||||
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
|
||||
diskdb := ethdb.NewMemDatabase()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
@@ -1326,7 +1309,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
||||
// Generate the original common chain segment and the two competing forks
|
||||
engine := ethash.NewFaker()
|
||||
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
|
||||
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
@@ -1334,7 +1317,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
||||
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()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
@@ -1394,7 +1377,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||
)
|
||||
// Generate the original common chain segment and the two competing forks
|
||||
engine := ethash.NewFaker()
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := gspec.MustCommit(db)
|
||||
|
||||
blockGenerator := func(i int, block *BlockGen) {
|
||||
@@ -1416,7 +1399,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Import the shared chain and the original canonical one
|
||||
diskdb := ethdb.NewMemDatabase()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
@@ -1429,7 +1412,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||
}
|
||||
b.StopTimer()
|
||||
if got := chain.CurrentBlock().Transactions().Len(); got != numTxs*numBlocks {
|
||||
b.Fatalf("Transactions were not included, expected %d, got %d", (numTxs * numBlocks), got)
|
||||
b.Fatalf("Transactions were not included, expected %d, got %d", numTxs*numBlocks, got)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1494,7 +1477,7 @@ func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
|
||||
func TestLowDiffLongChain(t *testing.T) {
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
|
||||
// We must use a pretty long chain to ensure that the fork doesn't overtake us
|
||||
@@ -1505,7 +1488,7 @@ func TestLowDiffLongChain(t *testing.T) {
|
||||
})
|
||||
|
||||
// Import the canonical chain
|
||||
diskdb := ethdb.NewMemDatabase()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
@@ -1538,3 +1521,85 @@ func TestLowDiffLongChain(t *testing.T) {
|
||||
header = chain.GetHeader(header.ParentHash, number-1)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that importing a sidechain (S), where
|
||||
// - S is sidechain, containing blocks [Sn...Sm]
|
||||
// - C is canon chain, containing blocks [G..Cn..Cm]
|
||||
// - A common ancestor is placed at prune-point + blocksBetweenCommonAncestorAndPruneblock
|
||||
// - The sidechain S is prepended with numCanonBlocksInSidechain blocks from the canon chain
|
||||
func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommonAncestorAndPruneblock int) {
|
||||
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
|
||||
// Generate and import the canonical chain
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, nil)
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
lastPrunedIndex := len(blocks) - triesInMemory - 1
|
||||
lastPrunedBlock := blocks[lastPrunedIndex]
|
||||
firstNonPrunedBlock := blocks[len(blocks)-triesInMemory]
|
||||
|
||||
// Verify pruning of lastPrunedBlock
|
||||
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
|
||||
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
|
||||
}
|
||||
// Verify firstNonPrunedBlock is not pruned
|
||||
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
|
||||
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
|
||||
}
|
||||
// Generate the sidechain
|
||||
// First block should be a known block, block after should be a pruned block. So
|
||||
// canon(pruned), side, side...
|
||||
|
||||
// Generate fork chain, make it longer than canon
|
||||
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
|
||||
parent := blocks[parentIndex]
|
||||
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*triesInMemory, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{2})
|
||||
})
|
||||
// Prepend the parent(s)
|
||||
var sidechain []*types.Block
|
||||
for i := numCanonBlocksInSidechain; i > 0; i-- {
|
||||
sidechain = append(sidechain, blocks[parentIndex+1-i])
|
||||
}
|
||||
sidechain = append(sidechain, fork...)
|
||||
_, err = chain.InsertChain(sidechain)
|
||||
if err != nil {
|
||||
t.Errorf("Got error, %v", err)
|
||||
}
|
||||
head := chain.CurrentBlock()
|
||||
if got := fork[len(fork)-1].Hash(); got != head.Hash() {
|
||||
t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that importing a sidechain (S), where
|
||||
// - S is sidechain, containing blocks [Sn...Sm]
|
||||
// - C is canon chain, containing blocks [G..Cn..Cm]
|
||||
// - The common ancestor Cc is pruned
|
||||
// - The first block in S: Sn, is == Cn
|
||||
// That is: the sidechain for import contains some blocks already present in canon chain.
|
||||
// So the blocks are
|
||||
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
||||
// ^ ^ ^ pruned
|
||||
func TestPrunedImportSide(t *testing.T) {
|
||||
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
|
||||
//glogger.Verbosity(3)
|
||||
//log.Root().SetHandler(log.Handler(glogger))
|
||||
testSideImport(t, 3, 3)
|
||||
testSideImport(t, 3, -3)
|
||||
testSideImport(t, 10, 0)
|
||||
testSideImport(t, 1, 10)
|
||||
testSideImport(t, 1, -10)
|
||||
}
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ type ChainIndexer struct {
|
||||
// NewChainIndexer creates a new chain indexer to do background processing on
|
||||
// chain segments of a given size after certain number of confirmations passed.
|
||||
// The throttling parameter might be used to prevent database thrashing.
|
||||
func NewChainIndexer(chainDb, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer {
|
||||
func NewChainIndexer(chainDb ethdb.Database, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer {
|
||||
c := &ChainIndexer{
|
||||
chainDb: chainDb,
|
||||
indexDb: indexDb,
|
||||
|
||||
+2
-3
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
// Runs multiple tests with randomized parameters.
|
||||
@@ -49,7 +48,7 @@ func TestChainIndexerWithChildren(t *testing.T) {
|
||||
// multiple backends. The section size and required confirmation count parameters
|
||||
// are randomized.
|
||||
func testChainIndexer(t *testing.T, count int) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
defer db.Close()
|
||||
|
||||
// Create a chain of indexers and ensure they all report empty
|
||||
@@ -60,7 +59,7 @@ func testChainIndexer(t *testing.T, count int) {
|
||||
confirmsReq = uint64(rand.Intn(10))
|
||||
)
|
||||
backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)}
|
||||
backends[i].indexer = NewChainIndexer(db, ethdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i))
|
||||
backends[i].indexer = NewChainIndexer(db, rawdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i))
|
||||
|
||||
if sections, _, _ := backends[i].indexer.Sections(); sections != 0 {
|
||||
t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0)
|
||||
|
||||
+2
-2
@@ -21,10 +21,10 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ func ExampleGenerateChain() {
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
)
|
||||
|
||||
// Ensure that key1 has some funds in the genesis block.
|
||||
|
||||
+8
-8
@@ -21,8 +21,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
@@ -32,13 +32,13 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
forkBlock := big.NewInt(32)
|
||||
|
||||
// Generate a common prefix for both pro-forkers and non-forkers
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
gspec := new(Genesis)
|
||||
genesis := gspec.MustCommit(db)
|
||||
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()
|
||||
proDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(proDb)
|
||||
|
||||
proConf := *params.TestChainConfig
|
||||
@@ -48,7 +48,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer proBc.Stop()
|
||||
|
||||
conDb := ethdb.NewMemDatabase()
|
||||
conDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(conDb)
|
||||
|
||||
conConf := *params.TestChainConfig
|
||||
@@ -67,7 +67,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
// Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks
|
||||
for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ {
|
||||
// Create a pro-fork block, and try to feed into the no-fork chain
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer bc.Stop()
|
||||
@@ -92,7 +92,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
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()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer bc.Stop()
|
||||
@@ -118,7 +118,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer bc.Stop()
|
||||
@@ -138,7 +138,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
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()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
|
||||
defer bc.Stop()
|
||||
|
||||
+2
-2
@@ -134,7 +134,7 @@ type GenesisMismatchError struct {
|
||||
}
|
||||
|
||||
func (e *GenesisMismatchError) Error() string {
|
||||
return fmt.Sprintf("database already contains an incompatible genesis block (have %x, new %x)", e.Stored[:8], e.New[:8])
|
||||
return fmt.Sprintf("database contains incompatible genesis (have %x, new %x)", e.Stored, e.New)
|
||||
}
|
||||
|
||||
// SetupGenesisBlock writes or updates the genesis block in db.
|
||||
@@ -228,7 +228,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||
// to the given database (or discards it if nil).
|
||||
func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||
if db == nil {
|
||||
db = ethdb.NewMemDatabase()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
}
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
for addr, account := range g.Alloc {
|
||||
|
||||
+1
-1
@@ -141,7 +141,7 @@ func TestSetupGenesis(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
config, hash, err := test.fn(db)
|
||||
// Check the return values.
|
||||
if !reflect.DeepEqual(err, test.wantErr) {
|
||||
|
||||
+12
-8
@@ -33,7 +33,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -219,14 +219,18 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
|
||||
|
||||
// Generate the list of seal verification requests, and start the parallel verifier
|
||||
seals := make([]bool, len(chain))
|
||||
for i := 0; i < len(seals)/checkFreq; i++ {
|
||||
index := i*checkFreq + hc.rand.Intn(checkFreq)
|
||||
if index >= len(seals) {
|
||||
index = len(seals) - 1
|
||||
if checkFreq != 0 {
|
||||
// In case of checkFreq == 0 all seals are left false.
|
||||
for i := 0; i < len(seals)/checkFreq; i++ {
|
||||
index := i*checkFreq + hc.rand.Intn(checkFreq)
|
||||
if index >= len(seals) {
|
||||
index = len(seals) - 1
|
||||
}
|
||||
seals[index] = true
|
||||
}
|
||||
seals[index] = true
|
||||
// Last should always be verified to avoid junk.
|
||||
seals[len(seals)-1] = true
|
||||
}
|
||||
seals[len(seals)-1] = true // Last should always be verified to avoid junk
|
||||
|
||||
abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
|
||||
defer close(abort)
|
||||
@@ -451,7 +455,7 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
|
||||
|
||||
// DeleteCallback is a callback function that is called by SetHead before
|
||||
// each header is deleted.
|
||||
type DeleteCallback func(rawdb.DatabaseDeleter, common.Hash, uint64)
|
||||
type DeleteCallback func(ethdb.Writer, common.Hash, uint64)
|
||||
|
||||
// SetHead rewinds the local chain to a new head. Everything above the new head
|
||||
// will be deleted and the new one set.
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@ package core
|
||||
import (
|
||||
"container/list"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
@@ -78,7 +79,7 @@ func (tm *TestManager) Db() ethdb.Database {
|
||||
func NewTestManager() *TestManager {
|
||||
testManager := &TestManager{}
|
||||
testManager.eventMux = new(event.TypeMux)
|
||||
testManager.db = ethdb.NewMemDatabase()
|
||||
testManager.db = rawdb.NewMemoryDatabase()
|
||||
// testManager.txPool = NewTxPool(testManager)
|
||||
// testManager.blockChain = NewBlockChain(testManager)
|
||||
// testManager.stateManager = NewStateManager(testManager)
|
||||
|
||||
+2
-1
@@ -52,7 +52,8 @@ func makelist(g *core.Genesis) allocList {
|
||||
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
|
||||
panic(fmt.Sprintf("can't encode account %x", addr))
|
||||
}
|
||||
a = append(a, allocItem{addr.Big(), account.Balance})
|
||||
bigAddr := new(big.Int).SetBytes(addr.Bytes())
|
||||
a = append(a, allocItem{bigAddr, account.Balance})
|
||||
}
|
||||
sort.Sort(a)
|
||||
return a
|
||||
|
||||
+81
-40
@@ -23,12 +23,13 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
|
||||
func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash {
|
||||
func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
|
||||
data, _ := db.Get(headerHashKey(number))
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
@@ -37,21 +38,21 @@ func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash {
|
||||
}
|
||||
|
||||
// WriteCanonicalHash stores the hash assigned to a canonical block number.
|
||||
func WriteCanonicalHash(db DatabaseWriter, hash common.Hash, number uint64) {
|
||||
func WriteCanonicalHash(db ethdb.Writer, hash common.Hash, number uint64) {
|
||||
if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store number to hash mapping", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCanonicalHash removes the number to hash canonical mapping.
|
||||
func DeleteCanonicalHash(db DatabaseDeleter, number uint64) {
|
||||
func DeleteCanonicalHash(db ethdb.Writer, number uint64) {
|
||||
if err := db.Delete(headerHashKey(number)); err != nil {
|
||||
log.Crit("Failed to delete number to hash mapping", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeaderNumber returns the header number assigned to a hash.
|
||||
func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 {
|
||||
func ReadHeaderNumber(db ethdb.Reader, hash common.Hash) *uint64 {
|
||||
data, _ := db.Get(headerNumberKey(hash))
|
||||
if len(data) != 8 {
|
||||
return nil
|
||||
@@ -61,7 +62,7 @@ func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 {
|
||||
}
|
||||
|
||||
// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
|
||||
func ReadHeadHeaderHash(db DatabaseReader) common.Hash {
|
||||
func ReadHeadHeaderHash(db ethdb.Reader) common.Hash {
|
||||
data, _ := db.Get(headHeaderKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
@@ -70,14 +71,14 @@ func ReadHeadHeaderHash(db DatabaseReader) common.Hash {
|
||||
}
|
||||
|
||||
// WriteHeadHeaderHash stores the hash of the current canonical head header.
|
||||
func WriteHeadHeaderHash(db DatabaseWriter, hash common.Hash) {
|
||||
func WriteHeadHeaderHash(db ethdb.Writer, hash common.Hash) {
|
||||
if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last header's hash", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeadBlockHash retrieves the hash of the current canonical head block.
|
||||
func ReadHeadBlockHash(db DatabaseReader) common.Hash {
|
||||
func ReadHeadBlockHash(db ethdb.Reader) common.Hash {
|
||||
data, _ := db.Get(headBlockKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
@@ -86,14 +87,14 @@ func ReadHeadBlockHash(db DatabaseReader) common.Hash {
|
||||
}
|
||||
|
||||
// WriteHeadBlockHash stores the head block's hash.
|
||||
func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) {
|
||||
func WriteHeadBlockHash(db ethdb.Writer, hash common.Hash) {
|
||||
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last block's hash", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
|
||||
func ReadHeadFastBlockHash(db DatabaseReader) common.Hash {
|
||||
func ReadHeadFastBlockHash(db ethdb.Reader) common.Hash {
|
||||
data, _ := db.Get(headFastBlockKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
@@ -102,7 +103,7 @@ func ReadHeadFastBlockHash(db DatabaseReader) common.Hash {
|
||||
}
|
||||
|
||||
// WriteHeadFastBlockHash stores the hash of the current fast-sync head block.
|
||||
func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) {
|
||||
func WriteHeadFastBlockHash(db ethdb.Writer, hash common.Hash) {
|
||||
if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last fast block's hash", "err", err)
|
||||
}
|
||||
@@ -110,7 +111,7 @@ func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) {
|
||||
|
||||
// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
|
||||
// reporting correct numbers across restarts.
|
||||
func ReadFastTrieProgress(db DatabaseReader) uint64 {
|
||||
func ReadFastTrieProgress(db ethdb.Reader) uint64 {
|
||||
data, _ := db.Get(fastTrieProgressKey)
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
@@ -120,20 +121,20 @@ func ReadFastTrieProgress(db DatabaseReader) uint64 {
|
||||
|
||||
// WriteFastTrieProgress stores the fast sync trie process counter to support
|
||||
// retrieving it across restarts.
|
||||
func WriteFastTrieProgress(db DatabaseWriter, count uint64) {
|
||||
func WriteFastTrieProgress(db ethdb.Writer, count uint64) {
|
||||
if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
|
||||
log.Crit("Failed to store fast sync trie progress", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||
func ReadHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(headerKey(number, hash))
|
||||
return data
|
||||
}
|
||||
|
||||
// HasHeader verifies the existence of a block header corresponding to the hash.
|
||||
func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool {
|
||||
func HasHeader(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -141,7 +142,7 @@ func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool {
|
||||
}
|
||||
|
||||
// ReadHeader retrieves the block header corresponding to the hash.
|
||||
func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Header {
|
||||
func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header {
|
||||
data := ReadHeaderRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
@@ -156,7 +157,7 @@ func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Heade
|
||||
|
||||
// WriteHeader stores a block header into the database and also stores the hash-
|
||||
// to-number mapping.
|
||||
func WriteHeader(db DatabaseWriter, header *types.Header) {
|
||||
func WriteHeader(db ethdb.Writer, header *types.Header) {
|
||||
// Write the hash -> number mapping
|
||||
var (
|
||||
hash = header.Hash()
|
||||
@@ -179,30 +180,36 @@ func WriteHeader(db DatabaseWriter, header *types.Header) {
|
||||
}
|
||||
|
||||
// DeleteHeader removes all block header data associated with a hash.
|
||||
func DeleteHeader(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(headerKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete header", "err", err)
|
||||
}
|
||||
func DeleteHeader(db ethdb.Writer, hash common.Hash, number uint64) {
|
||||
deleteHeaderWithoutNumber(db, hash, number)
|
||||
if err := db.Delete(headerNumberKey(hash)); err != nil {
|
||||
log.Crit("Failed to delete hash to number mapping", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// deleteHeaderWithoutNumber removes only the block header but does not remove
|
||||
// the hash to number mapping.
|
||||
func deleteHeaderWithoutNumber(db ethdb.Writer, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(headerKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete header", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
|
||||
func ReadBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(blockBodyKey(number, hash))
|
||||
return data
|
||||
}
|
||||
|
||||
// WriteBodyRLP stores an RLP encoded block body into the database.
|
||||
func WriteBodyRLP(db DatabaseWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
|
||||
func WriteBodyRLP(db ethdb.Writer, hash common.Hash, number uint64, rlp rlp.RawValue) {
|
||||
if err := db.Put(blockBodyKey(number, hash), rlp); err != nil {
|
||||
log.Crit("Failed to store block body", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// HasBody verifies the existence of a block body corresponding to the hash.
|
||||
func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool {
|
||||
func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -210,7 +217,7 @@ func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool {
|
||||
}
|
||||
|
||||
// ReadBody retrieves the block body corresponding to the hash.
|
||||
func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body {
|
||||
func ReadBody(db ethdb.Reader, hash common.Hash, number uint64) *types.Body {
|
||||
data := ReadBodyRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
@@ -224,7 +231,7 @@ func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body {
|
||||
}
|
||||
|
||||
// WriteBody storea a block body into the database.
|
||||
func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.Body) {
|
||||
func WriteBody(db ethdb.Writer, hash common.Hash, number uint64, body *types.Body) {
|
||||
data, err := rlp.EncodeToBytes(body)
|
||||
if err != nil {
|
||||
log.Crit("Failed to RLP encode body", "err", err)
|
||||
@@ -233,15 +240,21 @@ func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.B
|
||||
}
|
||||
|
||||
// DeleteBody removes all block body data associated with a hash.
|
||||
func DeleteBody(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
func DeleteBody(db ethdb.Writer, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(blockBodyKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete block body", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadTd retrieves a block's total difficulty corresponding to the hash.
|
||||
func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int {
|
||||
// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding.
|
||||
func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(headerTDKey(number, hash))
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadTd retrieves a block's total difficulty corresponding to the hash.
|
||||
func ReadTd(db ethdb.Reader, hash common.Hash, number uint64) *big.Int {
|
||||
data := ReadTdRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -254,7 +267,7 @@ func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int {
|
||||
}
|
||||
|
||||
// WriteTd stores the total difficulty of a block into the database.
|
||||
func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) {
|
||||
func WriteTd(db ethdb.Writer, hash common.Hash, number uint64, td *big.Int) {
|
||||
data, err := rlp.EncodeToBytes(td)
|
||||
if err != nil {
|
||||
log.Crit("Failed to RLP encode block total difficulty", "err", err)
|
||||
@@ -265,7 +278,7 @@ func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) {
|
||||
}
|
||||
|
||||
// DeleteTd removes all block total difficulty data associated with a hash.
|
||||
func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
func DeleteTd(db ethdb.Writer, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(headerTDKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete block total difficulty", "err", err)
|
||||
}
|
||||
@@ -273,17 +286,23 @@ func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
|
||||
// HasReceipts verifies the existence of all the transaction receipts belonging
|
||||
// to a block.
|
||||
func HasReceipts(db DatabaseReader, hash common.Hash, number uint64) bool {
|
||||
func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadReceipts retrieves all the transaction receipts belonging to a block.
|
||||
func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Receipts {
|
||||
// Retrieve the flattened receipt slice
|
||||
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
|
||||
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(blockReceiptsKey(number, hash))
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadReceipts retrieves all the transaction receipts belonging to a block.
|
||||
func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
|
||||
// Retrieve the flattened receipt slice
|
||||
data := ReadReceiptsRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -294,14 +313,27 @@ func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Rece
|
||||
return nil
|
||||
}
|
||||
receipts := make(types.Receipts, len(storageReceipts))
|
||||
logIndex := uint(0)
|
||||
for i, receipt := range storageReceipts {
|
||||
// Assemble deriving fields for log.
|
||||
for _, log := range receipt.Logs {
|
||||
log.TxHash = receipt.TxHash
|
||||
log.BlockHash = hash
|
||||
log.BlockNumber = number
|
||||
log.TxIndex = uint(i)
|
||||
log.Index = logIndex
|
||||
logIndex += 1
|
||||
}
|
||||
receipts[i] = (*types.Receipt)(receipt)
|
||||
receipts[i].BlockHash = hash
|
||||
receipts[i].BlockNumber = big.NewInt(0).SetUint64(number)
|
||||
receipts[i].TransactionIndex = uint(i)
|
||||
}
|
||||
return receipts
|
||||
}
|
||||
|
||||
// WriteReceipts stores all the transaction receipts belonging to a block.
|
||||
func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts types.Receipts) {
|
||||
func WriteReceipts(db ethdb.Writer, hash common.Hash, number uint64, receipts types.Receipts) {
|
||||
// Convert the receipts into their storage form and serialize them
|
||||
storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
|
||||
for i, receipt := range receipts {
|
||||
@@ -318,7 +350,7 @@ func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts
|
||||
}
|
||||
|
||||
// DeleteReceipts removes all receipt data associated with a block hash.
|
||||
func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
func DeleteReceipts(db ethdb.Writer, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete block receipts", "err", err)
|
||||
}
|
||||
@@ -330,7 +362,7 @@ func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
//
|
||||
// Note, due to concurrent download of header and block body the header and thus
|
||||
// canonical hash can be stored in the database but the body data not (yet).
|
||||
func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block {
|
||||
func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
|
||||
header := ReadHeader(db, hash, number)
|
||||
if header == nil {
|
||||
return nil
|
||||
@@ -343,21 +375,30 @@ func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block
|
||||
}
|
||||
|
||||
// WriteBlock serializes a block into the database, header and body separately.
|
||||
func WriteBlock(db DatabaseWriter, block *types.Block) {
|
||||
func WriteBlock(db ethdb.Writer, block *types.Block) {
|
||||
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
|
||||
WriteHeader(db, block.Header())
|
||||
}
|
||||
|
||||
// DeleteBlock removes all block data associated with a hash.
|
||||
func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) {
|
||||
func DeleteBlock(db ethdb.Writer, hash common.Hash, number uint64) {
|
||||
DeleteReceipts(db, hash, number)
|
||||
DeleteHeader(db, hash, number)
|
||||
DeleteBody(db, hash, number)
|
||||
DeleteTd(db, hash, number)
|
||||
}
|
||||
|
||||
// deleteBlockWithoutNumber removes all block data associated with a hash, except
|
||||
// the hash to number mapping.
|
||||
func deleteBlockWithoutNumber(db ethdb.Writer, hash common.Hash, number uint64) {
|
||||
DeleteReceipts(db, hash, number)
|
||||
deleteHeaderWithoutNumber(db, hash, number)
|
||||
DeleteBody(db, hash, number)
|
||||
DeleteTd(db, hash, number)
|
||||
}
|
||||
|
||||
// FindCommonAncestor returns the last common ancestor of two block headers
|
||||
func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header {
|
||||
func FindCommonAncestor(db ethdb.Reader, a, b *types.Header) *types.Header {
|
||||
for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
|
||||
a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
|
||||
if a == nil {
|
||||
|
||||
+10
-9
@@ -23,14 +23,13 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// Tests block header storage and retrieval operations.
|
||||
func TestHeaderStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
// Create a test header to move around the database and make sure it's really new
|
||||
header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
|
||||
@@ -63,7 +62,7 @@ func TestHeaderStorage(t *testing.T) {
|
||||
|
||||
// Tests block body storage and retrieval operations.
|
||||
func TestBodyStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
// Create a test body to move around the database and make sure it's really new
|
||||
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
|
||||
@@ -101,7 +100,7 @@ func TestBodyStorage(t *testing.T) {
|
||||
|
||||
// Tests block storage and retrieval operations.
|
||||
func TestBlockStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
// Create a test block to move around the database and make sure it's really new
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
@@ -151,7 +150,7 @@ func TestBlockStorage(t *testing.T) {
|
||||
|
||||
// Tests that partial block contents don't get reassembled into full blocks.
|
||||
func TestPartialBlockStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := NewMemoryDatabase()
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
Extra: []byte("test block"),
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
@@ -185,7 +184,7 @@ func TestPartialBlockStorage(t *testing.T) {
|
||||
|
||||
// Tests block total difficulty storage and retrieval operations.
|
||||
func TestTdStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
// Create a test TD to move around the database and make sure it's really new
|
||||
hash, td := common.Hash{}, big.NewInt(314)
|
||||
@@ -208,7 +207,7 @@ func TestTdStorage(t *testing.T) {
|
||||
|
||||
// Tests that canonical numbers can be mapped to hashes and retrieved.
|
||||
func TestCanonicalMappingStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
// Create a test canonical number and assinged hash to move around
|
||||
hash, number := common.Hash{0: 0xff}, uint64(314)
|
||||
@@ -231,7 +230,7 @@ func TestCanonicalMappingStorage(t *testing.T) {
|
||||
|
||||
// Tests that head headers and head blocks can be assigned, individually.
|
||||
func TestHeadStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
|
||||
blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
|
||||
@@ -266,7 +265,7 @@ func TestHeadStorage(t *testing.T) {
|
||||
|
||||
// Tests that receipts associated with a single block can be stored and retrieved.
|
||||
func TestBlockReceiptStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
receipt1 := &types.Receipt{
|
||||
Status: types.ReceiptStatusFailed,
|
||||
@@ -279,6 +278,7 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
||||
GasUsed: 111111,
|
||||
}
|
||||
receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
|
||||
receipt2 := &types.Receipt{
|
||||
PostState: common.Hash{2}.Bytes(),
|
||||
CumulativeGasUsed: 2,
|
||||
@@ -290,6 +290,7 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
||||
GasUsed: 222222,
|
||||
}
|
||||
receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
|
||||
receipts := []*types.Receipt{receipt1, receipt2}
|
||||
|
||||
// Check that no receipt entries are in a pristine database
|
||||
|
||||
+45
-33
@@ -19,88 +19,100 @@ package rawdb
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
|
||||
// hash to allow retrieving the transaction or receipt by hash.
|
||||
func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) (common.Hash, uint64, uint64) {
|
||||
func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) common.Hash {
|
||||
data, _ := db.Get(txLookupKey(hash))
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}, 0, 0
|
||||
return common.Hash{}
|
||||
}
|
||||
var entry TxLookupEntry
|
||||
if len(data) == common.HashLength {
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
// Probably it's legacy txlookup entry data, try to decode it.
|
||||
var entry LegacyTxLookupEntry
|
||||
if err := rlp.DecodeBytes(data, &entry); err != nil {
|
||||
log.Error("Invalid transaction lookup entry RLP", "hash", hash, "err", err)
|
||||
return common.Hash{}, 0, 0
|
||||
log.Error("Invalid transaction lookup entry RLP", "hash", hash, "blob", data, "err", err)
|
||||
return common.Hash{}
|
||||
}
|
||||
return entry.BlockHash, entry.BlockIndex, entry.Index
|
||||
return entry.BlockHash
|
||||
}
|
||||
|
||||
// WriteTxLookupEntries stores a positional metadata for every transaction from
|
||||
// a block, enabling hash based transaction and receipt lookups.
|
||||
func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) {
|
||||
for i, tx := range block.Transactions() {
|
||||
entry := TxLookupEntry{
|
||||
BlockHash: block.Hash(),
|
||||
BlockIndex: block.NumberU64(),
|
||||
Index: uint64(i),
|
||||
}
|
||||
data, err := rlp.EncodeToBytes(entry)
|
||||
if err != nil {
|
||||
log.Crit("Failed to encode transaction lookup entry", "err", err)
|
||||
}
|
||||
if err := db.Put(txLookupKey(tx.Hash()), data); err != nil {
|
||||
func WriteTxLookupEntries(db ethdb.Writer, block *types.Block) {
|
||||
for _, tx := range block.Transactions() {
|
||||
if err := db.Put(txLookupKey(tx.Hash()), block.Hash().Bytes()); err != nil {
|
||||
log.Crit("Failed to store transaction lookup entry", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteTxLookupEntry removes all transaction data associated with a hash.
|
||||
func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) {
|
||||
func DeleteTxLookupEntry(db ethdb.Writer, hash common.Hash) {
|
||||
db.Delete(txLookupKey(hash))
|
||||
}
|
||||
|
||||
// ReadTransaction retrieves a specific transaction from the database, along with
|
||||
// its added positional metadata.
|
||||
func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
||||
blockHash, blockNumber, txIndex := ReadTxLookupEntry(db, hash)
|
||||
func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
||||
blockHash := ReadTxLookupEntry(db, hash)
|
||||
if blockHash == (common.Hash{}) {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
body := ReadBody(db, blockHash, blockNumber)
|
||||
if body == nil || len(body.Transactions) <= int(txIndex) {
|
||||
log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash, "index", txIndex)
|
||||
blockNumber := ReadHeaderNumber(db, blockHash)
|
||||
if blockNumber == nil {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
return body.Transactions[txIndex], blockHash, blockNumber, txIndex
|
||||
body := ReadBody(db, blockHash, *blockNumber)
|
||||
if body == nil {
|
||||
log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
for txIndex, tx := range body.Transactions {
|
||||
if tx.Hash() == hash {
|
||||
return tx, blockHash, *blockNumber, uint64(txIndex)
|
||||
}
|
||||
}
|
||||
log.Error("Transaction not found", "number", blockNumber, "hash", blockHash, "txhash", hash)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
|
||||
// ReadReceipt retrieves a specific transaction receipt from the database, along with
|
||||
// its added positional metadata.
|
||||
func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
|
||||
blockHash, blockNumber, receiptIndex := ReadTxLookupEntry(db, hash)
|
||||
func ReadReceipt(db ethdb.Reader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
|
||||
blockHash := ReadTxLookupEntry(db, hash)
|
||||
if blockHash == (common.Hash{}) {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
receipts := ReadReceipts(db, blockHash, blockNumber)
|
||||
if len(receipts) <= int(receiptIndex) {
|
||||
log.Error("Receipt refereced missing", "number", blockNumber, "hash", blockHash, "index", receiptIndex)
|
||||
blockNumber := ReadHeaderNumber(db, blockHash)
|
||||
if blockNumber == nil {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
return receipts[receiptIndex], blockHash, blockNumber, receiptIndex
|
||||
receipts := ReadReceipts(db, blockHash, *blockNumber)
|
||||
for receiptIndex, receipt := range receipts {
|
||||
if receipt.TxHash == hash {
|
||||
return receipt, blockHash, *blockNumber, uint64(receiptIndex)
|
||||
}
|
||||
}
|
||||
log.Error("Receipt not found", "number", blockNumber, "hash", blockHash, "txhash", hash)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
|
||||
// ReadBloomBits retrieves the compressed bloom bit vector belonging to the given
|
||||
// section and bit index from the.
|
||||
func ReadBloomBits(db DatabaseReader, bit uint, section uint64, head common.Hash) ([]byte, error) {
|
||||
func ReadBloomBits(db ethdb.Reader, bit uint, section uint64, head common.Hash) ([]byte, error) {
|
||||
return db.Get(bloomBitsKey(bit, section, head))
|
||||
}
|
||||
|
||||
// WriteBloomBits stores the compressed bloom bits vector belonging to the given
|
||||
// section and bit index.
|
||||
func WriteBloomBits(db DatabaseWriter, bit uint, section uint64, head common.Hash, bits []byte) {
|
||||
func WriteBloomBits(db ethdb.Writer, bit uint, section uint64, head common.Hash, bits []byte) {
|
||||
if err := db.Put(bloomBitsKey(bit, section, head), bits); err != nil {
|
||||
log.Crit("Failed to store bloom bits", "err", err)
|
||||
}
|
||||
|
||||
+24
-2
@@ -22,12 +22,12 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// Tests that positional lookup metadata can be stored and retrieved.
|
||||
func TestLookupStorage(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
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})
|
||||
@@ -65,4 +65,26 @@ func TestLookupStorage(t *testing.T) {
|
||||
t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
|
||||
}
|
||||
}
|
||||
// Insert legacy txlookup and verify the data retrieval
|
||||
for index, tx := range block.Transactions() {
|
||||
entry := LegacyTxLookupEntry{
|
||||
BlockHash: block.Hash(),
|
||||
BlockIndex: block.NumberU64(),
|
||||
Index: uint64(index),
|
||||
}
|
||||
data, _ := rlp.EncodeToBytes(entry)
|
||||
db.Put(txLookupKey(tx.Hash()), data)
|
||||
}
|
||||
for i, tx := range txs {
|
||||
if txn, hash, number, index := ReadTransaction(db, tx.Hash()); txn == nil {
|
||||
t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
|
||||
} else {
|
||||
if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
|
||||
t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
|
||||
}
|
||||
if tx.Hash() != txn.Hash() {
|
||||
t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -20,13 +20,14 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// ReadDatabaseVersion retrieves the version number of the database.
|
||||
func ReadDatabaseVersion(db DatabaseReader) *uint64 {
|
||||
func ReadDatabaseVersion(db ethdb.Reader) *uint64 {
|
||||
var version uint64
|
||||
|
||||
enc, _ := db.Get(databaseVerisionKey)
|
||||
@@ -41,7 +42,7 @@ func ReadDatabaseVersion(db DatabaseReader) *uint64 {
|
||||
}
|
||||
|
||||
// WriteDatabaseVersion stores the version number of the database
|
||||
func WriteDatabaseVersion(db DatabaseWriter, version uint64) {
|
||||
func WriteDatabaseVersion(db ethdb.Writer, version uint64) {
|
||||
enc, err := rlp.EncodeToBytes(version)
|
||||
if err != nil {
|
||||
log.Crit("Failed to encode database version", "err", err)
|
||||
@@ -52,7 +53,7 @@ func WriteDatabaseVersion(db DatabaseWriter, version uint64) {
|
||||
}
|
||||
|
||||
// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
|
||||
func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig {
|
||||
func ReadChainConfig(db ethdb.Reader, hash common.Hash) *params.ChainConfig {
|
||||
data, _ := db.Get(configKey(hash))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
@@ -66,7 +67,7 @@ func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig {
|
||||
}
|
||||
|
||||
// WriteChainConfig writes the chain config settings to the database.
|
||||
func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConfig) {
|
||||
func WriteChainConfig(db ethdb.Writer, hash common.Hash, cfg *params.ChainConfig) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
@@ -80,13 +81,13 @@ func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConf
|
||||
}
|
||||
|
||||
// ReadPreimage retrieves a single preimage of the provided hash.
|
||||
func ReadPreimage(db DatabaseReader, hash common.Hash) []byte {
|
||||
func ReadPreimage(db ethdb.Reader, hash common.Hash) []byte {
|
||||
data, _ := db.Get(preimageKey(hash))
|
||||
return data
|
||||
}
|
||||
|
||||
// WritePreimages writes the provided set of preimages to the database.
|
||||
func WritePreimages(db DatabaseWriter, preimages map[common.Hash][]byte) {
|
||||
func WritePreimages(db ethdb.Writer, preimages map[common.Hash][]byte) {
|
||||
for hash, preimage := range preimages {
|
||||
if err := db.Put(preimageKey(hash), preimage); err != nil {
|
||||
log.Crit("Failed to store trie preimage", "err", err)
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/leveldb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||
)
|
||||
|
||||
// NewDatabase creates a high level database on top of a given key-value data
|
||||
// store without a freezer moving immutable chain segments into cold storage.
|
||||
func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
|
||||
return db
|
||||
}
|
||||
|
||||
// NewMemoryDatabase creates an ephemeral in-memory key-value database without a
|
||||
// freezer moving immutable chain segments into cold storage.
|
||||
func NewMemoryDatabase() ethdb.Database {
|
||||
return NewDatabase(memorydb.New())
|
||||
}
|
||||
|
||||
// NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database with
|
||||
// an initial starting capacity, but without a freezer moving immutable chain
|
||||
// segments into cold storage.
|
||||
func NewMemoryDatabaseWithCap(size int) ethdb.Database {
|
||||
return NewDatabase(memorydb.NewWithCap(size))
|
||||
}
|
||||
|
||||
// NewLevelDBDatabase creates a persistent key-value database without a freezer
|
||||
// moving immutable chain segments into cold storage.
|
||||
func NewLevelDBDatabase(file string, cache int, handles int, namespace string) (ethdb.Database, error) {
|
||||
db, err := leveldb.New(file, cache, handles, namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewDatabase(db), nil
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rawdb
|
||||
|
||||
// DatabaseReader wraps the Has and Get method of a backing data store.
|
||||
type DatabaseReader interface {
|
||||
Has(key []byte) (bool, error)
|
||||
Get(key []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// DatabaseWriter wraps the Put method of a backing data store.
|
||||
type DatabaseWriter interface {
|
||||
Put(key []byte, value []byte) error
|
||||
}
|
||||
|
||||
// DatabaseDeleter wraps the Delete method of a backing data store.
|
||||
type DatabaseDeleter interface {
|
||||
Delete(key []byte) error
|
||||
}
|
||||
+8
-3
@@ -63,9 +63,9 @@ var (
|
||||
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
|
||||
)
|
||||
|
||||
// TxLookupEntry is a positional metadata to help looking up the data content of
|
||||
// a transaction or receipt given only its hash.
|
||||
type TxLookupEntry struct {
|
||||
// LegacyTxLookupEntry is the legacy TxLookupEntry definition with some unnecessary
|
||||
// fields.
|
||||
type LegacyTxLookupEntry struct {
|
||||
BlockHash common.Hash
|
||||
BlockIndex uint64
|
||||
Index uint64
|
||||
@@ -78,6 +78,11 @@ func encodeBlockNumber(number uint64) []byte {
|
||||
return enc
|
||||
}
|
||||
|
||||
// headerKeyPrefix = headerPrefix + num (uint64 big endian)
|
||||
func headerKeyPrefix(number uint64) []byte {
|
||||
return append(headerPrefix, encodeBlockNumber(number)...)
|
||||
}
|
||||
|
||||
// headerKey = headerPrefix + num (uint64 big endian) + hash
|
||||
func headerKey(number uint64, hash common.Hash) []byte {
|
||||
return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
// table is a wrapper around a database that prefixes each key access with a pre-
|
||||
// configured string.
|
||||
type table struct {
|
||||
db ethdb.Database
|
||||
prefix string
|
||||
}
|
||||
|
||||
// NewTable returns a database object that prefixes all keys with a given string.
|
||||
func NewTable(db ethdb.Database, prefix string) ethdb.Database {
|
||||
return &table{
|
||||
db: db,
|
||||
prefix: prefix,
|
||||
}
|
||||
}
|
||||
|
||||
// Close is a noop to implement the Database interface.
|
||||
func (t *table) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Has retrieves if a prefixed version of a key is present in the database.
|
||||
func (t *table) Has(key []byte) (bool, error) {
|
||||
return t.db.Has(append([]byte(t.prefix), key...))
|
||||
}
|
||||
|
||||
// Get retrieves the given prefixed key if it's present in the database.
|
||||
func (t *table) Get(key []byte) ([]byte, error) {
|
||||
return t.db.Get(append([]byte(t.prefix), key...))
|
||||
}
|
||||
|
||||
// Put inserts the given value into the database at a prefixed version of the
|
||||
// provided key.
|
||||
func (t *table) Put(key []byte, value []byte) error {
|
||||
return t.db.Put(append([]byte(t.prefix), key...), value)
|
||||
}
|
||||
|
||||
// Delete removes the given prefixed key from the database.
|
||||
func (t *table) Delete(key []byte) error {
|
||||
return t.db.Delete(append([]byte(t.prefix), key...))
|
||||
}
|
||||
|
||||
// NewIterator creates a binary-alphabetical iterator over the entire keyspace
|
||||
// contained within the database.
|
||||
func (t *table) NewIterator() ethdb.Iterator {
|
||||
return t.NewIteratorWithPrefix(nil)
|
||||
}
|
||||
|
||||
// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset
|
||||
// of database content with a particular key prefix.
|
||||
func (t *table) NewIteratorWithPrefix(prefix []byte) ethdb.Iterator {
|
||||
return t.db.NewIteratorWithPrefix(append([]byte(t.prefix), prefix...))
|
||||
}
|
||||
|
||||
// Stat returns a particular internal stat of the database.
|
||||
func (t *table) Stat(property string) (string, error) {
|
||||
return t.db.Stat(property)
|
||||
}
|
||||
|
||||
// Compact flattens the underlying data store for the given key range. In essence,
|
||||
// deleted and overwritten versions are discarded, and the data is rearranged to
|
||||
// reduce the cost of operations needed to access them.
|
||||
//
|
||||
// A nil start is treated as a key before all keys in the data store; a nil limit
|
||||
// is treated as a key after all keys in the data store. If both is nil then it
|
||||
// will compact entire data store.
|
||||
func (t *table) Compact(start []byte, limit []byte) error {
|
||||
// If no start was specified, use the table prefix as the first value
|
||||
if start == nil {
|
||||
start = []byte(t.prefix)
|
||||
}
|
||||
// If no limit was specified, use the first element not matching the prefix
|
||||
// as the limit
|
||||
if limit == nil {
|
||||
limit = []byte(t.prefix)
|
||||
for i := len(limit) - 1; i >= 0; i-- {
|
||||
// Bump the current character, stopping if it doesn't overflow
|
||||
limit[i]++
|
||||
if limit[i] > 0 {
|
||||
break
|
||||
}
|
||||
// Character overflown, proceed to the next or nil if the last
|
||||
if i == 0 {
|
||||
limit = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// Range correctly calculated based on table prefix, delegate down
|
||||
return t.db.Compact(start, limit)
|
||||
}
|
||||
|
||||
// NewBatch creates a write-only database that buffers changes to its host db
|
||||
// until a final write is called, each operation prefixing all keys with the
|
||||
// pre-configured string.
|
||||
func (t *table) NewBatch() ethdb.Batch {
|
||||
return &tableBatch{t.db.NewBatch(), t.prefix}
|
||||
}
|
||||
|
||||
// tableBatch is a wrapper around a database batch that prefixes each key access
|
||||
// with a pre-configured string.
|
||||
type tableBatch struct {
|
||||
batch ethdb.Batch
|
||||
prefix string
|
||||
}
|
||||
|
||||
// Put inserts the given value into the batch for later committing.
|
||||
func (b *tableBatch) Put(key, value []byte) error {
|
||||
return b.batch.Put(append([]byte(b.prefix), key...), value)
|
||||
}
|
||||
|
||||
// Delete inserts the a key removal into the batch for later committing.
|
||||
func (b *tableBatch) Delete(key []byte) error {
|
||||
return b.batch.Delete(append([]byte(b.prefix), key...))
|
||||
}
|
||||
|
||||
// ValueSize retrieves the amount of data queued up for writing.
|
||||
func (b *tableBatch) ValueSize() int {
|
||||
return b.batch.ValueSize()
|
||||
}
|
||||
|
||||
// Write flushes any accumulated data to disk.
|
||||
func (b *tableBatch) Write() error {
|
||||
return b.batch.Write()
|
||||
}
|
||||
|
||||
// Reset resets the batch for reuse.
|
||||
func (b *tableBatch) Reset() {
|
||||
b.batch.Reset()
|
||||
}
|
||||
|
||||
// Replay replays the batch contents.
|
||||
func (b *tableBatch) Replay(w ethdb.Writer) error {
|
||||
return b.batch.Replay(w)
|
||||
}
|
||||
+45
-66
@@ -18,7 +18,6 @@ package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -26,14 +25,7 @@ import (
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// Trie cache generation limit after which to evict trie nodes from memory.
|
||||
var MaxTrieCacheGen = uint16(120)
|
||||
|
||||
const (
|
||||
// Number of past tries to keep. This value is chosen such that
|
||||
// reasonable chain reorg depths will hit an existing trie.
|
||||
maxPastTries = 12
|
||||
|
||||
// Number of codehash->size associations to keep.
|
||||
codeSizeCacheSize = 100000
|
||||
)
|
||||
@@ -59,28 +51,61 @@ type Database interface {
|
||||
TrieDB() *trie.Database
|
||||
}
|
||||
|
||||
// Trie is a Ethereum Merkle Trie.
|
||||
// Trie is a Ethereum Merkle Patricia trie.
|
||||
type Trie interface {
|
||||
// GetKey returns the sha3 preimage of a hashed key that was previously used
|
||||
// to store a value.
|
||||
//
|
||||
// TODO(fjl): remove this when SecureTrie is removed
|
||||
GetKey([]byte) []byte
|
||||
|
||||
// TryGet returns the value for key stored in the trie. The value bytes must
|
||||
// not be modified by the caller. If a node was not found in the database, a
|
||||
// trie.MissingNodeError is returned.
|
||||
TryGet(key []byte) ([]byte, error)
|
||||
|
||||
// TryUpdate associates key with value in the trie. If value has length zero, any
|
||||
// existing value is deleted from the trie. The value bytes must not be modified
|
||||
// by the caller while they are stored in the trie. If a node was not found in the
|
||||
// database, a trie.MissingNodeError is returned.
|
||||
TryUpdate(key, value []byte) error
|
||||
|
||||
// TryDelete removes any existing value for key from the trie. If a node was not
|
||||
// found in the database, a trie.MissingNodeError is returned.
|
||||
TryDelete(key []byte) error
|
||||
Commit(onleaf trie.LeafCallback) (common.Hash, error)
|
||||
|
||||
// Hash returns the root hash of the trie. It does not write to the database and
|
||||
// can be used even if the trie doesn't have one.
|
||||
Hash() common.Hash
|
||||
|
||||
// Commit writes all nodes to the trie's memory database, tracking the internal
|
||||
// and external (for account tries) references.
|
||||
Commit(onleaf trie.LeafCallback) (common.Hash, error)
|
||||
|
||||
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
|
||||
// starts at the key after the given start key.
|
||||
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
|
||||
|
||||
// Prove constructs a Merkle proof for key. The result contains all encoded nodes
|
||||
// on the path to the value at key. The value itself is also included in the last
|
||||
// node and can be retrieved by verifying the proof.
|
||||
//
|
||||
// If the trie does not contain a value for key, the returned proof contains all
|
||||
// nodes of the longest existing prefix of the key (at least the root), ending
|
||||
// with the node that proves the absence of the key.
|
||||
Prove(key []byte, fromLevel uint, proofDb ethdb.Writer) error
|
||||
}
|
||||
|
||||
// NewDatabase creates a backing store for state. The returned database is safe for
|
||||
// concurrent use and retains a few recent expanded trie nodes in memory. To keep
|
||||
// more historical state in memory, use the NewDatabaseWithCache constructor.
|
||||
// concurrent use, but does not retain any recent trie nodes in memory. To keep some
|
||||
// historical state in memory, use the NewDatabaseWithCache constructor.
|
||||
func NewDatabase(db ethdb.Database) Database {
|
||||
return NewDatabaseWithCache(db, 0)
|
||||
}
|
||||
|
||||
// NewDatabase creates a backing store for state. The returned database is safe for
|
||||
// concurrent use and retains both a few recent expanded trie nodes in memory, as
|
||||
// well as a lot of collapsed RLP trie nodes in a large memory cache.
|
||||
// NewDatabaseWithCache creates a backing store for state. The returned database
|
||||
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
|
||||
// large memory cache.
|
||||
func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
|
||||
csc, _ := lru.New(codeSizeCacheSize)
|
||||
return &cachingDB{
|
||||
@@ -91,50 +116,22 @@ func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
|
||||
|
||||
type cachingDB struct {
|
||||
db *trie.Database
|
||||
mu sync.Mutex
|
||||
pastTries []*trie.SecureTrie
|
||||
codeSizeCache *lru.Cache
|
||||
}
|
||||
|
||||
// OpenTrie opens the main account trie.
|
||||
// OpenTrie opens the main account trie at a specific root hash.
|
||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
db.mu.Lock()
|
||||
defer db.mu.Unlock()
|
||||
|
||||
for i := len(db.pastTries) - 1; i >= 0; i-- {
|
||||
if db.pastTries[i].Hash() == root {
|
||||
return cachedTrie{db.pastTries[i].Copy(), db}, nil
|
||||
}
|
||||
}
|
||||
tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cachedTrie{tr, db}, nil
|
||||
}
|
||||
|
||||
func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
|
||||
db.mu.Lock()
|
||||
defer db.mu.Unlock()
|
||||
|
||||
if len(db.pastTries) >= maxPastTries {
|
||||
copy(db.pastTries, db.pastTries[1:])
|
||||
db.pastTries[len(db.pastTries)-1] = t
|
||||
} else {
|
||||
db.pastTries = append(db.pastTries, t)
|
||||
}
|
||||
return trie.NewSecure(root, db.db)
|
||||
}
|
||||
|
||||
// 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)
|
||||
return trie.NewSecure(root, db.db)
|
||||
}
|
||||
|
||||
// CopyTrie returns an independent copy of the given trie.
|
||||
func (db *cachingDB) CopyTrie(t Trie) Trie {
|
||||
switch t := t.(type) {
|
||||
case cachedTrie:
|
||||
return cachedTrie{t.SecureTrie.Copy(), db}
|
||||
case *trie.SecureTrie:
|
||||
return t.Copy()
|
||||
default:
|
||||
@@ -164,21 +161,3 @@ func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, erro
|
||||
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) 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)
|
||||
}
|
||||
|
||||
+4
-1
@@ -51,7 +51,9 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||
t.Errorf("state entry not reported %x", hash)
|
||||
}
|
||||
}
|
||||
for _, key := range db.TrieDB().DiskDB().(*ethdb.MemDatabase).Keys() {
|
||||
it := db.TrieDB().DiskDB().(ethdb.Database).NewIterator()
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
||||
continue
|
||||
}
|
||||
@@ -59,4 +61,5 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||
t.Errorf("state entry not reported %x", key)
|
||||
}
|
||||
}
|
||||
it.Release()
|
||||
}
|
||||
|
||||
+2
-2
@@ -20,13 +20,13 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
)
|
||||
|
||||
var addr = common.BytesToAddress([]byte("test"))
|
||||
|
||||
func create() (*ManagedState, *account) {
|
||||
statedb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
ms := ManageState(statedb)
|
||||
ms.StateDB.SetNonce(addr, 100)
|
||||
ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr))
|
||||
|
||||
+20
@@ -21,9 +21,11 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@@ -177,6 +179,10 @@ func (self *stateObject) GetCommittedState(db Database, key common.Hash) common.
|
||||
if cached {
|
||||
return value
|
||||
}
|
||||
// Track the amount of time wasted on reading the storge trie
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { self.db.StorageReads += time.Since(start) }(time.Now())
|
||||
}
|
||||
// Otherwise load the value from the database
|
||||
enc, err := self.getTrie(db).TryGet(key[:])
|
||||
if err != nil {
|
||||
@@ -216,6 +222,11 @@ func (self *stateObject) setState(key, value common.Hash) {
|
||||
|
||||
// updateTrie writes cached storage modifications into the object's storage trie.
|
||||
func (self *stateObject) updateTrie(db Database) Trie {
|
||||
// Track the amount of time wasted on updating the storge trie
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { self.db.StorageUpdates += time.Since(start) }(time.Now())
|
||||
}
|
||||
// Update all the dirty slots in the trie
|
||||
tr := self.getTrie(db)
|
||||
for key, value := range self.dirtyStorage {
|
||||
delete(self.dirtyStorage, key)
|
||||
@@ -240,6 +251,11 @@ func (self *stateObject) updateTrie(db Database) Trie {
|
||||
// UpdateRoot sets the trie root to the current root hash of
|
||||
func (self *stateObject) updateRoot(db Database) {
|
||||
self.updateTrie(db)
|
||||
|
||||
// Track the amount of time wasted on hashing the storge trie
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { self.db.StorageHashes += time.Since(start) }(time.Now())
|
||||
}
|
||||
self.data.Root = self.trie.Hash()
|
||||
}
|
||||
|
||||
@@ -250,6 +266,10 @@ func (self *stateObject) CommitTrie(db Database) error {
|
||||
if self.dbErr != nil {
|
||||
return self.dbErr
|
||||
}
|
||||
// Track the amount of time wasted on committing the storge trie
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { self.db.StorageCommits += time.Since(start) }(time.Now())
|
||||
}
|
||||
root, err := self.trie.Commit(nil)
|
||||
if err == nil {
|
||||
self.data.Root = root
|
||||
|
||||
+4
-3
@@ -22,13 +22,14 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
checker "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type StateSuite struct {
|
||||
db *ethdb.MemDatabase
|
||||
db ethdb.Database
|
||||
state *StateDB
|
||||
}
|
||||
|
||||
@@ -87,7 +88,7 @@ func (s *StateSuite) TestDump(c *checker.C) {
|
||||
}
|
||||
|
||||
func (s *StateSuite) SetUpTest(c *checker.C) {
|
||||
s.db = ethdb.NewMemDatabase()
|
||||
s.db = rawdb.NewMemoryDatabase()
|
||||
s.state, _ = New(common.Hash{}, NewDatabase(s.db))
|
||||
}
|
||||
|
||||
@@ -141,7 +142,7 @@ func (s *StateSuite) TestSnapshotEmpty(c *checker.C) {
|
||||
// use testing instead of checker because checker does not support
|
||||
// printing/logging in tests (-check.vv does not work)
|
||||
func TestSnapshot2(t *testing.T) {
|
||||
state, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
|
||||
stateobjaddr0 := toAddr([]byte("so0"))
|
||||
stateobjaddr1 := toAddr([]byte("so1"))
|
||||
|
||||
+69
-21
@@ -22,11 +22,13 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
@@ -37,8 +39,8 @@ type revision struct {
|
||||
}
|
||||
|
||||
var (
|
||||
// emptyState is the known hash of an empty state trie entry.
|
||||
emptyState = crypto.Keccak256Hash(nil)
|
||||
// emptyRoot is the known root hash of an empty trie.
|
||||
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||
|
||||
// emptyCode is the known hash of the empty EVM bytecode.
|
||||
emptyCode = crypto.Keccak256Hash(nil)
|
||||
@@ -51,6 +53,10 @@ func (n *proofList) Put(key []byte, value []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *proofList) Delete(key []byte) error {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// 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:
|
||||
@@ -86,6 +92,16 @@ type StateDB struct {
|
||||
journal *journal
|
||||
validRevisions []revision
|
||||
nextRevisionId int
|
||||
|
||||
// Measurements gathered during execution for debugging purposes
|
||||
AccountReads time.Duration
|
||||
AccountHashes time.Duration
|
||||
AccountUpdates time.Duration
|
||||
AccountCommits time.Duration
|
||||
StorageReads time.Duration
|
||||
StorageHashes time.Duration
|
||||
StorageUpdates time.Duration
|
||||
StorageCommits time.Duration
|
||||
}
|
||||
|
||||
// Create a new state from a given trie.
|
||||
@@ -221,6 +237,16 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// TxIndex returns the current transaction index set by Prepare.
|
||||
func (self *StateDB) TxIndex() int {
|
||||
return self.txIndex
|
||||
}
|
||||
|
||||
// BlockHash returns the current block hash set by Prepare.
|
||||
func (self *StateDB) BlockHash() common.Hash {
|
||||
return self.bhash
|
||||
}
|
||||
|
||||
func (self *StateDB) GetCode(addr common.Address) []byte {
|
||||
stateObject := self.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
@@ -386,36 +412,51 @@ func (self *StateDB) Suicide(addr common.Address) bool {
|
||||
//
|
||||
|
||||
// updateStateObject writes the given object to the trie.
|
||||
func (self *StateDB) updateStateObject(stateObject *stateObject) {
|
||||
func (s *StateDB) updateStateObject(stateObject *stateObject) {
|
||||
// Track the amount of time wasted on updating the account from the trie
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
|
||||
}
|
||||
// Encode the account and update the account trie
|
||||
addr := stateObject.Address()
|
||||
|
||||
data, err := rlp.EncodeToBytes(stateObject)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
|
||||
}
|
||||
self.setError(self.trie.TryUpdate(addr[:], data))
|
||||
s.setError(s.trie.TryUpdate(addr[:], data))
|
||||
}
|
||||
|
||||
// deleteStateObject removes the given object from the state trie.
|
||||
func (self *StateDB) deleteStateObject(stateObject *stateObject) {
|
||||
func (s *StateDB) deleteStateObject(stateObject *stateObject) {
|
||||
// Track the amount of time wasted on deleting the account from the trie
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
|
||||
}
|
||||
// Delete the account from the trie
|
||||
stateObject.deleted = true
|
||||
|
||||
addr := stateObject.Address()
|
||||
self.setError(self.trie.TryDelete(addr[:]))
|
||||
s.setError(s.trie.TryDelete(addr[:]))
|
||||
}
|
||||
|
||||
// Retrieve a state object given by the address. Returns nil if not found.
|
||||
func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
|
||||
// Prefer 'live' objects.
|
||||
if obj := self.stateObjects[addr]; obj != nil {
|
||||
func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
|
||||
// Prefer live objects
|
||||
if obj := s.stateObjects[addr]; obj != nil {
|
||||
if obj.deleted {
|
||||
return nil
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// Load the object from the database.
|
||||
enc, err := self.trie.TryGet(addr[:])
|
||||
// Track the amount of time wasted on loading the object from the database
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now())
|
||||
}
|
||||
// Load the object from the database
|
||||
enc, err := s.trie.TryGet(addr[:])
|
||||
if len(enc) == 0 {
|
||||
self.setError(err)
|
||||
s.setError(err)
|
||||
return nil
|
||||
}
|
||||
var data Account
|
||||
@@ -423,9 +464,9 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObje
|
||||
log.Error("Failed to decode state object", "addr", addr, "err", err)
|
||||
return nil
|
||||
}
|
||||
// Insert into the live set.
|
||||
obj := newObject(self, addr, data)
|
||||
self.setStateObject(obj)
|
||||
// Insert into the live set
|
||||
obj := newObject(s, addr, data)
|
||||
s.setStateObject(obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
@@ -502,7 +543,7 @@ func (self *StateDB) Copy() *StateDB {
|
||||
refund: self.refund,
|
||||
logs: make(map[common.Hash][]*types.Log, len(self.logs)),
|
||||
logSize: self.logSize,
|
||||
preimages: make(map[common.Hash][]byte),
|
||||
preimages: make(map[common.Hash][]byte, len(self.preimages)),
|
||||
journal: newJournal(),
|
||||
}
|
||||
// Copy the dirty states, logs, and preimages
|
||||
@@ -600,6 +641,11 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||
// goes into transaction receipts.
|
||||
func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||
s.Finalise(deleteEmptyObjects)
|
||||
|
||||
// Track the amount of time wasted on hashing the account trie
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now())
|
||||
}
|
||||
return s.trie.Hash()
|
||||
}
|
||||
|
||||
@@ -624,7 +670,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
|
||||
for addr := range s.journal.dirties {
|
||||
s.stateObjectsDirty[addr] = struct{}{}
|
||||
}
|
||||
// Commit objects to the trie.
|
||||
// Commit objects to the trie, measuring the elapsed time
|
||||
for addr, stateObject := range s.stateObjects {
|
||||
_, isDirty := s.stateObjectsDirty[addr]
|
||||
switch {
|
||||
@@ -647,13 +693,16 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
|
||||
}
|
||||
delete(s.stateObjectsDirty, addr)
|
||||
}
|
||||
// Write trie changes.
|
||||
// Write the account trie changes, measuing the amount of wasted time
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now())
|
||||
}
|
||||
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 {
|
||||
if account.Root != emptyRoot {
|
||||
s.db.TrieDB().Reference(account.Root, parent)
|
||||
}
|
||||
code := common.BytesToHash(account.CodeHash)
|
||||
@@ -662,6 +711,5 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
|
||||
return root, err
|
||||
}
|
||||
|
||||
+30
-16
@@ -31,15 +31,15 @@ import (
|
||||
check "gopkg.in/check.v1"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
// Tests that updating a state trie does not leak any database writes prior to
|
||||
// actually committing the state.
|
||||
func TestUpdateLeaks(t *testing.T) {
|
||||
// Create an empty state database
|
||||
db := ethdb.NewMemDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
state, _ := New(common.Hash{}, NewDatabase(db))
|
||||
|
||||
// Update it with some accounts
|
||||
@@ -56,18 +56,19 @@ func TestUpdateLeaks(t *testing.T) {
|
||||
state.IntermediateRoot(false)
|
||||
}
|
||||
// Ensure that no data was leaked into the database
|
||||
for _, key := range db.Keys() {
|
||||
value, _ := db.Get(key)
|
||||
t.Errorf("State leaked into database: %x -> %x", key, value)
|
||||
it := db.NewIterator()
|
||||
for it.Next() {
|
||||
t.Errorf("State leaked into database: %x -> %x", it.Key(), it.Value())
|
||||
}
|
||||
it.Release()
|
||||
}
|
||||
|
||||
// Tests that no intermediate state of an object is stored into the database,
|
||||
// only the one right before the commit.
|
||||
func TestIntermediateLeaks(t *testing.T) {
|
||||
// Create two state databases, one transitioning to the final state, the other final from the beginning
|
||||
transDb := ethdb.NewMemDatabase()
|
||||
finalDb := ethdb.NewMemDatabase()
|
||||
transDb := rawdb.NewMemoryDatabase()
|
||||
finalDb := rawdb.NewMemoryDatabase()
|
||||
transState, _ := New(common.Hash{}, NewDatabase(transDb))
|
||||
finalState, _ := New(common.Hash{}, NewDatabase(finalDb))
|
||||
|
||||
@@ -103,16 +104,20 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
if _, err := finalState.Commit(false); err != nil {
|
||||
t.Fatalf("failed to commit final state: %v", err)
|
||||
}
|
||||
for _, key := range finalDb.Keys() {
|
||||
it := finalDb.NewIterator()
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
if _, err := transDb.Get(key); err != nil {
|
||||
val, _ := finalDb.Get(key)
|
||||
t.Errorf("entry missing from the transition database: %x -> %x", key, val)
|
||||
t.Errorf("entry missing from the transition database: %x -> %x", key, it.Value())
|
||||
}
|
||||
}
|
||||
for _, key := range transDb.Keys() {
|
||||
it.Release()
|
||||
|
||||
it = transDb.NewIterator()
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
if _, err := finalDb.Get(key); err != nil {
|
||||
val, _ := transDb.Get(key)
|
||||
t.Errorf("extra entry in the transition database: %x -> %x", key, val)
|
||||
t.Errorf("extra entry in the transition database: %x -> %x", key, it.Value())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +127,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
// https://github.com/ethereum/go-ethereum/pull/15549.
|
||||
func TestCopy(t *testing.T) {
|
||||
// Create a random state test to copy and modify "independently"
|
||||
orig, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
orig, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
|
||||
for i := byte(0); i < 255; i++ {
|
||||
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
@@ -276,6 +281,15 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
{
|
||||
name: "AddPreimage",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
preimage := []byte{1}
|
||||
hash := common.BytesToHash(preimage)
|
||||
s.AddPreimage(hash, preimage)
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
}
|
||||
action := actions[r.Intn(len(actions))]
|
||||
var nameargs []string
|
||||
@@ -333,7 +347,7 @@ func (test *snapshotTest) String() string {
|
||||
func (test *snapshotTest) run() bool {
|
||||
// Run all actions and create snapshots.
|
||||
var (
|
||||
state, _ = New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
state, _ = New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
snapshotRevs = make([]int, len(test.snapshots))
|
||||
sindex = 0
|
||||
)
|
||||
@@ -424,7 +438,7 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
|
||||
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
|
||||
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
|
||||
func TestCopyOfCopy(t *testing.T) {
|
||||
sdb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase()))
|
||||
sdb, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
addr := common.HexToAddress("aaaa")
|
||||
sdb.SetBalance(addr, big.NewInt(42))
|
||||
|
||||
|
||||
+2
-1
@@ -20,12 +20,13 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
// NewStateSync create a new state trie download scheduler.
|
||||
func NewStateSync(root common.Hash, database trie.DatabaseReader) *trie.Sync {
|
||||
func NewStateSync(root common.Hash, database ethdb.Reader) *trie.Sync {
|
||||
var syncer *trie.Sync
|
||||
callback := func(leaf []byte, parent common.Hash) error {
|
||||
var obj Account
|
||||
|
||||
+8
-7
@@ -22,6 +22,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
@@ -38,7 +39,7 @@ type testAccount struct {
|
||||
// makeTestState create a sample test state to test node-wise reconstruction.
|
||||
func makeTestState() (Database, common.Hash, []*testAccount) {
|
||||
// Create an empty state
|
||||
db := NewDatabase(ethdb.NewMemDatabase())
|
||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||
state, _ := New(common.Hash{}, db)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
@@ -124,7 +125,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
|
||||
// Tests that an empty state is not scheduled for syncing.
|
||||
func TestEmptyStateSync(t *testing.T) {
|
||||
empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||
if req := NewStateSync(empty, ethdb.NewMemDatabase()).Missing(1); len(req) != 0 {
|
||||
if req := NewStateSync(empty, rawdb.NewMemoryDatabase()).Missing(1); len(req) != 0 {
|
||||
t.Errorf("content requested for empty state: %v", req)
|
||||
}
|
||||
}
|
||||
@@ -139,7 +140,7 @@ func testIterativeStateSync(t *testing.T, batch int) {
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
queue := append([]common.Hash{}, sched.Missing(batch)...)
|
||||
@@ -171,7 +172,7 @@ func TestIterativeDelayedStateSync(t *testing.T) {
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
queue := append([]common.Hash{}, sched.Missing(0)...)
|
||||
@@ -208,7 +209,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
queue := make(map[common.Hash]struct{})
|
||||
@@ -248,7 +249,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
queue := make(map[common.Hash]struct{})
|
||||
@@ -295,7 +296,7 @@ func TestIncompleteStateSync(t *testing.T) {
|
||||
checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := ethdb.NewMemDatabase()
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb)
|
||||
|
||||
added := []common.Hash{}
|
||||
|
||||
+3
@@ -121,6 +121,9 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||
// Set the receipt logs and create a bloom for filtering
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
receipt.BlockHash = statedb.BlockHash()
|
||||
receipt.BlockNumber = header.Number
|
||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
||||
|
||||
return receipt, gas, err
|
||||
}
|
||||
|
||||
+23
-1
@@ -387,11 +387,26 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
||||
} else {
|
||||
// Reorg seems shallow enough to pull in all transactions into memory
|
||||
var discarded, included types.Transactions
|
||||
|
||||
var (
|
||||
rem = pool.chain.GetBlock(oldHead.Hash(), oldHead.Number.Uint64())
|
||||
add = pool.chain.GetBlock(newHead.Hash(), newHead.Number.Uint64())
|
||||
)
|
||||
if rem == nil {
|
||||
// This can happen if a setHead is performed, where we simply discard the old
|
||||
// head from the chain.
|
||||
// If that is the case, we don't have the lost transactions any more, and
|
||||
// there's nothing to add
|
||||
if newNum < oldNum {
|
||||
// If the reorg ended up on a lower number, it's indicative of setHead being the cause
|
||||
log.Debug("Skipping transaction reset caused by setHead",
|
||||
"old", oldHead.Hash(), "oldnum", oldNum, "new", newHead.Hash(), "newnum", newNum)
|
||||
} else {
|
||||
// If we reorged to a same or higher number, then it's not a case of setHead
|
||||
log.Warn("Transaction pool reset with missing oldhead",
|
||||
"old", oldHead.Hash(), "oldnum", oldNum, "new", newHead.Hash(), "newnum", newNum)
|
||||
}
|
||||
return
|
||||
}
|
||||
for rem.NumberU64() > add.NumberU64() {
|
||||
discarded = append(discarded, rem.Transactions()...)
|
||||
if rem = pool.chain.GetBlock(rem.ParentHash(), rem.NumberU64()-1); rem == nil {
|
||||
@@ -818,6 +833,9 @@ func (pool *TxPool) AddRemotes(txs []*types.Transaction) []error {
|
||||
|
||||
// addTx enqueues a single transaction into the pool if it is valid.
|
||||
func (pool *TxPool) addTx(tx *types.Transaction, local bool) error {
|
||||
// Cache sender in transaction before obtaining lock (pool.signer is immutable)
|
||||
types.Sender(pool.signer, tx)
|
||||
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
@@ -836,6 +854,10 @@ func (pool *TxPool) addTx(tx *types.Transaction, local bool) error {
|
||||
|
||||
// addTxs attempts to queue a batch of transactions if they are valid.
|
||||
func (pool *TxPool) addTxs(txs []*types.Transaction, local bool) []error {
|
||||
// Cache senders in transactions before obtaining lock (pool.signer is immutable)
|
||||
for _, tx := range txs {
|
||||
types.Sender(pool.signer, tx)
|
||||
}
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
|
||||
+26
-26
@@ -27,10 +27,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
@@ -78,7 +78,7 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec
|
||||
}
|
||||
|
||||
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
@@ -126,7 +126,7 @@ func validateEvents(events chan NewTxsEvent, count int) error {
|
||||
case ev := <-events:
|
||||
received = append(received, ev.Txs...)
|
||||
case <-time.After(time.Second):
|
||||
return fmt.Errorf("event #%d not fired", received)
|
||||
return fmt.Errorf("event #%d not fired", len(received))
|
||||
}
|
||||
}
|
||||
if len(received) > count {
|
||||
@@ -163,7 +163,7 @@ func (c *testChain) State() (*state.StateDB, error) {
|
||||
// a state change between those fetches.
|
||||
stdb := c.statedb
|
||||
if *c.trigger {
|
||||
c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
// simulate that the new head block included tx0 and tx1
|
||||
c.statedb.SetNonce(c.address, 2)
|
||||
c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether))
|
||||
@@ -181,7 +181,7 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
|
||||
var (
|
||||
key, _ = crypto.GenerateKey()
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
trigger = false
|
||||
)
|
||||
|
||||
@@ -335,7 +335,7 @@ func TestTransactionChainFork(t *testing.T) {
|
||||
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
resetState := func() {
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
||||
|
||||
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
@@ -364,7 +364,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
||||
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
resetState := func() {
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
||||
|
||||
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
@@ -554,7 +554,7 @@ func TestTransactionPostponing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the postponing with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
@@ -769,7 +769,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -857,7 +857,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
|
||||
evictionInterval = time.Second
|
||||
|
||||
// Create the pool to test the non-expiration enforcement
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1011,7 +1011,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1057,7 +1057,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1091,7 +1091,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1139,7 +1139,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
@@ -1260,7 +1260,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
@@ -1274,14 +1274,14 @@ 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, 100000, big.NewInt(int64(i)), keys[2])
|
||||
if err := pool.AddLocal(p_tx); err != nil {
|
||||
// Add pending transaction.
|
||||
pendingTx := pricedTransaction(i, 100000, big.NewInt(int64(i)), keys[2])
|
||||
if err := pool.AddLocal(pendingTx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Add queued
|
||||
q_tx := pricedTransaction(i+501, 100000, big.NewInt(int64(i)), keys[2])
|
||||
if err := pool.AddLocal(q_tx); err != nil {
|
||||
// Add queued transaction.
|
||||
queuedTx := pricedTransaction(i+501, 100000, big.NewInt(int64(i)), keys[2])
|
||||
if err := pool.AddLocal(queuedTx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1322,7 +1322,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1428,7 +1428,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1494,7 +1494,7 @@ func TestTransactionReplacement(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the pricing enforcement with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
@@ -1588,7 +1588,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||
os.Remove(journal)
|
||||
|
||||
// Create the original pool to inject transaction into the journal
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
@@ -1686,7 +1686,7 @@ func TestTransactionStatusCheck(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create the pool to test the status retrievals with
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
|
||||
+1
-1
@@ -32,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 uint64) error
|
||||
ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error
|
||||
}
|
||||
|
||||
// Processor is an interface for processing blocks using a given initial state.
|
||||
|
||||
+4
-2
@@ -21,10 +21,10 @@ import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -102,10 +102,12 @@ func (h *Header) Hash() common.Hash {
|
||||
return rlpHash(h)
|
||||
}
|
||||
|
||||
var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size())
|
||||
|
||||
// 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)
|
||||
return headerSize + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
|
||||
}
|
||||
|
||||
func rlpHash(x interface{}) (h common.Hash) {
|
||||
|
||||
+2
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
var _ = (*logMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (l Log) MarshalJSON() ([]byte, error) {
|
||||
type Log struct {
|
||||
Address common.Address `json:"address" gencodec:"required"`
|
||||
@@ -37,6 +38,7 @@ func (l Log) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (l *Log) UnmarshalJSON(input []byte) error {
|
||||
type Log struct {
|
||||
Address *common.Address `json:"address" gencodec:"required"`
|
||||
|
||||
+19
@@ -5,6 +5,7 @@ package types
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -23,6 +24,9 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress common.Address `json:"contractAddress"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
||||
}
|
||||
var enc Receipt
|
||||
enc.PostState = r.PostState
|
||||
@@ -33,6 +37,9 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||
enc.TxHash = r.TxHash
|
||||
enc.ContractAddress = r.ContractAddress
|
||||
enc.GasUsed = hexutil.Uint64(r.GasUsed)
|
||||
enc.BlockHash = r.BlockHash
|
||||
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
|
||||
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
@@ -47,6 +54,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress *common.Address `json:"contractAddress"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
|
||||
}
|
||||
var dec Receipt
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
@@ -81,5 +91,14 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
return errors.New("missing required field 'gasUsed' for Receipt")
|
||||
}
|
||||
r.GasUsed = uint64(*dec.GasUsed)
|
||||
if dec.BlockHash != nil {
|
||||
r.BlockHash = *dec.BlockHash
|
||||
}
|
||||
if dec.BlockNumber != nil {
|
||||
r.BlockNumber = (*big.Int)(dec.BlockNumber)
|
||||
}
|
||||
if dec.TransactionIndex != nil {
|
||||
r.TransactionIndex = uint(*dec.TransactionIndex)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+2
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
var _ = (*txdataMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (t txdata) MarshalJSON() ([]byte, error) {
|
||||
type txdata struct {
|
||||
AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||
@@ -40,6 +41,7 @@ func (t txdata) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (t *txdata) UnmarshalJSON(input []byte) error {
|
||||
type txdata struct {
|
||||
AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
|
||||
|
||||
+29
-18
@@ -68,7 +68,11 @@ type rlpLog struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type rlpStorageLog struct {
|
||||
// rlpStorageLog is the storage encoding of a log.
|
||||
type rlpStorageLog rlpLog
|
||||
|
||||
// LegacyRlpStorageLog is the previous storage encoding of a log including some redundant fields.
|
||||
type LegacyRlpStorageLog struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
Data []byte
|
||||
@@ -101,31 +105,38 @@ type LogForStorage Log
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (l *LogForStorage) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, rlpStorageLog{
|
||||
Address: l.Address,
|
||||
Topics: l.Topics,
|
||||
Data: l.Data,
|
||||
BlockNumber: l.BlockNumber,
|
||||
TxHash: l.TxHash,
|
||||
TxIndex: l.TxIndex,
|
||||
BlockHash: l.BlockHash,
|
||||
Index: l.Index,
|
||||
Address: l.Address,
|
||||
Topics: l.Topics,
|
||||
Data: l.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
//
|
||||
// Note some redundant fields(e.g. block number, tx hash etc) will be assembled later.
|
||||
func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||
blob, err := s.Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var dec rlpStorageLog
|
||||
err := s.Decode(&dec)
|
||||
err = rlp.DecodeBytes(blob, &dec)
|
||||
if err == nil {
|
||||
*l = LogForStorage{
|
||||
Address: dec.Address,
|
||||
Topics: dec.Topics,
|
||||
Data: dec.Data,
|
||||
BlockNumber: dec.BlockNumber,
|
||||
TxHash: dec.TxHash,
|
||||
TxIndex: dec.TxIndex,
|
||||
BlockHash: dec.BlockHash,
|
||||
Index: dec.Index,
|
||||
Address: dec.Address,
|
||||
Topics: dec.Topics,
|
||||
Data: dec.Data,
|
||||
}
|
||||
} else {
|
||||
// Try to decode log with previous definition.
|
||||
var dec LegacyRlpStorageLog
|
||||
err = rlp.DecodeBytes(blob, &dec)
|
||||
if err == nil {
|
||||
*l = LogForStorage{
|
||||
Address: dec.Address,
|
||||
Topics: dec.Topics,
|
||||
Data: dec.Data,
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
|
||||
+41
-6
@@ -20,6 +20,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -44,17 +45,24 @@ const (
|
||||
|
||||
// Receipt represents the results of a transaction.
|
||||
type Receipt struct {
|
||||
// Consensus fields
|
||||
// Consensus fields: These fields are defined by the Yellow Paper
|
||||
PostState []byte `json:"root"`
|
||||
Status uint64 `json:"status"`
|
||||
CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Logs []*Log `json:"logs" gencodec:"required"`
|
||||
|
||||
// Implementation fields (don't reorder!)
|
||||
// Implementation fields: These fields are added by geth when processing a transaction.
|
||||
// They are stored in the chain database.
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress common.Address `json:"contractAddress"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
|
||||
// Inclusion information: These fields provide information about the inclusion of the
|
||||
// transaction corresponding to this receipt.
|
||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||
BlockNumber *big.Int `json:"blockNumber,omitempty"`
|
||||
TransactionIndex uint `json:"transactionIndex"`
|
||||
}
|
||||
|
||||
type receiptMarshaling struct {
|
||||
@@ -62,6 +70,8 @@ type receiptMarshaling struct {
|
||||
Status hexutil.Uint64
|
||||
CumulativeGasUsed hexutil.Uint64
|
||||
GasUsed hexutil.Uint64
|
||||
BlockNumber *hexutil.Big
|
||||
TransactionIndex hexutil.Uint
|
||||
}
|
||||
|
||||
// receiptRLP is the consensus encoding of a receipt.
|
||||
@@ -72,7 +82,18 @@ type receiptRLP struct {
|
||||
Logs []*Log
|
||||
}
|
||||
|
||||
// receiptStorageRLP is the storage encoding of a receipt.
|
||||
type receiptStorageRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed uint64
|
||||
TxHash common.Hash
|
||||
ContractAddress common.Address
|
||||
Logs []*LogForStorage
|
||||
GasUsed uint64
|
||||
}
|
||||
|
||||
// LegacyReceiptStorageRLP is the previous storage encoding of a receipt including some unnecessary fields.
|
||||
type LegacyReceiptStorageRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed uint64
|
||||
Bloom Bloom
|
||||
@@ -159,7 +180,6 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
||||
enc := &receiptStorageRLP{
|
||||
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
|
||||
CumulativeGasUsed: r.CumulativeGasUsed,
|
||||
Bloom: r.Bloom,
|
||||
TxHash: r.TxHash,
|
||||
ContractAddress: r.ContractAddress,
|
||||
Logs: make([]*LogForStorage, len(r.Logs)),
|
||||
@@ -174,19 +194,34 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
||||
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
|
||||
// fields of a receipt from an RLP stream.
|
||||
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec receiptStorageRLP
|
||||
if err := s.Decode(&dec); err != nil {
|
||||
blob, err := s.Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var dec receiptStorageRLP
|
||||
if err := rlp.DecodeBytes(blob, &dec); err != nil {
|
||||
var sdec LegacyReceiptStorageRLP
|
||||
if err := rlp.DecodeBytes(blob, &sdec); err != nil {
|
||||
return err
|
||||
}
|
||||
dec.PostStateOrStatus = common.CopyBytes(sdec.PostStateOrStatus)
|
||||
dec.CumulativeGasUsed = sdec.CumulativeGasUsed
|
||||
dec.TxHash = sdec.TxHash
|
||||
dec.ContractAddress = sdec.ContractAddress
|
||||
dec.Logs = sdec.Logs
|
||||
dec.GasUsed = sdec.GasUsed
|
||||
}
|
||||
if err := (*Receipt)(r).setStatus(dec.PostStateOrStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
// Assign the consensus fields
|
||||
r.CumulativeGasUsed, r.Bloom = dec.CumulativeGasUsed, dec.Bloom
|
||||
r.CumulativeGasUsed = dec.CumulativeGasUsed
|
||||
r.Logs = make([]*Log, len(dec.Logs))
|
||||
for i, log := range dec.Logs {
|
||||
r.Logs[i] = (*Log)(log)
|
||||
}
|
||||
|
||||
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
|
||||
// Assign the implementation fields
|
||||
r.TxHash, r.ContractAddress, r.GasUsed = dec.TxHash, dec.ContractAddress, dec.GasUsed
|
||||
return nil
|
||||
|
||||
+24
-6
@@ -23,13 +23,31 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
// calculates the memory size required for a step
|
||||
func calcMemSize(off, l *big.Int) *big.Int {
|
||||
if l.Sign() == 0 {
|
||||
return common.Big0
|
||||
// calcMemSize64 calculates the required memory size, and returns
|
||||
// the size and whether the result overflowed uint64
|
||||
func calcMemSize64(off, l *big.Int) (uint64, bool) {
|
||||
if !l.IsUint64() {
|
||||
return 0, true
|
||||
}
|
||||
return calcMemSize64WithUint(off, l.Uint64())
|
||||
}
|
||||
|
||||
return new(big.Int).Add(off, l)
|
||||
// calcMemSize64WithUint calculates the required memory size, and returns
|
||||
// the size and whether the result overflowed uint64
|
||||
// Identical to calcMemSize64, but length is a uint64
|
||||
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
|
||||
// if length is zero, memsize is always zero, regardless of offset
|
||||
if length64 == 0 {
|
||||
return 0, false
|
||||
}
|
||||
// Check that offset doesn't overflow
|
||||
if !off.IsUint64() {
|
||||
return 0, true
|
||||
}
|
||||
offset64 := off.Uint64()
|
||||
val := offset64 + length64
|
||||
// if value < either of it's parts, then it overflowed
|
||||
return val, val < offset64
|
||||
}
|
||||
|
||||
// getData returns a slice from the data based on the start and size and pads
|
||||
@@ -59,7 +77,7 @@ func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
|
||||
// bigUint64 returns the integer casted to a uint64 and returns whether it
|
||||
// overflowed in the process.
|
||||
func bigUint64(v *big.Int) (uint64, bool) {
|
||||
return v.Uint64(), v.BitLen() > 64
|
||||
return v.Uint64(), !v.IsUint64()
|
||||
}
|
||||
|
||||
// toWordSize returns the ceiled word size required for memory expansion.
|
||||
|
||||
+6
-9
@@ -267,9 +267,8 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||
snapshot = evm.StateDB.Snapshot()
|
||||
to = AccountRef(caller.Address())
|
||||
)
|
||||
// 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.
|
||||
// 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))
|
||||
|
||||
@@ -333,9 +332,8 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||
to = AccountRef(addr)
|
||||
snapshot = evm.StateDB.Snapshot()
|
||||
)
|
||||
// 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.
|
||||
// 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, new(big.Int), gas)
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
|
||||
@@ -396,9 +394,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||
}
|
||||
evm.Transfer(evm.StateDB, caller.Address(), address, value)
|
||||
|
||||
// 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.
|
||||
// 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, AccountRef(address), value, gas)
|
||||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||
|
||||
|
||||
+2
-6
@@ -30,10 +30,6 @@ const (
|
||||
GasMidStep uint64 = 8
|
||||
GasSlowStep uint64 = 10
|
||||
GasExtStep uint64 = 20
|
||||
|
||||
GasReturn uint64 = 0
|
||||
GasStop uint64 = 0
|
||||
GasContractByte uint64 = 200
|
||||
)
|
||||
|
||||
// calcGas returns the actual gas cost of the call.
|
||||
@@ -47,11 +43,11 @@ func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.
|
||||
// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
|
||||
// is smaller than the requested amount. Therefor we return the new gas instead
|
||||
// of returning an error.
|
||||
if callCost.BitLen() > 64 || gas < callCost.Uint64() {
|
||||
if !callCost.IsUint64() || gas < callCost.Uint64() {
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
if callCost.BitLen() > 64 {
|
||||
if !callCost.IsUint64() {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
|
||||
+2
-20
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// memoryGasCosts calculates the quadratic gas for memory expansion. It does so
|
||||
// memoryGasCost calculates the quadratic gas for memory expansion. It does so
|
||||
// only for the memory region that is expanded, not the total memory.
|
||||
func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
||||
|
||||
@@ -57,12 +57,6 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func constGasFunc(gas uint64) gasFunc {
|
||||
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
|
||||
func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
@@ -382,7 +376,7 @@ func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
|
||||
gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas
|
||||
overflow bool
|
||||
)
|
||||
if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow {
|
||||
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
@@ -521,15 +515,3 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return GasFastestStep, nil
|
||||
}
|
||||
|
||||
func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return GasFastestStep, nil
|
||||
}
|
||||
|
||||
func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return GasFastestStep, nil
|
||||
}
|
||||
|
||||
+25
-12
@@ -18,7 +18,6 @@ package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -35,6 +34,7 @@ var (
|
||||
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
|
||||
errExecutionReverted = errors.New("evm: execution reverted")
|
||||
errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
|
||||
errInvalidJump = errors.New("evm: invalid jump destination")
|
||||
)
|
||||
|
||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
@@ -405,7 +405,7 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
|
||||
}
|
||||
|
||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(contract.Address().Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(contract.Address().Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -416,12 +416,12 @@ func opBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
|
||||
}
|
||||
|
||||
func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(interpreter.evm.Origin.Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(contract.Caller().Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(contract.Caller().Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -467,7 +467,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contrac
|
||||
)
|
||||
defer interpreter.intPool.put(memOffset, dataOffset, length, end)
|
||||
|
||||
if end.BitLen() > 64 || uint64(len(interpreter.returnData)) < end.Uint64() {
|
||||
if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
|
||||
return nil, errReturnDataOutOfBounds
|
||||
}
|
||||
memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
|
||||
@@ -572,7 +572,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, me
|
||||
}
|
||||
|
||||
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(interpreter.evm.Coinbase.Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -645,8 +645,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
|
||||
func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
pos := stack.pop()
|
||||
if !contract.validJumpdest(pos) {
|
||||
nop := contract.GetOp(pos.Uint64())
|
||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
||||
return nil, errInvalidJump
|
||||
}
|
||||
*pc = pos.Uint64()
|
||||
|
||||
@@ -658,8 +657,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
|
||||
pos, cond := stack.pop(), stack.pop()
|
||||
if cond.Sign() != 0 {
|
||||
if !contract.validJumpdest(pos) {
|
||||
nop := contract.GetOp(pos.Uint64())
|
||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
||||
return nil, errInvalidJump
|
||||
}
|
||||
*pc = pos.Uint64()
|
||||
} else {
|
||||
@@ -711,7 +709,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
|
||||
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
|
||||
stack.push(interpreter.intPool.getZero())
|
||||
} else {
|
||||
stack.push(addr.Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
|
||||
}
|
||||
contract.Gas += returnGas
|
||||
interpreter.intPool.put(value, offset, size)
|
||||
@@ -739,7 +737,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
|
||||
if suberr != nil {
|
||||
stack.push(interpreter.intPool.getZero())
|
||||
} else {
|
||||
stack.push(addr.Big())
|
||||
stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
|
||||
}
|
||||
contract.Gas += returnGas
|
||||
interpreter.intPool.put(endowment, offset, size, salt)
|
||||
@@ -912,6 +910,21 @@ func makeLog(size int) executionFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// opPush1 is a specialized version of pushN
|
||||
func opPush1(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var (
|
||||
codeLen = uint64(len(contract.Code))
|
||||
integer = interpreter.intPool.get()
|
||||
)
|
||||
*pc += 1
|
||||
if *pc < codeLen {
|
||||
stack.push(integer.SetUint64(uint64(contract.Code[*pc])))
|
||||
} else {
|
||||
stack.push(integer.SetUint64(0))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// make push instruction function
|
||||
func makePush(size uint64, pushByteSize int) executionFunc {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
|
||||
+29
-25
@@ -118,22 +118,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
|
||||
if in.evm.chainRules.IsByzantium {
|
||||
if in.readOnly {
|
||||
// If the interpreter is operating in readonly mode, make sure no
|
||||
// state-modifying operation is performed. The 3rd stack item
|
||||
// for a call operation is the value. Transferring value from one
|
||||
// account to the others means the state is modified and should also
|
||||
// return with an error.
|
||||
if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) {
|
||||
return errWriteProtection
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run loops and evaluates the contract's code with the given input data and returns
|
||||
// the return byte-slice and an error if one occurred.
|
||||
//
|
||||
@@ -182,6 +166,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
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
|
||||
res []byte // result of the opcode execution function
|
||||
)
|
||||
contract.Input = input
|
||||
|
||||
@@ -216,19 +201,35 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
if !operation.valid {
|
||||
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
|
||||
}
|
||||
if err := operation.validateStack(stack); err != nil {
|
||||
return nil, err
|
||||
// Validate stack
|
||||
if sLen := stack.len(); sLen < operation.minStack {
|
||||
return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack)
|
||||
} else if sLen > operation.maxStack {
|
||||
return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack)
|
||||
}
|
||||
// If the operation is valid, enforce and write restrictions
|
||||
if err := in.enforceRestrictions(op, operation, stack); err != nil {
|
||||
return nil, err
|
||||
if in.readOnly && in.evm.chainRules.IsByzantium {
|
||||
// If the interpreter is operating in readonly mode, make sure no
|
||||
// state-modifying operation is performed. The 3rd stack item
|
||||
// for a call operation is the value. Transferring value from one
|
||||
// account to the others means the state is modified and should also
|
||||
// return with an error.
|
||||
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
|
||||
return nil, errWriteProtection
|
||||
}
|
||||
}
|
||||
// Static portion of gas
|
||||
if !contract.UseGas(operation.constantGas) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
||||
var memorySize uint64
|
||||
// calculate the new memory size and expand the memory to fit
|
||||
// the operation
|
||||
// Memory check needs to be done prior to evaluating the dynamic gas portion,
|
||||
// to detect calculation overflows
|
||||
if operation.memorySize != nil {
|
||||
memSize, overflow := bigUint64(operation.memorySize(stack))
|
||||
memSize, overflow := operation.memorySize(stack)
|
||||
if overflow {
|
||||
return nil, errGasUintOverflow
|
||||
}
|
||||
@@ -238,11 +239,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
return nil, errGasUintOverflow
|
||||
}
|
||||
}
|
||||
// Dynamic portion of gas
|
||||
// consume the gas and return an error if not enough gas is available.
|
||||
// cost is explicitly set so that the capture state defer method can 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 operation.dynamicGas != nil {
|
||||
cost, err = operation.dynamicGas(in.gasTable, in.evm, contract, stack, mem, memorySize)
|
||||
if err != nil || !contract.UseGas(cost) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
if memorySize > 0 {
|
||||
mem.Resize(memorySize)
|
||||
@@ -254,7 +258,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
}
|
||||
|
||||
// execute the operation
|
||||
res, err := operation.execute(&pc, in, contract, mem, stack)
|
||||
res, err = operation.execute(&pc, in, contract, mem, stack)
|
||||
// verifyPool is a build flag. Pool verification makes sure the integrity
|
||||
// of the integer pool by comparing values to a default value.
|
||||
if verifyPool {
|
||||
|
||||
+750
-608
File diff suppressed because it is too large
Load Diff
+66
-50
@@ -16,82 +16,98 @@
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
func memorySha3(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
||||
func memorySha3(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryCallDataCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
||||
func memoryCallDataCopy(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryReturnDataCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
||||
func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryCodeCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(2))
|
||||
func memoryCodeCopy(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryExtCodeCopy(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(1), stack.Back(3))
|
||||
func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(3))
|
||||
}
|
||||
|
||||
func memoryMLoad(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
||||
func memoryMLoad(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||
}
|
||||
|
||||
func memoryMStore8(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), big.NewInt(1))
|
||||
func memoryMStore8(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64WithUint(stack.Back(0), 1)
|
||||
}
|
||||
|
||||
func memoryMStore(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), big.NewInt(32))
|
||||
func memoryMStore(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||
}
|
||||
|
||||
func memoryCreate(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
||||
func memoryCreate(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryCreate2(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(1), stack.Back(2))
|
||||
func memoryCreate2(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||
}
|
||||
|
||||
func memoryCall(stack *Stack) *big.Int {
|
||||
x := calcMemSize(stack.Back(5), stack.Back(6))
|
||||
y := calcMemSize(stack.Back(3), stack.Back(4))
|
||||
|
||||
return math.BigMax(x, y)
|
||||
func memoryCall(stack *Stack) (uint64, bool) {
|
||||
x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
if x > y {
|
||||
return x, false
|
||||
}
|
||||
return y, false
|
||||
}
|
||||
func memoryDelegateCall(stack *Stack) (uint64, bool) {
|
||||
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
if x > y {
|
||||
return x, false
|
||||
}
|
||||
return y, false
|
||||
}
|
||||
|
||||
func memoryDelegateCall(stack *Stack) *big.Int {
|
||||
x := calcMemSize(stack.Back(4), stack.Back(5))
|
||||
y := calcMemSize(stack.Back(2), stack.Back(3))
|
||||
|
||||
return math.BigMax(x, y)
|
||||
func memoryStaticCall(stack *Stack) (uint64, bool) {
|
||||
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
if x > y {
|
||||
return x, false
|
||||
}
|
||||
return y, false
|
||||
}
|
||||
|
||||
func memoryStaticCall(stack *Stack) *big.Int {
|
||||
x := calcMemSize(stack.Back(4), stack.Back(5))
|
||||
y := calcMemSize(stack.Back(2), stack.Back(3))
|
||||
|
||||
return math.BigMax(x, y)
|
||||
func memoryReturn(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryReturn(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
||||
func memoryRevert(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryRevert(stack *Stack) *big.Int {
|
||||
return calcMemSize(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
func memoryLog(stack *Stack) *big.Int {
|
||||
mSize, mStart := stack.Back(1), stack.Back(0)
|
||||
return calcMemSize(mStart, mSize)
|
||||
func memoryLog(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
}
|
||||
|
||||
+3
-3
@@ -22,10 +22,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
@@ -99,7 +99,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||
setDefaults(cfg)
|
||||
|
||||
if cfg.State == nil {
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
}
|
||||
var (
|
||||
address = common.BytesToAddress([]byte("contract"))
|
||||
@@ -129,7 +129,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
||||
setDefaults(cfg)
|
||||
|
||||
if cfg.State == nil {
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
}
|
||||
var (
|
||||
vmenv = NewEnv(cfg)
|
||||
|
||||
+3
-3
@@ -23,9 +23,9 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
@@ -95,7 +95,7 @@ func TestExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCall(t *testing.T) {
|
||||
state, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
address := common.HexToAddress("0x0a")
|
||||
state.SetCode(address, []byte{
|
||||
byte(vm.PUSH1), 10,
|
||||
@@ -151,7 +151,7 @@ func BenchmarkCall(b *testing.B) {
|
||||
}
|
||||
func benchmarkEVM_Create(bench *testing.B, code string) {
|
||||
var (
|
||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
sender = common.BytesToAddress([]byte("sender"))
|
||||
receiver = common.BytesToAddress([]byte("receiver"))
|
||||
)
|
||||
|
||||
+15
-17
@@ -17,28 +17,26 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func makeStackFunc(pop, push int) stackValidationFunc {
|
||||
return func(stack *Stack) error {
|
||||
if err := stack.require(pop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if stack.len()+push-pop > int(params.StackLimit) {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func minSwapStack(n int) int {
|
||||
return minStack(n, n)
|
||||
}
|
||||
func maxSwapStack(n int) int {
|
||||
return maxStack(n, n)
|
||||
}
|
||||
|
||||
func makeDupStackFunc(n int) stackValidationFunc {
|
||||
return makeStackFunc(n, n+1)
|
||||
func minDupStack(n int) int {
|
||||
return minStack(n, n+1)
|
||||
}
|
||||
func maxDupStack(n int) int {
|
||||
return maxStack(n, n+1)
|
||||
}
|
||||
|
||||
func makeSwapStackFunc(n int) stackValidationFunc {
|
||||
return makeStackFunc(n, n)
|
||||
func maxStack(pop, push int) int {
|
||||
return int(params.StackLimit) + pop - push
|
||||
}
|
||||
func minStack(pops, push int) int {
|
||||
return pops
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user