From bf13c3e66baf61512d6e95edfc14f526b8c06be1 Mon Sep 17 00:00:00 2001 From: Kien Dang Date: Mon, 3 Apr 2023 10:59:43 +0700 Subject: [PATCH] fix: missing reorged blocks (#250) * fix: add `added blocks` to ChainEvent if reorg happened * chore: fix unit test failed * chore: move read/write store internal transaction flag to `accessors_chain` * chore: add `shouldStoreInternalTxs` into blockchain and use it to check if internal txs should be stored into db or not * chore: backup dirty accounts to db when blockchain is stop * chore: change struct of storing latest dirty accounts to make sure canonical or sidechain blockhash can be found --- cmd/ronin/config.go | 4 + cmd/ronin/main.go | 1 + cmd/ronin/usage.go | 1 + cmd/utils/flags.go | 4 + core/blockchain.go | 108 +++++++++++++++------ core/blockchain_reader.go | 92 ++++++++++++------ core/rawdb/accessors_chain.go | 67 +++++++++++++ core/rawdb/accessors_chain_test.go | 150 +++++++++++++++++++++++++++++ core/rawdb/schema.go | 11 +++ core/types/state_account.go | 5 + core/types/transaction.go | 13 ++- core/vm/evm_test.go | 30 +++--- 12 files changed, 408 insertions(+), 78 deletions(-) diff --git a/cmd/ronin/config.go b/cmd/ronin/config.go index ad899ab434..9105fee103 100644 --- a/cmd/ronin/config.go +++ b/cmd/ronin/config.go @@ -20,6 +20,7 @@ import ( "bufio" "errors" "fmt" + "github.com/ethereum/go-ethereum/core/rawdb" "math/big" "os" "reflect" @@ -161,6 +162,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { } backend, eth := utils.RegisterEthService(stack, &cfg.Eth) + // store internal transaction is enabled flag + rawdb.WriteStoreInternalTransactionsEnabled(backend.ChainDb(), ctx.GlobalBool(utils.StoreInternalTransactions.Name)) + // Configure catalyst. if ctx.GlobalBool(utils.CatalystFlag.Name) { if eth == nil { diff --git a/cmd/ronin/main.go b/cmd/ronin/main.go index d38928fd4e..ec4a20ee8e 100644 --- a/cmd/ronin/main.go +++ b/cmd/ronin/main.go @@ -164,6 +164,7 @@ var ( configFileFlag, utils.CatalystFlag, utils.MonitorDoubleSign, + utils.StoreInternalTransactions, } rpcFlags = []cli.Flag{ diff --git a/cmd/ronin/usage.go b/cmd/ronin/usage.go index 94b61bd1c8..149fb278b3 100644 --- a/cmd/ronin/usage.go +++ b/cmd/ronin/usage.go @@ -56,6 +56,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.WhitelistFlag, utils.ForceOverrideChainConfigFlag, utils.MonitorDoubleSign, + utils.StoreInternalTransactions, }, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index e3b5bab645..25c0607634 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -235,6 +235,10 @@ var ( Name: "monitor.doublesign", Usage: "Enable double sign monitoring", } + StoreInternalTransactions = cli.BoolFlag{ + Name: "internaltxs", + Usage: "Enable storing internal transactions to db", + } LightKDFFlag = cli.BoolFlag{ Name: "lightkdf", Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", diff --git a/core/blockchain.go b/core/blockchain.go index 35c0e3380e..2ac8d7542d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -85,13 +85,15 @@ var ( ) const ( - bodyCacheLimit = 256 - blockCacheLimit = 256 - receiptsCacheLimit = 32 - txLookupCacheLimit = 1024 - maxFutureBlocks = 256 - maxTimeFutureBlocks = 30 - TriesInMemory = 128 + bodyCacheLimit = 256 + blockCacheLimit = 256 + receiptsCacheLimit = 32 + txLookupCacheLimit = 1024 + maxFutureBlocks = 256 + maxTimeFutureBlocks = 30 + TriesInMemory = 128 + dirtyAccountsCacheLimit = 32 + internalTxsCacheLimit = 32 // BlockChainVersion ensures that an incompatible database forces a resync from scratch. // @@ -196,13 +198,15 @@ type BlockChain struct { currentBlock atomic.Value // Current head of the block chain currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) - stateCache state.Database // State database to reuse between imports (contains state cache) - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - receiptsCache *lru.Cache // Cache for the most recent receipts per block - blockCache *lru.Cache // Cache for the most recent entire blocks - txLookupCache *lru.Cache // Cache for the most recent transaction lookup data. - futureBlocks *lru.Cache // future blocks are blocks added for later processing + stateCache state.Database // State database to reuse between imports (contains state cache) + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + receiptsCache *lru.Cache // Cache for the most recent receipts per block + blockCache *lru.Cache // Cache for the most recent entire blocks + txLookupCache *lru.Cache // Cache for the most recent transaction lookup data. + futureBlocks *lru.Cache // future blocks are blocks added for later processing + dirtyAccountsCache *lru.Cache // Cache for the most recent dirtyAccounts + internalTransactionsCache *lru.Cache // Cache for most recent internal transactions with block hash at key wg sync.WaitGroup // quit chan struct{} // shutdown signal, closed in Stop. @@ -215,7 +219,8 @@ type BlockChain struct { processor Processor // Block transaction processor interface vmConfig vm.Config - shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. + shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. + shouldStoreInternalTxs bool } // NewBlockChain returns a fully initialised block chain using information @@ -231,6 +236,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par blockCache, _ := lru.New(blockCacheLimit) txLookupCache, _ := lru.New(txLookupCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) + dirtyAccountsCache, _ := lru.New(dirtyAccountsCacheLimit) + internalTxsCache, _ := lru.New(internalTxsCacheLimit) bc := &BlockChain{ chainConfig: chainConfig, @@ -242,17 +249,20 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par Journal: cacheConfig.TrieCleanJournal, Preimages: cacheConfig.Preimages, }), - quit: make(chan struct{}), - chainmu: syncx.NewClosableMutex(), - shouldPreserve: shouldPreserve, - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - receiptsCache: receiptsCache, - blockCache: blockCache, - txLookupCache: txLookupCache, - futureBlocks: futureBlocks, - engine: engine, - vmConfig: vmConfig, + quit: make(chan struct{}), + chainmu: syncx.NewClosableMutex(), + shouldPreserve: shouldPreserve, + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + receiptsCache: receiptsCache, + blockCache: blockCache, + txLookupCache: txLookupCache, + dirtyAccountsCache: dirtyAccountsCache, + internalTransactionsCache: internalTxsCache, + futureBlocks: futureBlocks, + engine: engine, + vmConfig: vmConfig, + shouldStoreInternalTxs: rawdb.ReadStoreInternalTransactionsEnabled(db), } bc.validator = NewBlockValidator(chainConfig, bc, engine) bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine) @@ -406,9 +416,20 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par triedb.SaveCachePeriodically(bc.cacheConfig.TrieCleanJournal, bc.cacheConfig.TrieCleanRejournal, bc.quit) }() } + + // load the latest dirty accounts stored from last stop to cache + bc.loadLatestDirtyAccounts() + return bc, nil } +func (bc *BlockChain) loadLatestDirtyAccounts() { + dirtyStateAccounts := rawdb.ReadDirtyAccounts(bc.db) + for _, data := range dirtyStateAccounts { + bc.dirtyAccountsCache.Add(data.BlockHash, data.DirtyAccounts) + } +} + func (bc *BlockChain) StartDoubleSignMonitor() { log.Info("Starting double sign monitor") doubleSignMonitor, err := monitor.NewDoubleSignMonitor() @@ -821,6 +842,19 @@ func (bc *BlockChain) Stop() { // Unsubscribe all subscriptions registered from blockchain. bc.scope.Close() + // store cached dirty accounts to db + dirtyStateAccounts := make([]*types.DirtyStateAccountsAndBlock, 0) + for _, blockHash := range bc.dirtyAccountsCache.Keys() { + dirtyAccounts, _ := bc.dirtyAccountsCache.Get(blockHash) + dirtyStateAccounts = append(dirtyStateAccounts, &types.DirtyStateAccountsAndBlock{ + BlockHash: blockHash.(common.Hash), + DirtyAccounts: dirtyAccounts.([]*types.DirtyStateAccount), + }) + } + if len(dirtyStateAccounts) > 0 { + rawdb.WriteDirtyAccounts(bc.db, dirtyStateAccounts) + } + // Ensure that the entirety of the state snapshot is journalled to disk. var snapBase common.Hash if bc.snaps != nil { @@ -1448,7 +1482,7 @@ func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (in return 0, errChainStopped } defer bc.chainmu.Unlock() - return bc.insertChain(types.Blocks([]*types.Block{block}), false) + return bc.insertChain([]*types.Block{block}, false) } // insertChain is the internal implementation of InsertChain, which assumes that @@ -1669,8 +1703,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er return it.index, err } - // send internalTxs events + // store internal txs to db and send them to internalTxFeed if len(internalTxs) > 0 { + bc.WriteInternalTransactions(block.Hash(), internalTxs) bc.internalTxFeed.Send(internalTxs) } @@ -1712,6 +1747,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er if dirtyAccounts != nil { bc.dirtyAccountFeed.Send(dirtyAccounts) + bc.dirtyAccountsCache.Add(block.Hash(), dirtyAccounts) } // Update the metrics touched during block commit @@ -1915,10 +1951,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { // collectLogs collects the logs that were generated or removed during // the processing of the block that corresponds with the given hash. // These logs are later announced as deleted or reborn - collectLogs = func(hash common.Hash, removed bool) { + collectLogs = func(hash common.Hash, removed bool) (types.Receipts, []*types.Log) { number := bc.hc.GetBlockNumber(hash) if number == nil { - return + return nil, nil } receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig) @@ -1939,6 +1975,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { rebirthLogs = append(rebirthLogs, logs) } } + return receipts, logs } // mergeLogs returns a merged log slice with specified sort order. mergeLogs = func(logs [][]*types.Log, reverse bool) []*types.Log { @@ -2023,7 +2060,16 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { bc.writeHeadBlock(newChain[i]) // Collect reborn logs due to chain reorg - collectLogs(newChain[i].Hash(), false) + receipts, logs := collectLogs(newChain[i].Hash(), false) + + // get dirty accounts + dirtyAccounts := bc.ReadDirtyAccounts(newChain[i].Hash()) + + // get internal transactions + internalTxs := bc.ReadInternalTransactions(newChain[i].Hash()) + + log.Info("send new block event due to reorg", "height", newChain[i].NumberU64(), "txs", len(newChain[i].Transactions()), "logs", len(logs)) + bc.chainFeed.Send(ChainEvent{Block: newChain[i], Hash: newChain[i].Hash(), Logs: logs, InternalTxs: internalTxs, DirtyAccounts: dirtyAccounts, Receipts: receipts}) // Collect the new added transactions. addedTxs = append(addedTxs, newChain[i].Transactions()...) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 0830300d24..e4558345f6 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -400,6 +400,38 @@ func (bc *BlockChain) SubscribeDirtyAccountEvent(ch chan<- []*types.DirtyStateAc return bc.scope.Track(bc.dirtyAccountFeed.Subscribe(ch)) } +func (bc *BlockChain) WriteInternalTransactions(hash common.Hash, internalTxs []*types.InternalTransaction) { + // cache first + bc.internalTransactionsCache.Add(hash, internalTxs) + + // check if store internal transactions is enabled or not + if !bc.shouldStoreInternalTxs { + return + } + rawdb.WriteInternalTransactions(bc.db, hash, internalTxs) +} + +func (bc *BlockChain) ReadInternalTransactions(hash common.Hash) []*types.InternalTransaction { + // get internal txs from cache + if internalTxs, exist := bc.internalTransactionsCache.Get(hash); exist { + return internalTxs.([]*types.InternalTransaction) + } + // otherwise get from db + internalTxs := rawdb.ReadInternalTransactions(bc.db, hash) + if internalTxs == nil { + return nil + } + bc.internalTransactionsCache.Add(hash, internalTxs) + return internalTxs +} + +func (bc *BlockChain) ReadDirtyAccounts(hash common.Hash) []*types.DirtyStateAccount { + if dirtyAccount, _ := bc.dirtyAccountsCache.Get(hash); dirtyAccount != nil { + return dirtyAccount.([]*types.DirtyStateAccount) + } + return nil +} + func (bc *BlockChain) OpEvents() []*vm.PublishEvent { return []*vm.PublishEvent{ { @@ -427,20 +459,22 @@ func (tx *InternalTransferOrSmcCallEvent) Publish( err error, ) *types.InternalTransaction { internal := &types.InternalTransaction{ - Opcode: opcode.String(), - Order: order, - TransactionHash: hash, - Type: types.InternalTransactionContractCall, - Value: value, - Input: input, - Output: output, - From: from, - To: to, - Success: err == nil, - Error: "", - Height: blockHeight, - BlockHash: blockHash, - BlockTime: blockTime, + Opcode: opcode.String(), + Type: types.InternalTransactionContractCall, + Success: err == nil, + Error: "", + InternalTransactionBody: &types.InternalTransactionBody{ + Order: order, + TransactionHash: hash, + Value: value, + Input: input, + Output: output, + From: from, + To: to, + Height: blockHeight, + BlockHash: blockHash, + BlockTime: blockTime, + }, } if value != nil && value.Cmp(big.NewInt(0)) > 0 && (input == nil || len(input) == 0) { internal.Type = types.InternalTransactionTransfer @@ -465,20 +499,22 @@ func (tx *InternalTransactionContractCreation) Publish( err error, ) *types.InternalTransaction { internal := &types.InternalTransaction{ - Opcode: opcode.String(), - Order: order, - TransactionHash: hash, - Type: types.InternalTransactionContractCreation, - Value: value, - Input: input, - Output: output, - From: from, - To: to, - Success: err == nil, - Error: "", - Height: blockHeight, - BlockHash: blockHash, - BlockTime: blockTime, + Opcode: opcode.String(), + Type: types.InternalTransactionContractCreation, + Success: err == nil, + Error: "", + InternalTransactionBody: &types.InternalTransactionBody{ + Order: order, + TransactionHash: hash, + Value: value, + Input: input, + Output: output, + From: from, + To: to, + Height: blockHeight, + BlockHash: blockHash, + BlockTime: blockTime, + }, } if err != nil { internal.Error = err.Error() diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 4028191b76..6f8c0119bd 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -470,6 +470,31 @@ func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { } } +// ReadInternalTransactions retrieves the internal transactions corresponding to the hash. +func ReadInternalTransactions(db ethdb.Reader, hash common.Hash) []*types.InternalTransaction { + data, _ := db.Get(internalTxsKey(hash)) + if len(data) == 0 { + return nil + } + var internalTxs []*types.InternalTransaction + if err := rlp.Decode(bytes.NewReader(data), &internalTxs); err != nil { + log.Error("Invalid internal transactions RLP", "hash", hash, "err", err) + return nil + } + return internalTxs +} + +// WriteInternalTransactions stores internal transactions into the database. +func WriteInternalTransactions(db ethdb.KeyValueWriter, hash common.Hash, internalTxs []*types.InternalTransaction) { + data, err := rlp.EncodeToBytes(internalTxs) + if err != nil { + log.Crit("Failed to RLP encode internal txs", "err", err) + } + if err := db.Put(internalTxsKey(hash), data); err != nil { + log.Crit("Failed to store internal txs", "err", err) + } +} + // 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 { var data []byte @@ -951,3 +976,45 @@ func ReadHeadBlock(db ethdb.Reader) *types.Block { } return ReadBlock(db, headBlockHash, *headBlockNumber) } + +// ReadStoreInternalTransactionsEnabled retrieves if the store internal transactions option is enabled. +func ReadStoreInternalTransactionsEnabled(db ethdb.KeyValueReader) bool { + disabled, _ := db.Get(storeInternalTxsEnabledKey) + return len(disabled) > 0 && string(disabled) == "1" +} + +// WriteStoreInternalTransactionsEnabled stores the store internal transactions enabled flag. +func WriteStoreInternalTransactionsEnabled(db ethdb.KeyValueWriter, store bool) { + v := []byte("0") + if store { + v = []byte("1") + } + if err := db.Put(storeInternalTxsEnabledKey, v); err != nil { + log.Crit("Failed to store internal transactions enabled flag", "err", err) + } +} + +// WriteDirtyAccounts stores the dirty accounts to db. +func WriteDirtyAccounts(db ethdb.KeyValueWriter, dirtyStateAccounts []*types.DirtyStateAccountsAndBlock) { + data, err := rlp.EncodeToBytes(dirtyStateAccounts) + if err != nil { + log.Crit("Failed to encode dirty accounts", "err", err) + } + if err := db.Put(dirtyAccountsKey, data); err != nil { + log.Crit("Failed to write dirty accounts", "err", err) + } +} + +// ReadDirtyAccounts get dirty accounts from db +func ReadDirtyAccounts(db ethdb.KeyValueReader) []*types.DirtyStateAccountsAndBlock { + data, _ := db.Get(dirtyAccountsKey) + if len(data) == 0 { + return nil + } + var dirtyStateAccounts []*types.DirtyStateAccountsAndBlock + if err := rlp.Decode(bytes.NewReader(data), &dirtyStateAccounts); err != nil { + log.Error("Invalid dirty accounts RLP", "err", err) + return nil + } + return dirtyStateAccounts +} diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 50b0d53902..b7c00f94ef 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -26,6 +26,7 @@ import ( "os" "reflect" "testing" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -883,3 +884,152 @@ func BenchmarkDecodeRLPLogs(b *testing.B) { } }) } + +func TestReadWriteInternalTransactions(t *testing.T) { + db := NewMemoryDatabase() + blockTime := time.Now().Unix() + blockHash := common.HexToHash("0x3") + internalTxs := []*types.InternalTransaction{ + { + Opcode: "CALL", + Type: "call", + Success: true, + Error: "", + InternalTransactionBody: &types.InternalTransactionBody{ + Order: 1, + TransactionHash: common.HexToHash("0x4"), + Value: nil, + Input: nil, + Output: nil, + From: common.HexToAddress("0x1"), + To: common.HexToAddress("0x2"), + Height: 100, + BlockHash: blockHash, + BlockTime: uint64(blockTime), + }, + }, + { + Opcode: "CALL", + Type: "call", + Success: true, + Error: "", + InternalTransactionBody: &types.InternalTransactionBody{ + Order: 2, + TransactionHash: common.HexToHash("0x4"), + Value: nil, + Input: nil, + Output: nil, + From: common.HexToAddress("0x3"), + To: common.HexToAddress("0x4"), + Height: 100, + BlockHash: blockHash, + BlockTime: uint64(blockTime), + }, + }, + } + WriteInternalTransactions(db, blockHash, internalTxs) + results := ReadInternalTransactions(db, blockHash) + if results == nil { + t.Fatal("no internal transactions found at hash") + } + if len(results) != len(internalTxs) { + t.Fatalf("mismatched length between input and output internal transactions, expected %d got %d", len(internalTxs), len(results)) + } + for i, tx := range internalTxs { + if tx.Hash().Hex() != results[i].Hash().Hex() { + t.Fatalf("mismatched hash at index %d, expected %s got %s", i, tx.Hash().Hex(), results[i].Hash().Hex()) + } + if tx.Opcode != results[i].Opcode { + t.Fatalf("mismatched Opcode at index %d, expected %s got %s", i, tx.Opcode, results[i].Opcode) + } + } +} + +func TestReadWriteDirtyAccounts(t *testing.T) { + db := NewMemoryDatabase() + blockHash1, blockHash2 := common.HexToHash("0x3"), common.HexToHash("0x4") + dirtyAccounts := []*types.DirtyStateAccountsAndBlock{ + { + BlockHash: blockHash1, + DirtyAccounts: []*types.DirtyStateAccount{ + { + Address: common.HexToAddress("0x1"), + Nonce: 1, + Balance: big.NewInt(0), + Root: common.HexToHash("0x123"), + CodeHash: common.HexToHash("0x234"), + BlockNumber: 100, + BlockHash: blockHash1, + Deleted: false, + Suicided: false, + DirtyCode: false, + }, + { + Address: common.HexToAddress("0x2"), + Nonce: 2, + Balance: big.NewInt(0), + Root: common.HexToHash("0x123"), + CodeHash: common.HexToHash("0x234"), + BlockNumber: 100, + BlockHash: blockHash1, + Deleted: false, + Suicided: false, + DirtyCode: false, + }, + }, + }, + { + BlockHash: blockHash2, + DirtyAccounts: []*types.DirtyStateAccount{ + { + Address: common.HexToAddress("0x3"), + Nonce: 3, + Balance: big.NewInt(0), + Root: common.HexToHash("0x123"), + CodeHash: common.HexToHash("0x234"), + BlockNumber: 100, + BlockHash: blockHash2, + Deleted: false, + Suicided: false, + DirtyCode: false, + }, + { + Address: common.HexToAddress("0x4"), + Nonce: 4, + Balance: big.NewInt(0), + Root: common.HexToHash("0x123"), + CodeHash: common.HexToHash("0x234"), + BlockNumber: 100, + BlockHash: blockHash2, + Deleted: false, + Suicided: false, + DirtyCode: false, + }, + }, + }, + } + WriteDirtyAccounts(db, dirtyAccounts) + results := ReadDirtyAccounts(db) + if results == nil { + t.Fatal("no dirty accounts found at hash") + } + if len(results) != len(dirtyAccounts) { + t.Fatalf("mismatched length between input and output dirty accounts, expected %d got %d", len(dirtyAccounts), len(results)) + } + for i, accs := range dirtyAccounts { + if accs.BlockHash.Hex() != results[i].BlockHash.Hex() { + t.Fatalf("mismatched blockHash at index %d, expected %s got %s", i, accs.BlockHash.Hex(), results[i].BlockHash.Hex()) + } + if len(accs.DirtyAccounts) != len(results[i].DirtyAccounts) { + t.Fatalf("mismatched length between input and output dirty accounts at index %d, expected %d got %d", i, len(accs.DirtyAccounts), len(results[i].DirtyAccounts)) + } + for j, acc := range accs.DirtyAccounts { + if acc.Address.Hex() != results[i].DirtyAccounts[j].Address.Hex() { + t.Fatalf("mismatched address at index %d, expected %s got %s", i, acc.Address.Hex(), results[i].DirtyAccounts[j].Address.Hex()) + } + if acc.Nonce != results[i].DirtyAccounts[j].Nonce { + t.Fatalf("mismatched address at index %d, expected %d got %d", i, acc.Nonce, results[i].DirtyAccounts[j].Nonce) + } + } + } +} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index d432db2ab7..1ea77a5664 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -75,6 +75,9 @@ var ( // uncleanShutdownKey tracks the list of local crashes uncleanShutdownKey = []byte("unclean-shutdown") // config prefix for the db + // storeInternalTxsEnabledKey flags that internal transactions will be stored into db + storeInternalTxsEnabledKey = []byte("storeInternalTxsEnabled") + // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes). headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td @@ -90,6 +93,9 @@ var ( SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value CodePrefix = []byte("c") // CodePrefix + code hash -> account code + internalTxsPrefix = []byte("itxs") // internalTxsPrefix + block hash -> internal transactions + dirtyAccountsKey = []byte("dacc") // dirtyAccountsPrefix + block hash -> dirty accounts + PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage configPrefix = []byte("ethereum-config-") // config prefix for the db @@ -177,6 +183,11 @@ func blockReceiptsKey(number uint64, hash common.Hash) []byte { return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) } +// internalTxsKey = internalTxsPrefix + hash +func internalTxsKey(hash common.Hash) []byte { + return append(internalTxsPrefix, hash.Bytes()...) +} + // txLookupKey = txLookupPrefix + hash func txLookupKey(hash common.Hash) []byte { return append(txLookupPrefix, hash.Bytes()...) diff --git a/core/types/state_account.go b/core/types/state_account.go index ae2514920c..a80a048f16 100644 --- a/core/types/state_account.go +++ b/core/types/state_account.go @@ -43,3 +43,8 @@ type DirtyStateAccount struct { Suicided bool `json:"suicided"` DirtyCode bool `json:"dirtyCode"` } + +type DirtyStateAccountsAndBlock struct { + BlockHash common.Hash + DirtyAccounts []*DirtyStateAccount +} diff --git a/core/types/transaction.go b/core/types/transaction.go index f93bdcee59..474881d2bb 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -647,11 +647,14 @@ const ( ) type InternalTransaction struct { - Opcode string `rlp:"-"` - Type string `rlp:"-"` - Success bool `rlp:"-"` - Error string `rlp:"-"` + Opcode string + Type string + Success bool + Error string + *InternalTransactionBody +} +type InternalTransactionBody struct { Order uint64 TransactionHash common.Hash Value *big.Int @@ -667,5 +670,5 @@ type InternalTransaction struct { } func (internal *InternalTransaction) Hash() common.Hash { - return rlpHash(internal) + return rlpHash(internal.InternalTransactionBody) } diff --git a/core/vm/evm_test.go b/core/vm/evm_test.go index fb2995e784..26c505a4cf 100644 --- a/core/vm/evm_test.go +++ b/core/vm/evm_test.go @@ -23,20 +23,22 @@ func (tx *TestOpEvent) Publish( err error, ) *types.InternalTransaction { return &types.InternalTransaction{ - Opcode: opcode.String(), - Order: order, - TransactionHash: hash, - Type: "test", - Value: value, - Input: input, - Output: output, - From: from, - To: to, - Success: err == nil, - Error: "", - Height: blockHeight, - BlockHash: blockHash, - BlockTime: blockTime, + Opcode: opcode.String(), + Type: "test", + Success: err == nil, + Error: "", + InternalTransactionBody: &types.InternalTransactionBody{ + Order: order, + TransactionHash: hash, + Value: value, + Input: input, + Output: output, + From: from, + To: to, + Height: blockHeight, + BlockHash: blockHash, + BlockTime: blockTime, + }, } }