From 27c3baab4b7a5b30e91c5e8b79f5c667ec325c1a Mon Sep 17 00:00:00 2001 From: Herbert Jordan Date: Fri, 29 Nov 2024 21:25:35 +0100 Subject: [PATCH] Update Carmen to get proof steps in correct order --- ethapi/api.go | 8 +++----- go.mod | 2 +- go.sum | 4 ++-- tests/block_header_test.go | 41 ++++++++++++++++++++++++++------------ 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/ethapi/api.go b/ethapi/api.go index b826cf734..22166561e 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -745,7 +745,7 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre storageProof := make([]StorageResult, len(keys)) for i, key := range keys { - elements, _, _ := proof.GetStorageElements(cc.Hash(header.Root), cc.Address(address), cc.Key(keys[i])) + elements, _ := proof.GetStorageElements(cc.Hash(header.Root), cc.Address(address), cc.Key(keys[i])) storageProof[i] = StorageResult{ Key: key.Hex(), Value: (*hexutil.Big)(state.GetState(address, key).Big()), @@ -753,14 +753,12 @@ func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Addre } } - _, storageHash, _ := proof.GetStorageElements(cc.Hash(header.Root), cc.Address(address)) + accountProof, storageHash, _ := proof.GetAccountElements(cc.Hash(header.Root), cc.Address(address)) codeHash := state.GetCodeHash(address) - accountProof, _ := proof.Extract(cc.Hash(header.Root), cc.Address(address)) - return &AccountResult{ Address: address, - AccountProof: toHexSlice(accountProof.GetElements()), + AccountProof: toHexSlice(accountProof), Balance: (*hexutil.U256)(state.GetBalance(address)), CodeHash: codeHash, Nonce: hexutil.Uint64(state.GetNonce(address)), diff --git a/go.mod b/go.mod index 7a0384646..2e0d2dd66 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.0 toolchain go1.22.3 require ( - github.com/Fantom-foundation/Carmen/go v0.0.0-20241128114701-34dad37b5648 + github.com/Fantom-foundation/Carmen/go v0.0.0-20241129202153-690bc10fa624 github.com/Fantom-foundation/Tosca v0.0.0-20241028082205-7b33705a4675 github.com/Fantom-foundation/lachesis-base v0.0.0-20240116072301-a75735c4ef00 github.com/cespare/cp v1.1.1 diff --git a/go.sum b/go.sum index dfa8f6462..01e3251f8 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Fantom-foundation/Carmen/go v0.0.0-20241128114701-34dad37b5648 h1:gVmZHEjmw9/JAyaU28MOWkbDzLPZ+Qfu1WTHOuE26UU= -github.com/Fantom-foundation/Carmen/go v0.0.0-20241128114701-34dad37b5648/go.mod h1:vNRGm/b21hDlF8kYyKHkXq1s+jLMKpV4jGwXHylXIIg= +github.com/Fantom-foundation/Carmen/go v0.0.0-20241129202153-690bc10fa624 h1:9bFojtY5KoLFUtLYZGc1GNf0oIdBIOarlLr4+a/DA7U= +github.com/Fantom-foundation/Carmen/go v0.0.0-20241129202153-690bc10fa624/go.mod h1:vNRGm/b21hDlF8kYyKHkXq1s+jLMKpV4jGwXHylXIIg= github.com/Fantom-foundation/Tosca v0.0.0-20241028082205-7b33705a4675 h1:Meqtt0eM9UAw1ceQ1tGiD497V0IVfqj8c6UrFJhkFNE= github.com/Fantom-foundation/Tosca v0.0.0-20241028082205-7b33705a4675/go.mod h1:8i7r+dUOkXjEC61SaFoRet6JYjRaSoiM/PhviP6K4Y8= github.com/Fantom-foundation/go-ethereum-sonic v0.0.0-20241022121122-7063a6b506bd h1:WoAkgzLLlyh3Zpnd/3gW8Ym5sHtugLCA4rQdT5udVF8= diff --git a/tests/block_header_test.go b/tests/block_header_test.go index a59fa3f43..f44737410 100644 --- a/tests/block_header_test.go +++ b/tests/block_header_test.go @@ -481,14 +481,14 @@ func testHeaders_StateRootsMatchActualStateRoots(t *testing.T, headers []*types. // root we see in the database. To get access to the database, we request // a witness proof for an account at the given block. From this proof we // we have a list of state-root candidates which we can test for. - steps, err := getWitnessProofSteps(client, int(header.Number.Int64())) + want, err := getStateRoot(client, int(header.Number.Int64())) require.NoError(err, "failed to get witness proof for block %d", i) got := header.Root - require.Contains(steps, got, "state root mismatch for block %d", i) + require.Equal(want, got, "state root mismatch for block %d", i) } } -func getWitnessProofSteps(client *ethclient.Client, blockNumber int) ([]common.Hash, error) { +func getStateRoot(client *ethclient.Client, blockNumber int) (common.Hash, error) { var result struct { AccountProof []string } @@ -500,18 +500,19 @@ func getWitnessProofSteps(client *ethclient.Client, blockNumber int) ([]common.H fmt.Sprintf("0x%x", blockNumber), ) if err != nil { - return nil, fmt.Errorf("failed to get witness proof; %v", err) + return common.Hash{}, fmt.Errorf("failed to get witness proof; %v", err) } - res := []common.Hash{} - for _, proof := range result.AccountProof { - data, err := hexutil.Decode(proof) - if err != nil { - return nil, err - } - res = append(res, common.BytesToHash(crypto.Keccak256(data))) + // The hash of the first element of the account proof is the state root. + if len(result.AccountProof) == 0 { + return common.Hash{}, fmt.Errorf("no account proof found") } - return res, nil + + data, err := hexutil.Decode(result.AccountProof[0]) + if err != nil { + return common.Hash{}, err + } + return common.BytesToHash(crypto.Keccak256(data)), nil } func testHeaders_SystemContractsHaveNonZeroNonce(t *testing.T, headers []*types.Header, client *ethclient.Client) { @@ -708,6 +709,7 @@ func getVerifiedCounterState( require := require.New(t) var result struct { AccountProof []string + StorageHash common.Hash StorageProof []struct { Value string Proof []string @@ -722,6 +724,7 @@ func getVerifiedCounterState( ) require.NoError(err, "failed to get witness proof") require.Equal(1, len(result.StorageProof), "expected exactly one storage proof") + require.GreaterOrEqual(len(result.StorageProof[0].Proof), 1, "expected at least one proof element") // Verify the proof. elements := []carmen.Bytes{} @@ -733,13 +736,25 @@ func getVerifiedCounterState( } } proof := carmen.CreateWitnessProofFromNodes(elements...) - require.True(proof.IsValid(), "proof is invalid") + require.True(proof.IsValid()) // Extract the storage value from the proof. value, present, err := proof.GetState(carmen.Hash(stateRoot), carmen.Address(counterAddress), carmen.Key{}) require.NoError(err, "failed to get state from proof") require.True(present, "slot not found in proof") + // Check that the storage root hash is consistent. + _, storageRoot, complete := proof.GetAccountElements(carmen.Hash(stateRoot), carmen.Address(counterAddress)) + require.True(complete, "proof is not complete") + require.Equal(common.Hash(storageRoot), result.StorageHash, "storage root mismatch") + + // Check that the storage proof starts with an element that corresponds to + // the storage root. + hash := crypto.Keccak256(hexutil.MustDecode(result.StorageProof[0].Proof[0])) + firstElementHash := carmen.Hash{} + copy(firstElementHash[:], hash) + require.Equal(storageRoot, firstElementHash, "storage proof does not start with the storage root") + // Compare the proof value with the value in the RPC result. fromProof := int(new(big.Int).SetBytes(value[:]).Uint64()) fromResult, err := hexutil.DecodeUint64(result.StorageProof[0].Value)