aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeterminant <tederminant@gmail.com>2020-09-16 00:39:05 -0400
committerDeterminant <tederminant@gmail.com>2020-09-16 00:39:05 -0400
commitfec21d6c958264ff5ff729c0c91e4a882aecc23b (patch)
treece806865e047ffc98e93ee707b9419d0c64e7297
parent78745551c077bf54151202138c2629f288769561 (diff)
add lists to keep track of actually hacked files
-rw-r--r--copied-list.txt107
-rw-r--r--core/events.go7
-rw-r--r--core/evm.go38
-rw-r--r--core/genesis.go78
-rw-r--r--core/rawdb/accessors_chain.go238
-rw-r--r--core/state/journal.go10
-rw-r--r--core/state/state_object.go161
-rw-r--r--core/state/statedb.go682
-rw-r--r--core/state_processor.go22
-rw-r--r--hacked-list.txt44
10 files changed, 977 insertions, 410 deletions
diff --git a/copied-list.txt b/copied-list.txt
new file mode 100644
index 0000000..94353ff
--- /dev/null
+++ b/copied-list.txt
@@ -0,0 +1,107 @@
+./consensus/clique/api.go
+./consensus/clique/snapshot.go
+./consensus/errors.go
+./consensus/ethash/algorithm.go
+./consensus/ethash/api.go
+./consensus/ethash/ethash.go
+./consensus/ethash/sealer.go
+./consensus/misc/dao.go
+./consensus/misc/forks.go
+./core/blockchain_insert.go
+./core/blocks.go
+./core/block_validator.go
+./core/bloombits/doc.go
+./core/bloombits/generator.go
+./core/bloombits/matcher.go
+./core/bloombits/scheduler.go
+./core/chain_indexer.go
+./core/error.go
+./core/gaspool.go
+./core/genesis_alloc.go
+./core/headerchain.go
+./core/mkalloc.go
+./core/rawdb/accessors_indexes.go
+./core/rawdb/accessors_metadata.go
+./core/rawdb/database.go
+./core/rawdb/freezer.go
+./core/rawdb/freezer_reinit.go
+./core/rawdb/freezer_table.go
+./core/rawdb/schema.go
+./core/rawdb/table.go
+./core/state/database.go
+./core/state/dump.go
+./core/state/iterator.go
+./core/state_prefetcher.go
+./core/state/sync.go
+./core/state_transition.go
+./core/tx_cacher.go
+./core/tx_journal.go
+./core/tx_list.go
+./core/tx_noncer.go
+./core/types/bloom9.go
+./core/types/derive_sha.go
+./core/types/gen_header_json.go
+./core/types/gen_log_json.go
+./core/types/gen_receipt_json.go
+./core/types/gen_tx_json.go
+./core/types.go
+./core/types/log.go
+./core/types/receipt.go
+./core/types/transaction.go
+./core/types/transaction_signing.go
+./core/vm/analysis.go
+./core/vm/common.go
+./core/vm/contract.go
+./core/vm/contracts.go
+./core/vm/doc.go
+./core/vm/eips.go
+./core/vm/gas.go
+./core/vm/gas_table.go
+./core/vm/gen_structlog.go
+./core/vm/intpool.go
+./core/vm/int_pool_verifier_empty.go
+./core/vm/int_pool_verifier.go
+./core/vm/logger.go
+./core/vm/logger_json.go
+./core/vm/memory.go
+./core/vm/stack.go
+./core/vm/stack_table.go
+./eth/bloombits.go
+./eth/filters/api.go
+./eth/filters/filter.go
+./eth/filters/filter_system.go
+./eth/gasprice/gasprice.go
+./eth/metrics.go
+./eth/protocol.go
+./ethstats/ethstats.go
+./eth/tracers/internal/tracers/tracers.go
+./eth/tracers/tracer.go
+./eth/tracers/tracers.go
+./internal/debug/api.go
+./internal/debug/flags.go
+./internal/debug/loudpanic_fallback.go
+./internal/debug/loudpanic.go
+./internal/debug/trace_fallback.go
+./internal/debug/trace.go
+./internal/ethapi/addrlock.go
+./miner/unconfirmed.go
+./node/defaults.go
+./node/errors.go
+./params/config.go
+./params/dao.go
+./params/denomination.go
+./params/network_params.go
+./params/version.go
+./rpc/constants_unix_nocgo.go
+./rpc/doc.go
+./rpc/errors.go
+./rpc/gzip.go
+./rpc/handler.go
+./rpc/http.go
+./rpc/ipc_js.go
+./rpc/json.go
+./rpc/server.go
+./rpc/service.go
+./rpc/subscription.go
+./rpc/websocket.go
+./accounts/*
diff --git a/core/events.go b/core/events.go
index f05e69b..28fbc44 100644
--- a/core/events.go
+++ b/core/events.go
@@ -18,7 +18,7 @@ package core
import (
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common"
)
// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
@@ -27,11 +27,6 @@ type NewTxsEvent struct{ Txs []*types.Transaction }
// NewTxPoolHeadEvent is posted when the pool head is updated.
type NewTxPoolHeadEvent struct{ Block *types.Block }
-// PendingLogsEvent is posted pre mining and notifies of pending logs.
-type PendingLogsEvent struct {
- Logs []*types.Log
-}
-
// NewMinedBlockEvent is posted when a block has been imported.
type NewMinedBlockEvent struct{ Block *types.Block }
diff --git a/core/evm.go b/core/evm.go
index 796b312..74891d7 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -22,8 +22,8 @@ import (
"github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
)
// ChainContext supports retrieving headers and consensus parameters from the
@@ -63,24 +63,32 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author
// GetHashFn returns a GetHashFunc which retrieves header hashes by number
func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash {
- var cache map[uint64]common.Hash
+ // Cache will initially contain [refHash.parent],
+ // Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...]
+ var cache []common.Hash
return func(n uint64) common.Hash {
// If there's no hash cache yet, make one
- if cache == nil {
- cache = map[uint64]common.Hash{
- ref.Number.Uint64() - 1: ref.ParentHash,
- }
+ if len(cache) == 0 {
+ cache = append(cache, ref.ParentHash)
}
- // Try to fulfill the request from the cache
- if hash, ok := cache[n]; ok {
- return hash
+ if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) {
+ return cache[idx]
}
- // Not cached, iterate the blocks and cache the hashes
- for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
- cache[header.Number.Uint64()-1] = header.ParentHash
- if n == header.Number.Uint64()-1 {
- return header.ParentHash
+ // No luck in the cache, but we can start iterating from the last element we already know
+ lastKnownHash := cache[len(cache)-1]
+ lastKnownNumber := ref.Number.Uint64() - uint64(len(cache))
+
+ for {
+ header := chain.GetHeader(lastKnownHash, lastKnownNumber)
+ if header == nil {
+ break
+ }
+ cache = append(cache, header.ParentHash)
+ lastKnownHash = header.ParentHash
+ lastKnownNumber = header.Number.Uint64() - 1
+ if n == lastKnownNumber {
+ return lastKnownHash
}
}
return common.Hash{}
diff --git a/core/genesis.go b/core/genesis.go
index 7d21d00..e48f411 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -29,13 +29,14 @@ import (
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/common/math"
- "github.com/ava-labs/go-ethereum/crypto"
- "github.com/ava-labs/go-ethereum/ethdb"
- "github.com/ava-labs/go-ethereum/log"
- "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
)
//go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
@@ -155,10 +156,6 @@ func (e *GenesisMismatchError) Error() string {
//
// The returned chain configuration is never nil.
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
- return SetupGenesisBlockWithOverride(db, genesis, nil)
-}
-
-func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideIstanbul *big.Int) (*params.ChainConfig, common.Hash, error) {
if genesis != nil && genesis.Config == nil {
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
}
@@ -181,7 +178,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override
// We have the genesis block in database(perhaps in ancient database)
// but the corresponding state is missing.
header := rawdb.ReadHeader(db, stored, 0)
- if _, err := state.New(header.Root, state.NewDatabaseWithCache(db, 0)); err != nil {
+ if _, err := state.New(header.Root, state.NewDatabaseWithCache(db, 0, ""), nil); err != nil {
if genesis == nil {
genesis = DefaultGenesisBlock()
}
@@ -207,8 +204,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override
// Get the existing chain configuration.
newcfg := configOrDefault(genesis, stored)
- if overrideIstanbul != nil {
- newcfg.IstanbulBlock = overrideIstanbul
+ if err := newcfg.CheckConfigForkOrder(); err != nil {
+ return newcfg, common.Hash{}, err
}
storedcfg := rawdb.ReadChainConfig(db, stored)
if storedcfg == nil {
@@ -243,8 +240,14 @@ func configOrDefault(g *Genesis, ghash common.Hash) *params.ChainConfig {
return g.Config
case ghash == params.MainnetGenesisHash:
return params.MainnetChainConfig
- case ghash == params.TestnetGenesisHash:
- return params.TestnetChainConfig
+ case ghash == params.RopstenGenesisHash:
+ return params.RopstenChainConfig
+ case ghash == params.RinkebyGenesisHash:
+ return params.RinkebyChainConfig
+ case ghash == params.GoerliGenesisHash:
+ return params.GoerliChainConfig
+ case ghash == params.YoloV1GenesisHash:
+ return params.YoloV1ChainConfig
default:
return params.AllEthashProtocolChanges
}
@@ -256,7 +259,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
if db == nil {
db = rawdb.NewMemoryDatabase()
}
- statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(db), nil)
for addr, account := range g.Alloc {
statedb.AddBalance(addr, account.Balance)
statedb.SetCode(addr, account.Code)
@@ -292,9 +295,9 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
head.Difficulty = params.GenesisDifficulty
}
statedb.Commit(false)
- statedb.Database().TrieDB().Commit(root, true)
+ statedb.Database().TrieDB().Commit(root, true, nil)
- return types.NewBlock(head, nil, nil, nil, nil)
+ return types.NewBlock(head, nil, nil, nil, new(trie.Trie), nil)
}
// Commit writes the block and state of a genesis specification to the database.
@@ -304,6 +307,13 @@ func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
if block.Number().Sign() != 0 {
return nil, fmt.Errorf("can't commit genesis block with number > 0")
}
+ config := g.Config
+ if config == nil {
+ config = params.AllEthashProtocolChanges
+ }
+ if err := config.CheckConfigForkOrder(); err != nil {
+ return nil, err
+ }
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty)
rawdb.WriteBlock(db, block)
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
@@ -311,11 +321,6 @@ func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
rawdb.WriteHeadBlockHash(db, block.Hash())
rawdb.WriteHeadFastBlockHash(db, block.Hash())
rawdb.WriteHeadHeaderHash(db, block.Hash())
-
- config := g.Config
- if config == nil {
- config = params.AllEthashProtocolChanges
- }
rawdb.WriteChainConfig(db, block.Hash(), config)
return block, nil
}
@@ -348,15 +353,15 @@ func DefaultGenesisBlock() *Genesis {
}
}
-// DefaultTestnetGenesisBlock returns the Ropsten network genesis block.
-func DefaultTestnetGenesisBlock() *Genesis {
+// DefaultRopstenGenesisBlock returns the Ropsten network genesis block.
+func DefaultRopstenGenesisBlock() *Genesis {
return &Genesis{
- Config: params.TestnetChainConfig,
+ Config: params.RopstenChainConfig,
Nonce: 66,
ExtraData: hexutil.MustDecode("0x3535353535353535353535353535353535353535353535353535353535353535"),
GasLimit: 16777216,
Difficulty: big.NewInt(1048576),
- Alloc: decodePrealloc(testnetAllocData),
+ Alloc: decodePrealloc(ropstenAllocData),
}
}
@@ -384,8 +389,18 @@ func DefaultGoerliGenesisBlock() *Genesis {
}
}
-// DeveloperGenesisBlock returns the 'geth --dev' genesis block. Note, this must
-// be seeded with the
+func DefaultYoloV1GenesisBlock() *Genesis {
+ return &Genesis{
+ Config: params.YoloV1ChainConfig,
+ Timestamp: 0x5ed754f1,
+ ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000008a37866fd3627c9205a37c8685666f32ec07bb1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
+ GasLimit: 0x47b760,
+ Difficulty: big.NewInt(1),
+ Alloc: decodePrealloc(yoloV1AllocData),
+ }
+}
+
+// DeveloperGenesisBlock returns the 'geth --dev' genesis block.
func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
// Override the default period to the user requested one
config := *params.AllCliqueProtocolChanges
@@ -395,7 +410,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
return &Genesis{
Config: &config,
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...),
- GasLimit: 6283185,
+ GasLimit: 11500000,
Difficulty: big.NewInt(1),
Alloc: map[common.Address]GenesisAccount{
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
@@ -406,6 +421,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
+ common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))},
},
}
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
index fdfd6ec..cd48885 100644
--- a/core/rawdb/accessors_chain.go
+++ b/core/rawdb/accessors_chain.go
@@ -23,10 +23,11 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/ethdb"
- "github.com/ava-labs/go-ethereum/log"
- "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "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.
@@ -68,7 +69,7 @@ func ReadAllHashes(db ethdb.Iteratee, number uint64) []common.Hash {
prefix := headerKeyPrefix(number)
hashes := make([]common.Hash, 0, 1)
- it := db.NewIteratorWithPrefix(prefix)
+ it := db.NewIterator(prefix, nil)
defer it.Release()
for it.Next() {
@@ -79,6 +80,39 @@ func ReadAllHashes(db ethdb.Iteratee, number uint64) []common.Hash {
return hashes
}
+// ReadAllCanonicalHashes retrieves all canonical number and hash mappings at the
+// certain chain range. If the accumulated entries reaches the given threshold,
+// abort the iteration and return the semi-finish result.
+func ReadAllCanonicalHashes(db ethdb.Iteratee, from uint64, to uint64, limit int) ([]uint64, []common.Hash) {
+ // Short circuit if the limit is 0.
+ if limit == 0 {
+ return nil, nil
+ }
+ var (
+ numbers []uint64
+ hashes []common.Hash
+ )
+ // Construct the key prefix of start point.
+ start, end := headerHashKey(from), headerHashKey(to)
+ it := db.NewIterator(nil, start)
+ defer it.Release()
+
+ for it.Next() {
+ if bytes.Compare(it.Key(), end) >= 0 {
+ break
+ }
+ if key := it.Key(); len(key) == len(headerPrefix)+8+1 && bytes.Equal(key[len(key)-1:], headerHashSuffix) {
+ numbers = append(numbers, binary.BigEndian.Uint64(key[len(headerPrefix):len(headerPrefix)+8]))
+ hashes = append(hashes, common.BytesToHash(it.Value()))
+ // If the accumulated entries reaches the limit threshold, return.
+ if len(numbers) >= limit {
+ break
+ }
+ }
+ }
+ return numbers, hashes
+}
+
// ReadHeaderNumber returns the header number assigned to a hash.
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
data, _ := db.Get(headerNumberKey(hash))
@@ -153,6 +187,32 @@ func WriteHeadFastBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
}
}
+// ReadLastPivotNumber retrieves the number of the last pivot block. If the node
+// full synced, the last pivot will always be nil.
+func ReadLastPivotNumber(db ethdb.KeyValueReader) *uint64 {
+ data, _ := db.Get(lastPivotKey)
+ if len(data) == 0 {
+ return nil
+ }
+ var pivot uint64
+ if err := rlp.DecodeBytes(data, &pivot); err != nil {
+ log.Error("Invalid pivot block number in database", "err", err)
+ return nil
+ }
+ return &pivot
+}
+
+// WriteLastPivotNumber stores the number of the last pivot block.
+func WriteLastPivotNumber(db ethdb.KeyValueWriter, pivot uint64) {
+ enc, err := rlp.EncodeToBytes(pivot)
+ if err != nil {
+ log.Crit("Failed to encode pivot block number", "err", err)
+ }
+ if err := db.Put(lastPivotKey, enc); err != nil {
+ log.Crit("Failed to store pivot block number", "err", err)
+ }
+}
+
// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
// reporting correct numbers across restarts.
func ReadFastTrieProgress(db ethdb.KeyValueReader) uint64 {
@@ -171,20 +231,66 @@ func WriteFastTrieProgress(db ethdb.KeyValueWriter, count uint64) {
}
}
+// ReadTxIndexTail retrieves the number of oldest indexed block
+// whose transaction indices has been indexed. If the corresponding entry
+// is non-existent in database it means the indexing has been finished.
+func ReadTxIndexTail(db ethdb.KeyValueReader) *uint64 {
+ data, _ := db.Get(txIndexTailKey)
+ if len(data) != 8 {
+ return nil
+ }
+ number := binary.BigEndian.Uint64(data)
+ return &number
+}
+
+// WriteTxIndexTail stores the number of oldest indexed block
+// into database.
+func WriteTxIndexTail(db ethdb.KeyValueWriter, number uint64) {
+ if err := db.Put(txIndexTailKey, encodeBlockNumber(number)); err != nil {
+ log.Crit("Failed to store the transaction index tail", "err", err)
+ }
+}
+
+// ReadFastTxLookupLimit retrieves the tx lookup limit used in fast sync.
+func ReadFastTxLookupLimit(db ethdb.KeyValueReader) *uint64 {
+ data, _ := db.Get(fastTxLookupLimitKey)
+ if len(data) != 8 {
+ return nil
+ }
+ number := binary.BigEndian.Uint64(data)
+ return &number
+}
+
+// WriteFastTxLookupLimit stores the txlookup limit used in fast sync into database.
+func WriteFastTxLookupLimit(db ethdb.KeyValueWriter, number uint64) {
+ if err := db.Put(fastTxLookupLimitKey, encodeBlockNumber(number)); err != nil {
+ log.Crit("Failed to store transaction lookup limit for fast sync", "err", err)
+ }
+}
+
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
+ // First try to look up the data in ancient database. Extra hash
+ // comparison is necessary since ancient database only maintains
+ // the canonical data.
data, _ := db.Ancient(freezerHeaderTable, number)
- if len(data) == 0 {
- data, _ = db.Get(headerKey(number, hash))
- // In the background freezer is moving data from leveldb to flatten files.
- // So during the first check for ancient db, the data is not yet in there,
- // but when we reach into leveldb, the data was already moved. That would
- // result in a not found error.
- if len(data) == 0 {
- data, _ = db.Ancient(freezerHeaderTable, number)
- }
+ if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
+ return data
}
- return data
+ // Then try to look up the data in leveldb.
+ data, _ = db.Get(headerKey(number, hash))
+ if len(data) > 0 {
+ return data
+ }
+ // In the background freezer is moving data from leveldb to flatten files.
+ // So during the first check for ancient db, the data is not yet in there,
+ // but when we reach into leveldb, the data was already moved. That would
+ // result in a not found error.
+ data, _ = db.Ancient(freezerHeaderTable, number)
+ if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
+ return data
+ }
+ return nil // Can't find the data anywhere.
}
// HasHeader verifies the existence of a block header corresponding to the hash.
@@ -251,9 +357,43 @@ func deleteHeaderWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
+ // First try to look up the data in ancient database. Extra hash
+ // comparison is necessary since ancient database only maintains
+ // the canonical data.
+ data, _ := db.Ancient(freezerBodiesTable, number)
+ if len(data) > 0 {
+ h, _ := db.Ancient(freezerHashTable, number)
+ if common.BytesToHash(h) == hash {
+ return data
+ }
+ }
+ // Then try to look up the data in leveldb.
+ data, _ = db.Get(blockBodyKey(number, hash))
+ if len(data) > 0 {
+ return data
+ }
+ // In the background freezer is moving data from leveldb to flatten files.
+ // So during the first check for ancient db, the data is not yet in there,
+ // but when we reach into leveldb, the data was already moved. That would
+ // result in a not found error.
+ data, _ = db.Ancient(freezerBodiesTable, number)
+ if len(data) > 0 {
+ h, _ := db.Ancient(freezerHashTable, number)
+ if common.BytesToHash(h) == hash {
+ return data
+ }
+ }
+ return nil // Can't find the data anywhere.
+}
+
+// ReadCanonicalBodyRLP retrieves the block body (transactions and uncles) for the canonical
+// block at number, in RLP encoding.
+func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
+ // If it's an ancient one, we don't need the canonical hash
data, _ := db.Ancient(freezerBodiesTable, number)
if len(data) == 0 {
- data, _ = db.Get(blockBodyKey(number, hash))
+ // Need to get the hash
+ data, _ = db.Get(blockBodyKey(number, ReadCanonicalHash(db, number)))
// In the background freezer is moving data from leveldb to flatten files.
// So during the first check for ancient db, the data is not yet in there,
// but when we reach into leveldb, the data was already moved. That would
@@ -315,18 +455,33 @@ func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
// 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 {
+ // First try to look up the data in ancient database. Extra hash
+ // comparison is necessary since ancient database only maintains
+ // the canonical data.
data, _ := db.Ancient(freezerDifficultyTable, number)
- if len(data) == 0 {
- data, _ = db.Get(headerTDKey(number, hash))
- // In the background freezer is moving data from leveldb to flatten files.
- // So during the first check for ancient db, the data is not yet in there,
- // but when we reach into leveldb, the data was already moved. That would
- // result in a not found error.
- if len(data) == 0 {
- data, _ = db.Ancient(freezerDifficultyTable, number)
+ if len(data) > 0 {
+ h, _ := db.Ancient(freezerHashTable, number)
+ if common.BytesToHash(h) == hash {
+ return data
}
}
- return data
+ // Then try to look up the data in leveldb.
+ data, _ = db.Get(headerTDKey(number, hash))
+ if len(data) > 0 {
+ return data
+ }
+ // In the background freezer is moving data from leveldb to flatten files.
+ // So during the first check for ancient db, the data is not yet in there,
+ // but when we reach into leveldb, the data was already moved. That would
+ // result in a not found error.
+ data, _ = db.Ancient(freezerDifficultyTable, number)
+ if len(data) > 0 {
+ h, _ := db.Ancient(freezerHashTable, number)
+ if common.BytesToHash(h) == hash {
+ return data
+ }
+ }
+ return nil // Can't find the data anywhere.
}
// ReadTd retrieves a block's total difficulty corresponding to the hash.
@@ -375,18 +530,33 @@ func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
// 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 {
+ // First try to look up the data in ancient database. Extra hash
+ // comparison is necessary since ancient database only maintains
+ // the canonical data.
data, _ := db.Ancient(freezerReceiptTable, number)
- if len(data) == 0 {
- data, _ = db.Get(blockReceiptsKey(number, hash))
- // In the background freezer is moving data from leveldb to flatten files.
- // So during the first check for ancient db, the data is not yet in there,
- // but when we reach into leveldb, the data was already moved. That would
- // result in a not found error.
- if len(data) == 0 {
- data, _ = db.Ancient(freezerReceiptTable, number)
+ if len(data) > 0 {
+ h, _ := db.Ancient(freezerHashTable, number)
+ if common.BytesToHash(h) == hash {
+ return data
}
}
- return data
+ // Then try to look up the data in leveldb.
+ data, _ = db.Get(blockReceiptsKey(number, hash))
+ if len(data) > 0 {
+ return data
+ }
+ // In the background freezer is moving data from leveldb to flatten files.
+ // So during the first check for ancient db, the data is not yet in there,
+ // but when we reach into leveldb, the data was already moved. That would
+ // result in a not found error.
+ data, _ = db.Ancient(freezerReceiptTable, number)
+ if len(data) > 0 {
+ h, _ := db.Ancient(freezerHashTable, number)
+ if common.BytesToHash(h) == hash {
+ return data
+ }
+ }
+ return nil // Can't find the data anywhere.
}
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
diff --git a/core/state/journal.go b/core/state/journal.go
index 6e85173..cfa1a4a 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -90,7 +90,8 @@ type (
account *common.Address
}
resetObjectChange struct {
- prev *stateObject
+ prev *stateObject
+ prevdestruct bool
}
suicideChange struct {
account *common.Address
@@ -130,9 +131,7 @@ type (
hash common.Hash
}
touchChange struct {
- account *common.Address
- prev bool
- prevDirty bool
+ account *common.Address
}
)
@@ -147,6 +146,9 @@ func (ch createObjectChange) dirtied() *common.Address {
func (ch resetObjectChange) revert(s *StateDB) {
s.setStateObject(ch.prev)
+ if !ch.prevdestruct && s.snap != nil {
+ delete(s.snapDestructs, ch.prev.addrHash)
+ }
}
func (ch resetObjectChange) dirtied() *common.Address {
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 9c47dc4..2893f80 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -23,10 +23,10 @@ import (
"math/big"
"time"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/crypto"
- "github.com/ava-labs/go-ethereum/metrics"
- "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/rlp"
)
var emptyCodeHash = crypto.Keccak256(nil)
@@ -79,9 +79,10 @@ type stateObject struct {
trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded
- originStorage Storage // Storage cache of original entries to dedup rewrites
- dirtyStorage Storage // Storage entries that need to be flushed to disk
- fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.
+ originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
+ pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
+ dirtyStorage Storage // Storage entries that have been modified in the current transaction execution
+ fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.
// Cache flags.
// When an object is marked suicided it will be delete from the trie
@@ -114,13 +115,17 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
if data.CodeHash == nil {
data.CodeHash = emptyCodeHash
}
+ if data.Root == (common.Hash{}) {
+ data.Root = emptyRoot
+ }
return &stateObject{
- db: db,
- address: address,
- addrHash: crypto.Keccak256Hash(address[:]),
- data: data,
- originStorage: make(Storage),
- dirtyStorage: make(Storage),
+ db: db,
+ address: address,
+ addrHash: crypto.Keccak256Hash(address[:]),
+ data: data,
+ originStorage: make(Storage),
+ pendingStorage: make(Storage),
+ dirtyStorage: make(Storage),
}
}
@@ -184,21 +189,44 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
if s.fakeStorage != nil {
return s.fakeStorage[key]
}
- // If we have the original value cached, return that
- value, cached := s.originStorage[key]
- if cached {
+ // If we have a pending write or clean cached, return that
+ if value, pending := s.pendingStorage[key]; pending {
return value
}
- // Track the amount of time wasted on reading the storage trie
- if metrics.EnabledExpensive {
- defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now())
+ if value, cached := s.originStorage[key]; cached {
+ return value
}
- // Otherwise load the value from the database
- enc, err := s.getTrie(db).TryGet(key[:])
- if err != nil {
- s.setError(err)
- return common.Hash{}
+ // If no live objects are available, attempt to use snapshots
+ var (
+ enc []byte
+ err error
+ )
+ if s.db.snap != nil {
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.db.SnapshotStorageReads += time.Since(start) }(time.Now())
+ }
+ // If the object was destructed in *this* block (and potentially resurrected),
+ // the storage has been cleared out, and we should *not* consult the previous
+ // snapshot about any storage values. The only possible alternatives are:
+ // 1) resurrect happened, and new slot values were set -- those should
+ // have been handles via pendingStorage above.
+ // 2) we don't have new values, and can deliver empty response back
+ if _, destructed := s.db.snapDestructs[s.addrHash]; destructed {
+ return common.Hash{}
+ }
+ enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes()))
+ }
+ // If snapshot unavailable or reading from it failed, load from the database
+ if s.db.snap == nil || err != nil {
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now())
+ }
+ if enc, err = s.getTrie(db).TryGet(key.Bytes()); err != nil {
+ s.setError(err)
+ return common.Hash{}
+ }
}
+ var value common.Hash
if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
@@ -253,38 +281,73 @@ func (s *stateObject) setState(key, value common.Hash) {
s.dirtyStorage[key] = value
}
+// finalise moves all dirty storage slots into the pending area to be hashed or
+// committed later. It is invoked at the end of every transaction.
+func (s *stateObject) finalise() {
+ for key, value := range s.dirtyStorage {
+ s.pendingStorage[key] = value
+ }
+ if len(s.dirtyStorage) > 0 {
+ s.dirtyStorage = make(Storage)
+ }
+}
+
// updateTrie writes cached storage modifications into the object's storage trie.
+// It will return nil if the trie has not been loaded and no changes have been made
func (s *stateObject) updateTrie(db Database) Trie {
+ // Make sure all dirty slots are finalized into the pending storage area
+ s.finalise()
+ if len(s.pendingStorage) == 0 {
+ return s.trie
+ }
// Track the amount of time wasted on updating the storge trie
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now())
}
- // Update all the dirty slots in the trie
+ // Retrieve the snapshot storage map for the object
+ var storage map[common.Hash][]byte
+ if s.db.snap != nil {
+ // Retrieve the old storage map, if available, create a new one otherwise
+ storage = s.db.snapStorage[s.addrHash]
+ if storage == nil {
+ storage = make(map[common.Hash][]byte)
+ s.db.snapStorage[s.addrHash] = storage
+ }
+ }
+ // Insert all the pending updates into the trie
tr := s.getTrie(db)
- for key, value := range s.dirtyStorage {
- delete(s.dirtyStorage, key)
-
+ for key, value := range s.pendingStorage {
// Skip noop changes, persist actual changes
if value == s.originStorage[key] {
continue
}
s.originStorage[key] = value
+ var v []byte
if (value == common.Hash{}) {
s.setError(tr.TryDelete(key[:]))
- continue
+ } else {
+ // Encoding []byte cannot fail, ok to ignore the error.
+ v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
+ s.setError(tr.TryUpdate(key[:], v))
+ }
+ // If state snapshotting is active, cac