From 160a28774da027196f60976992e9d9baf6c53f7f Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Fri, 29 Nov 2024 13:07:34 +0000 Subject: [PATCH 1/2] move storage root verification to AccountResult::VerifyStorageRoot --- op-service/eth/account_proof.go | 65 +++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/op-service/eth/account_proof.go b/op-service/eth/account_proof.go index b7d09eeff062..7d7cb2cff956 100644 --- a/op-service/eth/account_proof.go +++ b/op-service/eth/account_proof.go @@ -33,34 +33,9 @@ type AccountResult struct { // Verify an account (and optionally storage) proof from the getProof RPC. See https://eips.ethereum.org/EIPS/eip-1186 func (res *AccountResult) Verify(stateRoot common.Hash) error { - // verify storage proof values, if any, against the storage trie root hash of the account - for i, entry := range res.StorageProof { - // load all MPT nodes into a DB - db := memorydb.New() - for j, encodedNode := range entry.Proof { - nodeKey := encodedNode - if len(encodedNode) >= 32 { // small MPT nodes are not hashed - nodeKey = crypto.Keccak256(encodedNode) - } - if err := db.Put(nodeKey, encodedNode); err != nil { - return fmt.Errorf("failed to load storage proof node %d of storage value %d into mem db: %w", j, i, err) - } - } - path := crypto.Keccak256(entry.Key[:]) - val, err := trie.VerifyProof(res.StorageHash, path, db) - if err != nil { - return fmt.Errorf("failed to verify storage value %d with key %s (path %x) in storage trie %s: %w", i, entry.Key, path, res.StorageHash, err) - } - if val == nil && entry.Value.ToInt().Cmp(common.Big0) == 0 { // empty storage is zero by default - continue - } - comparison, err := rlp.EncodeToBytes(entry.Value.ToInt().Bytes()) - if err != nil { - return fmt.Errorf("failed to encode storage value %d with key %s (path %x) in storage trie %s: %w", i, entry.Key, path, res.StorageHash, err) - } - if !bytes.Equal(val, comparison) { - return fmt.Errorf("value %d in storage proof does not match proven value at key %s (path %x)", i, entry.Key, path) - } + err := res.VerifyStorageRoot() + if err != nil { + return err } accountClaimed := []any{uint64(res.Nonce), res.Balance.ToInt().Bytes(), res.StorageHash, res.CodeHash} @@ -93,3 +68,37 @@ func (res *AccountResult) Verify(stateRoot common.Hash) error { } return err } + +// Verify an account storage proof from the getProof RPC. +func (res *AccountResult) VerifyStorageRoot() error { + // verify storage proof values, if any, against the storage trie root hash of the account + for i, entry := range res.StorageProof { + // load all MPT nodes into a DB + db := memorydb.New() + for j, encodedNode := range entry.Proof { + nodeKey := encodedNode + if len(encodedNode) >= 32 { // small MPT nodes are not hashed + nodeKey = crypto.Keccak256(encodedNode) + } + if err := db.Put(nodeKey, encodedNode); err != nil { + return fmt.Errorf("failed to load storage proof node %d of storage value %d into mem db: %w", j, i, err) + } + } + path := crypto.Keccak256(entry.Key[:]) + val, err := trie.VerifyProof(res.StorageHash, path, db) + if err != nil { + return fmt.Errorf("failed to verify storage value %d with key %s (path %x) in storage trie %s: %w", i, entry.Key, path, res.StorageHash, err) + } + if val == nil && entry.Value.ToInt().Cmp(common.Big0) == 0 { // empty storage is zero by default + continue + } + comparison, err := rlp.EncodeToBytes(entry.Value.ToInt().Bytes()) + if err != nil { + return fmt.Errorf("failed to encode storage value %d with key %s (path %x) in storage trie %s: %w", i, entry.Key, path, res.StorageHash, err) + } + if !bytes.Equal(val, comparison) { + return fmt.Errorf("value %d in storage proof does not match proven value at key %s (path %x)", i, entry.Key, path) + } + } + return nil +} From e810b2590550dd4e1b5bf667fcaa1a14b3740870 Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Fri, 29 Nov 2024 13:07:53 +0000 Subject: [PATCH 2/2] use VerifyStorageRoot on OutputV0AtBlock query instead --- op-service/sources/l2_client.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/op-service/sources/l2_client.go b/op-service/sources/l2_client.go index 078385d40510..ca5178cd4961 100644 --- a/op-service/sources/l2_client.go +++ b/op-service/sources/l2_client.go @@ -182,14 +182,14 @@ func (s *L2Client) OutputV0AtBlock(ctx context.Context, blockHash common.Hash) ( proof, err := s.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, blockHash.String()) if err != nil { - return nil, fmt.Errorf("failed to get contract proof at block %s: %w", blockHash, err) + return nil, fmt.Errorf("failed to get contract proof at address %s block %s: %w ", predeploys.L2ToL1MessagePasserAddr, blockHash, err) } if proof == nil { return nil, fmt.Errorf("proof %w", ethereum.NotFound) } // make sure that the proof (including storage hash) that we retrieved is correct by verifying it against the state-root - if err := proof.Verify(head.Root()); err != nil { - return nil, fmt.Errorf("invalid withdrawal root hash, state root was %s: %w", head.Root(), err) + if err := proof.VerifyStorageRoot(); err != nil { + return nil, fmt.Errorf("invalid withdrawal storage proof, root was %s: %w", proof.StorageHash, err) } stateRoot := head.Root() return ð.OutputV0{