diff --git a/cmd/rpcdaemon/rpcdaemontest/test_util.go b/cmd/rpcdaemon/rpcdaemontest/test_util.go index b1150762426815fa4cd82bcf721be3b0c726c044..4df38906345921b8d6c5cd1b3ebb302fa4958493 100644 --- a/cmd/rpcdaemon/rpcdaemontest/test_util.go +++ b/cmd/rpcdaemon/rpcdaemontest/test_util.go @@ -2,7 +2,9 @@ package rpcdaemontest import ( "context" + "crypto/ecdsa" "encoding/binary" + "github.com/ledgerwatch/erigon/consensus" "math/big" "net" "testing" @@ -33,8 +35,16 @@ func CreateTestKV(t *testing.T) kv.RwDB { return s.DB } -func CreateTestSentry(t *testing.T) (*stages.MockSentry, *core.ChainPack, []*core.ChainPack) { - // Configure and generate a sample block chain +type testAddresses struct { + key *ecdsa.PrivateKey + key1 *ecdsa.PrivateKey + key2 *ecdsa.PrivateKey + address common.Address + address1 common.Address + address2 common.Address +} + +func makeTestAddresses() testAddresses { var ( key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") key1, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") @@ -42,8 +52,29 @@ func CreateTestSentry(t *testing.T) (*stages.MockSentry, *core.ChainPack, []*cor address = crypto.PubkeyToAddress(key.PublicKey) address1 = crypto.PubkeyToAddress(key1.PublicKey) address2 = crypto.PubkeyToAddress(key2.PublicKey) - theAddr = common.Address{1} - gspec = &core.Genesis{ + ) + + return testAddresses{ + key: key, + key1: key1, + key2: key2, + address: address, + address1: address1, + address2: address2, + } +} + +func CreateTestSentry(t *testing.T) (*stages.MockSentry, *core.ChainPack, []*core.ChainPack) { + addresses := makeTestAddresses() + var ( + key = addresses.key + address = addresses.address + address1 = addresses.address1 + address2 = addresses.address2 + ) + + var ( + gspec = &core.Genesis{ Config: params.AllEthashProtocolChanges, Alloc: core.GenesisAlloc{ address: {Balance: big.NewInt(9000000000000000000)}, @@ -52,30 +83,80 @@ func CreateTestSentry(t *testing.T) (*stages.MockSentry, *core.ChainPack, []*cor }, GasLimit: 10000000, } - chainId = big.NewInt(1337) - // this code generates a log - signer = types.LatestSignerForChainID(nil) ) m := stages.MockWithGenesis(t, gspec, key) contractBackend := backends.NewSimulatedBackendWithConfig(gspec.Alloc, gspec.Config, gspec.GasLimit) defer contractBackend.Close() - transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, chainId) - transactOpts1, _ := bind.NewKeyedTransactorWithChainID(key1, chainId) - transactOpts2, _ := bind.NewKeyedTransactorWithChainID(key2, chainId) - var poly *contracts.Poly - - var err error - var tokenContract *contracts.Token // Generate empty chain to have some orphaned blocks for tests orphanedChain, err := core.GenerateChain(m.ChainConfig, m.Genesis, m.Engine, m.DB, 5, func(i int, block *core.BlockGen) { }, true) if err != nil { t.Fatal(err) } - // We generate the blocks without plainstant because it's not supported in core.GenerateChain - chain, err := core.GenerateChain(m.ChainConfig, m.Genesis, m.Engine, m.DB, 10, func(i int, block *core.BlockGen) { + + chain, err := getChainInstance(&addresses, m.ChainConfig, m.Genesis, m.Engine, m.DB, contractBackend) + if err != nil { + t.Fatal(err) + } + + if err = m.InsertChain(orphanedChain); err != nil { + t.Fatal(err) + } + if err = m.InsertChain(chain); err != nil { + t.Fatal(err) + } + + return m, chain, []*core.ChainPack{orphanedChain} +} + +var chainInstance *core.ChainPack + +func getChainInstance( + addresses *testAddresses, + config *params.ChainConfig, + parent *types.Block, + engine consensus.Engine, + db kv.RwDB, + contractBackend *backends.SimulatedBackend, +) (*core.ChainPack, error) { + var err error + if chainInstance == nil { + chainInstance, err = generateChain(addresses, config, parent, engine, db, contractBackend) + } + return chainInstance.Copy(), err +} + +func generateChain( + addresses *testAddresses, + config *params.ChainConfig, + parent *types.Block, + engine consensus.Engine, + db kv.RwDB, + contractBackend *backends.SimulatedBackend, +) (*core.ChainPack, error) { + var ( + key = addresses.key + key1 = addresses.key1 + key2 = addresses.key2 + address = addresses.address + address1 = addresses.address1 + address2 = addresses.address2 + theAddr = common.Address{1} + chainId = big.NewInt(1337) + // this code generates a log + signer = types.LatestSignerForChainID(nil) + ) + + transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, chainId) + transactOpts1, _ := bind.NewKeyedTransactorWithChainID(key1, chainId) + transactOpts2, _ := bind.NewKeyedTransactorWithChainID(key2, chainId) + var poly *contracts.Poly + var tokenContract *contracts.Token + + // We generate the blocks without plain state because it's not supported in core.GenerateChain + return core.GenerateChain(config, parent, engine, db, 10, func(i int, block *core.BlockGen) { var ( txn types.Transaction txs []types.Transaction @@ -193,18 +274,6 @@ func CreateTestSentry(t *testing.T) (*stages.MockSentry, *core.ChainPack, []*cor } contractBackend.Commit() }, true) - if err != nil { - t.Fatal(err) - } - - if err = m.InsertChain(orphanedChain); err != nil { - t.Fatal(err) - } - if err = m.InsertChain(chain); err != nil { - t.Fatal(err) - } - - return m, chain, []*core.ChainPack{orphanedChain} } type IsMiningMock struct{} diff --git a/core/chain_makers.go b/core/chain_makers.go index 94473dadb8bac021d4a866d430f6fe2b118cc720..24a8e4266df02ccc35f9ec56c88af5032b4ac899 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -238,6 +238,38 @@ func (cp ChainPack) Slice(i, j int) *ChainPack { } } +// Copy creates a deep copy of the ChainPack. +func (cp *ChainPack) Copy() *ChainPack { + headers := make([]*types.Header, 0, len(cp.Headers)) + for _, header := range cp.Headers { + headers = append(headers, types.CopyHeader(header)) + } + + blocks := make([]*types.Block, 0, len(cp.Blocks)) + for _, block := range cp.Blocks { + blocks = append(blocks, block.Copy()) + } + + receipts := make([]types.Receipts, 0, len(cp.Receipts)) + for _, receiptList := range cp.Receipts { + receiptListCopy := make(types.Receipts, 0, len(receiptList)) + for _, receipt := range receiptList { + receiptListCopy = append(receiptListCopy, receipt.Copy()) + } + receipts = append(receipts, receiptListCopy) + } + + topBlock := cp.TopBlock.Copy() + + return &ChainPack{ + Length: cp.Length, + Headers: headers, + Blocks: blocks, + Receipts: receipts, + TopBlock: topBlock, + } +} + // GenerateChain creates a chain of n blocks. The first block's // parent will be the provided parent. db is used to store // intermediate states and should contain the parent's state trie. diff --git a/core/types/block.go b/core/types/block.go index c89910e0a92b986f9f8647fa05fe0d697910bcb7..ae83134f35fdcd25efb05c3352fe4d49549f11b4 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -1280,6 +1280,52 @@ func CalcUncleHash(uncles []*Header) common.Hash { return rlpHash(uncles) } +// Copy creates a deep copy of the Block. +func (b *Block) Copy() *Block { + uncles := make([]*Header, 0, len(b.uncles)) + for _, uncle := range b.uncles { + uncles = append(uncles, CopyHeader(uncle)) + } + + transactionsData, err := MarshalTransactionsBinary(b.transactions) + if err != nil { + panic(fmt.Errorf("MarshalTransactionsBinary failed: %w", err)) + } + transactions, err := DecodeTransactions(transactionsData) + if err != nil { + panic(fmt.Errorf("DecodeTransactions failed: %w", err)) + } + + var hashValue atomic.Value + if value := b.hash.Load(); value != nil { + hash := value.(common.Hash) + hashCopy := common.BytesToHash(hash.Bytes()) + hashValue.Store(hashCopy) + } + + var sizeValue atomic.Value + if size := b.size.Load(); size != nil { + sizeValue.Store(size) + } + + td := big.NewInt(0).Set(b.td) + + if b.ReceivedFrom != nil { + panic("ReceivedFrom deep copy is not supported") + } + + return &Block{ + header: CopyHeader(b.header), + uncles: uncles, + transactions: transactions, + hash: hashValue, + size: sizeValue, + td: td, + ReceivedAt: b.ReceivedAt, + ReceivedFrom: nil, + } +} + // WithSeal returns a new block with the data from b but the header replaced with // the sealed one. func (b *Block) WithSeal(header *Header) *Block { diff --git a/core/types/log.go b/core/types/log.go index 21c2f2725e4a51b10a06f9c53d84282f343e85ad..29ea8dafc46fe836c4ee089155239c2e885f3fdd 100644 --- a/core/types/log.go +++ b/core/types/log.go @@ -100,6 +100,30 @@ func (l *Log) DecodeRLP(s *rlp.Stream) error { return err } +// Copy creates a deep copy of the Log. +func (l *Log) Copy() *Log { + topics := make([]common.Hash, 0, len(l.Topics)) + for _, topic := range l.Topics { + topicCopy := common.BytesToHash(topic.Bytes()) + topics = append(topics, topicCopy) + } + + data := make([]byte, len(l.Data)) + copy(data, l.Data) + + return &Log{ + Address: common.BytesToAddress(l.Address.Bytes()), + Topics: topics, + Data: data, + BlockNumber: l.BlockNumber, + TxHash: common.BytesToHash(l.TxHash.Bytes()), + TxIndex: l.TxIndex, + BlockHash: common.BytesToHash(l.BlockHash.Bytes()), + Index: l.Index, + Removed: l.Removed, + } +} + // LogForStorage is a wrapper around a Log that flattens and parses the entire content of // a log including non-consensus fields. type LogForStorage Log diff --git a/core/types/receipt.go b/core/types/receipt.go index 55451350503af2cf0c964fc5a930f7de2844f10b..14fb4d73d22a357456f75926daf64471ca2d7db7 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -297,6 +297,39 @@ func (r *Receipt) Size() common.StorageSize { return size } +// Copy creates a deep copy of the Receipt. +func (r *Receipt) Copy() *Receipt { + postState := make([]byte, len(r.PostState)) + copy(postState, r.PostState) + + bloom := BytesToBloom(r.Bloom.Bytes()) + + logs := make(Logs, 0, len(r.Logs)) + for _, log := range r.Logs { + logs = append(logs, log.Copy()) + } + + txHash := common.BytesToHash(r.TxHash.Bytes()) + contractAddress := common.BytesToAddress(r.ContractAddress.Bytes()) + blockHash := common.BytesToHash(r.BlockHash.Bytes()) + blockNumber := big.NewInt(0).Set(r.BlockNumber) + + return &Receipt{ + Type: r.Type, + PostState: postState, + Status: r.Status, + CumulativeGasUsed: r.CumulativeGasUsed, + Bloom: bloom, + Logs: logs, + TxHash: txHash, + ContractAddress: contractAddress, + GasUsed: r.GasUsed, + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: r.TransactionIndex, + } +} + type ReceiptsForStorage []*ReceiptForStorage // ReceiptForStorage is a wrapper around a Receipt that flattens and parses the