From 13d1e032f1e1fde7c81de04e2d752497b51194b9 Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Fri, 10 May 2024 17:24:34 +0800 Subject: [PATCH] handle withdrawals --- cmd/root.go | 2 +- cmd/serve.go | 1 + pkg/eth/api.go | 3 ++- pkg/eth/api_test/api_test.go | 4 ++-- pkg/eth/backend.go | 30 ++++++++++++++++++++++++++---- pkg/eth/retriever.go | 18 ++++++++++++++++++ pkg/eth/sql.go | 15 +++++++++++++++ pkg/eth/test_helpers/test_data.go | 14 +++++++++----- test/stack.yml | 4 ++-- 9 files changed, 76 insertions(+), 15 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 7c403f78..067a57ed 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -95,7 +95,7 @@ func init() { func initConfig() { if cfgFile == "" && envFile == "" { - log.Fatal("No configuration file specified, use --config , --env flag to provide configuration") + log.Warn("No configuration file specified, use --config , --env flag to provide configuration") } if cfgFile != "" { diff --git a/cmd/serve.go b/cmd/serve.go index a1fb8621..7d94cbf3 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -60,6 +60,7 @@ func serve() { logWithCommand.Fatal(err) } logWithCommand.Debugf("server config: %+v", serverConfig) + logWithCommand.Debugf("chain config: %+v", serverConfig.ChainConfig) server, err := s.NewServer(serverConfig) if err != nil { logWithCommand.Fatal(err) diff --git a/pkg/eth/api.go b/pkg/eth/api.go index d21e364a..6adcc901 100644 --- a/pkg/eth/api.go +++ b/pkg/eth/api.go @@ -1245,7 +1245,8 @@ func (pea *PublicEthAPI) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx boo if inclTx { td, err := pea.B.GetTd(b.Hash()) if err != nil { - log.Errorf("error getting td for block with hash and number %s, %s: %s", b.Hash().String(), b.Number().String(), err) + err = fmt.Errorf("error getting TD for block at (%s, %s): %s", b.Number(), b.Hash(), err) + log.Error(err) return nil, err } fields["totalDifficulty"] = (*hexutil.Big)(td) diff --git a/pkg/eth/api_test/api_test.go b/pkg/eth/api_test/api_test.go index dfda4fdd..a44673e8 100644 --- a/pkg/eth/api_test/api_test.go +++ b/pkg/eth/api_test/api_test.go @@ -50,7 +50,7 @@ var ( blockHash = test_helpers.MockBlock.Header().Hash() baseFee = test_helpers.MockLondonBlock.BaseFee() ctx = context.Background() - chainConfig = &*params.TestChainConfig + chainConfig = &*params.MergedTestChainConfig expectedBlock = map[string]interface{}{ "number": (*hexutil.Big)(test_helpers.MockBlock.Number()), @@ -390,7 +390,7 @@ var _ = Describe("API", func() { Expect(block).To(BeZero()) }) It("Fetch BaseFee from london block by block hash, returns `nil` for legacy block", func() { - block, err := api.GetBlockByHash(ctx, test_helpers.MockBlock.Hash(), true) + block, err := api.GetBlockByHash(ctx, test_helpers.MockBlock.Hash(), false) Expect(err).ToNot(HaveOccurred()) _, ok := block["baseFeePerGas"] Expect(ok).To(Equal(false)) diff --git a/pkg/eth/backend.go b/pkg/eth/backend.go index 331ad0c7..34d2448e 100644 --- a/pkg/eth/backend.go +++ b/pkg/eth/backend.go @@ -271,7 +271,7 @@ func (b *Backend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Blo func (b *Backend) BlockByNumber(ctx context.Context, blockNumber rpc.BlockNumber) (*types.Block, error) { number, err := b.NormalizeBlockNumber(blockNumber) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to normalize block number: %w", err) } canonicalHash, err := b.GetCanonicalHash(uint64(number)) if err != nil { @@ -349,11 +349,16 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo return nil, err } - // Placeholder for withdrawal processing (TODO: https://git.vdb.to/cerc-io/ipld-eth-server/pulls/265) + // Fetch withdrawals var withdrawals types.Withdrawals if b.Config.ChainConfig.IsShanghai(header.Number, header.Time) { - // All blocks after Shanghai must include a withdrawals root. - withdrawals = make(types.Withdrawals, 0) + withdrawals, err = b.GetWithdrawals(tx, hash, blockNumber) + if err != nil && err != sql.ErrNoRows { + log.Error("error fetching withdrawals: ", err) + return nil, err + } + } else if len(withdrawals) > 0 { + return nil, errors.New("withdrawals set before Shanghai activation") } // Compose everything together into a complete block @@ -501,6 +506,23 @@ func (b *Backend) GetReceiptsByBlockHashAndNumber(tx *sqlx.Tx, hash common.Hash, return rcts, nil } +// GetWithdrawals retrieves transactions for a provided block hash and number +func (b *Backend) GetWithdrawals(tx *sqlx.Tx, hash common.Hash, number uint64) (types.Withdrawals, error) { + _, rlpBytes, err := b.Retriever.RetrieveWithdrawals(tx, hash, number) + if err != nil { + return nil, err + } + + withdrawals := make(types.Withdrawals, len(rlpBytes)) + for i, bytes := range rlpBytes { + withdrawals[i] = new(types.Withdrawal) + if err := rlp.DecodeBytes(bytes, withdrawals[i]); err != nil { + return nil, err + } + } + return withdrawals, nil +} + // GetTransaction retrieves a tx by hash // It also returns the blockhash, blocknumber, and tx index associated with the transaction func (b *Backend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { diff --git a/pkg/eth/retriever.go b/pkg/eth/retriever.go index 328e75fc..e2cf4c26 100644 --- a/pkg/eth/retriever.go +++ b/pkg/eth/retriever.go @@ -506,6 +506,24 @@ func (r *Retriever) RetrieveReceiptsByBlockHash(tx *sqlx.Tx, hash common.Hash) ( return cids, rcts, txs, nil } +// RetrieveWithdrawals returns the CIDs and RLP bytes for the withdrawals corresponding to the +// provided block hash, number. Returned CIDs correspond to the leaf node data which contains the +// withdrawal object. +func (r *Retriever) RetrieveWithdrawals(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, error) { + results := make([]ipldResult, 0) + if err := tx.Select(&results, RetrieveWithdrawalsPgStr, hash.Hex(), number); err != nil { + return nil, nil, err + } + cids := make([]string, len(results)) + withdrawals := make([][]byte, len(results)) + + for i, res := range results { + cids[i] = res.CID + withdrawals[i] = res.Data + } + return cids, withdrawals, nil +} + // RetrieveAccountByAddressAndBlockHash returns the cid and rlp bytes for the account corresponding to the provided address and block hash // TODO: ensure this handles deleted accounts appropriately func (r *Retriever) RetrieveAccountByAddressAndBlockHash(address common.Address, hash common.Hash) (StateAccountRecord, error) { diff --git a/pkg/eth/sql.go b/pkg/eth/sql.go index 95fef81c..392d6d0d 100644 --- a/pkg/eth/sql.go +++ b/pkg/eth/sql.go @@ -107,6 +107,21 @@ WHERE header_cids.block_hash = $1 AND blocks.key = receipt_cids.cid ORDER BY eth.transaction_cids.index ASC ` + RetrieveWithdrawalsPgStr = ` +SELECT withdrawal_cids.cid, + blocks.data + FROM eth.withdrawal_cids + JOIN eth.header_cids + ON header_cids.block_hash = $1 + AND header_cids.block_number = $2 + AND header_cids.canonical + AND withdrawal_cids.block_number = header_cids.block_number + AND withdrawal_cids.header_id = header_cids.block_hash + JOIN ipld.blocks + ON blocks.block_number = header_cids.block_number + AND blocks.key = withdrawal_cids.cid + ORDER BY eth.withdrawal_cids.index ASC` + RetrieveAccountByLeafKeyAndBlockHashPgStr = ` SELECT state_cids.nonce, state_cids.balance, diff --git a/pkg/eth/test_helpers/test_data.go b/pkg/eth/test_helpers/test_data.go index afe7b289..c66c033e 100644 --- a/pkg/eth/test_helpers/test_data.go +++ b/pkg/eth/test_helpers/test_data.go @@ -53,7 +53,11 @@ var ( Extra: []byte{}, } MockTransactions, MockReceipts, SenderAddr = createLegacyTransactionsAndReceipts() - MockUncles = []*types.Header{ + MockWithdrawals = types.Withdrawals{ + {Index: 0, Validator: 1, Address: Address, Amount: 1000000000}, + {Index: 1, Validator: 5, Address: AnotherAddress, Amount: 2000000000}, + } + MockUncles = []*types.Header{ { Time: 1, Number: big.NewInt(BlockNumber1 + 1), @@ -75,7 +79,7 @@ var ( ParentHash: Genesis.Hash(), }, } - MockBlock = createNewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, trie.NewEmpty(nil)) + MockBlock = createNewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, nil, trie.NewEmpty(nil)) MockChildHeader = types.Header{ Time: 0, Number: big.NewInt(BlockNumber1 + 1), @@ -326,11 +330,11 @@ var ( Extra: []byte{}, }, } - MockLondonBlock = createNewBlock(&MockLondonHeader, MockLondonTransactions, MockLondonUncles, MockLondonReceipts, trie.NewEmpty(nil)) + MockLondonBlock = createNewBlock(&MockLondonHeader, MockLondonTransactions, MockLondonUncles, MockLondonReceipts, MockWithdrawals, trie.NewEmpty(nil)) ) -func createNewBlock(header *types.Header, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, hasher types.TrieHasher) *types.Block { - block := types.NewBlock(header, txs, uncles, receipts, hasher) +func createNewBlock(header *types.Header, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals types.Withdrawals, hasher types.TrieHasher) *types.Block { + block := types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, hasher) bHash := block.Hash() for _, r := range receipts { for _, l := range r.Logs { diff --git a/test/stack.yml b/test/stack.yml index 8829a4d8..750d13b1 100644 --- a/test/stack.yml +++ b/test/stack.yml @@ -3,9 +3,9 @@ name: fixturenet-plugeth-tx description: "Plugeth Ethereum Fixturenet for testing ipld-eth-server" repos: - git.vdb.to/cerc-io/plugeth@v1.13.14-cerc-2 - - git.vdb.to/cerc-io/plugeth-statediff + - git.vdb.to/cerc-io/plugeth-statediff@index-withdrawals # todo: dev - git.vdb.to/cerc-io/lighthouse - - git.vdb.to/cerc-io/ipld-eth-db@v5.2.1-alpha + - git.vdb.to/cerc-io/ipld-eth-db@add-withdrawals # todo: dev - git.vdb.to/cerc-io/ipld-eth-server containers: - cerc/plugeth-statediff