diff --git a/core/blockchain.go b/core/blockchain.go index 6f1db96463..f6443c92b3 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1281,6 +1281,11 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { rawdb.WriteHeadFastBlockHash(batch, block.Hash()) rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64()) rawdb.WriteTxLookupEntriesByBlock(batch, block) + + signer := types.MakeSigner(bc.chainConfig, block.Number(), block.Time()) + // Write sender+nonce index + rawdb.WriteTxSenderNonceEntryByBlock(batch, block, signer) + rawdb.WriteHeadBlockHash(batch, block.Hash()) // Flush the whole batch into the disk, exit the node if failed diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index 8c8c3ec9bb..fb2c3fa307 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -134,6 +134,34 @@ func DeleteAllTxLookupEntries(db ethdb.KeyValueStore, condition func(common.Hash } } +// WriteTxSenderNonceIndex stores the mapping of sender+nonce to tx hash. +func WriteTxSenderNonceEntry(db ethdb.KeyValueWriter, sender common.Address, nonce uint64, hash common.Hash) { + if err := db.Put(txSenderNonceKey(sender, nonce), hash.Bytes()); err != nil { + log.Crit("Failed to store sender nonce index", "err", err) + } +} + +// WriteTxSenderNonceIndexByBlock stores a sender+nonce to transaction hash mapping +// for every transaction in a block, enabling sender and nonce based transaction lookups. +func WriteTxSenderNonceEntryByBlock(db ethdb.KeyValueWriter, block *types.Block, signer types.Signer) { + for _, tx := range block.Transactions() { + if sender, err := types.Sender(signer, tx); err == nil { + WriteTxSenderNonceEntry(db, sender, tx.Nonce(), tx.Hash()) + } + } + +} + +// ReadTxSenderNonceIndex retrieves the hash for a specific sender and nonce. +func ReadTxSenderNonceEntry(db ethdb.KeyValueReader, sender common.Address, nonce uint64) *common.Hash { + data, _ := db.Get(txSenderNonceKey(sender, nonce)) + if len(data) == 0 { + return nil + } + hash := common.BytesToHash(data) + return &hash +} + // findTxInBlockBody traverses the given RLP-encoded block body, searching for // the transaction specified by its hash. func findTxInBlockBody(blockbody rlp.RawValue, target common.Hash) (*types.Transaction, uint64, error) { diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index d9140c5fd6..e7b01eba3e 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -114,6 +114,7 @@ var ( blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata + txSenderNoncePrefix = []byte("x") // txSenderNoncePrefix + sender + nonce -> transaction hash bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value @@ -219,6 +220,15 @@ func txLookupKey(hash common.Hash) []byte { return append(txLookupPrefix, hash.Bytes()...) } +// txSenderNonceKey = txSenderNoncePrefix + sender + nonce -> transaction hash +func txSenderNonceKey(sender common.Address, nonce uint64) []byte { + buf := make([]byte, len(txSenderNoncePrefix)+common.AddressLength+8) + n := copy(buf, txSenderNoncePrefix) + n += copy(buf[n:], sender.Bytes()) + binary.BigEndian.PutUint64(buf[n:], nonce) + return buf +} + // accountSnapshotKey = SnapshotAccountPrefix + hash func accountSnapshotKey(hash common.Hash) []byte { return append(SnapshotAccountPrefix, hash.Bytes()...)