internal/ethapi: fix codehash lookup in eth_getProof (#28357)
This change fixes #28355, where eth_getProof failed to return the correct codehash under certain conditions. This PR changes the logic to unconditionally look up the codehash, and also adds some more tests.
This commit is contained in:
parent
a5544d35f6
commit
8b99ad4602
@ -41,6 +41,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
testContract = common.HexToAddress("0xbeef")
|
||||||
testSlot = common.HexToHash("0xdeadbeef")
|
testSlot = common.HexToHash("0xdeadbeef")
|
||||||
testValue = crypto.Keccak256Hash(testSlot[:])
|
testValue = crypto.Keccak256Hash(testSlot[:])
|
||||||
testBalance = big.NewInt(2e15)
|
testBalance = big.NewInt(2e15)
|
||||||
@ -79,7 +80,8 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
|||||||
func generateTestChain() (*core.Genesis, []*types.Block) {
|
func generateTestChain() (*core.Genesis, []*types.Block) {
|
||||||
genesis := &core.Genesis{
|
genesis := &core.Genesis{
|
||||||
Config: params.AllEthashProtocolChanges,
|
Config: params.AllEthashProtocolChanges,
|
||||||
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance, Storage: map[common.Hash]common.Hash{testSlot: testValue}}},
|
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance, Storage: map[common.Hash]common.Hash{testSlot: testValue}},
|
||||||
|
testContract: {Nonce: 1, Code: []byte{0x13, 0x37}}},
|
||||||
ExtraData: []byte("test genesis"),
|
ExtraData: []byte("test genesis"),
|
||||||
Timestamp: 9000,
|
Timestamp: 9000,
|
||||||
}
|
}
|
||||||
@ -103,8 +105,11 @@ func TestGethClient(t *testing.T) {
|
|||||||
test func(t *testing.T)
|
test func(t *testing.T)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"TestGetProof",
|
"TestGetProof1",
|
||||||
func(t *testing.T) { testGetProof(t, client) },
|
func(t *testing.T) { testGetProof(t, client, testAddr) },
|
||||||
|
}, {
|
||||||
|
"TestGetProof2",
|
||||||
|
func(t *testing.T) { testGetProof(t, client, testContract) },
|
||||||
}, {
|
}, {
|
||||||
"TestGetProofCanonicalizeKeys",
|
"TestGetProofCanonicalizeKeys",
|
||||||
func(t *testing.T) { testGetProofCanonicalizeKeys(t, client) },
|
func(t *testing.T) { testGetProofCanonicalizeKeys(t, client) },
|
||||||
@ -201,39 +206,42 @@ func testAccessList(t *testing.T, client *rpc.Client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetProof(t *testing.T, client *rpc.Client) {
|
func testGetProof(t *testing.T, client *rpc.Client, addr common.Address) {
|
||||||
ec := New(client)
|
ec := New(client)
|
||||||
ethcl := ethclient.NewClient(client)
|
ethcl := ethclient.NewClient(client)
|
||||||
result, err := ec.GetProof(context.Background(), testAddr, []string{testSlot.String()}, nil)
|
result, err := ec.GetProof(context.Background(), addr, []string{testSlot.String()}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(result.Address[:], testAddr[:]) {
|
if result.Address != addr {
|
||||||
t.Fatalf("unexpected address, want: %v got: %v", testAddr, result.Address)
|
t.Fatalf("unexpected address, have: %v want: %v", result.Address, addr)
|
||||||
}
|
}
|
||||||
// test nonce
|
// test nonce
|
||||||
nonce, _ := ethcl.NonceAt(context.Background(), result.Address, nil)
|
if nonce, _ := ethcl.NonceAt(context.Background(), addr, nil); result.Nonce != nonce {
|
||||||
if result.Nonce != nonce {
|
|
||||||
t.Fatalf("invalid nonce, want: %v got: %v", nonce, result.Nonce)
|
t.Fatalf("invalid nonce, want: %v got: %v", nonce, result.Nonce)
|
||||||
}
|
}
|
||||||
// test balance
|
// test balance
|
||||||
balance, _ := ethcl.BalanceAt(context.Background(), result.Address, nil)
|
if balance, _ := ethcl.BalanceAt(context.Background(), addr, nil); result.Balance.Cmp(balance) != 0 {
|
||||||
if result.Balance.Cmp(balance) != 0 {
|
|
||||||
t.Fatalf("invalid balance, want: %v got: %v", balance, result.Balance)
|
t.Fatalf("invalid balance, want: %v got: %v", balance, result.Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// test storage
|
// test storage
|
||||||
if len(result.StorageProof) != 1 {
|
if len(result.StorageProof) != 1 {
|
||||||
t.Fatalf("invalid storage proof, want 1 proof, got %v proof(s)", len(result.StorageProof))
|
t.Fatalf("invalid storage proof, want 1 proof, got %v proof(s)", len(result.StorageProof))
|
||||||
}
|
}
|
||||||
proof := result.StorageProof[0]
|
for _, proof := range result.StorageProof {
|
||||||
slotValue, _ := ethcl.StorageAt(context.Background(), testAddr, testSlot, nil)
|
|
||||||
if !bytes.Equal(slotValue, proof.Value.Bytes()) {
|
|
||||||
t.Fatalf("invalid storage proof value, want: %v, got: %v", slotValue, proof.Value.Bytes())
|
|
||||||
}
|
|
||||||
if proof.Key != testSlot.String() {
|
if proof.Key != testSlot.String() {
|
||||||
t.Fatalf("invalid storage proof key, want: %q, got: %q", testSlot.String(), proof.Key)
|
t.Fatalf("invalid storage proof key, want: %q, got: %q", testSlot.String(), proof.Key)
|
||||||
}
|
}
|
||||||
|
slotValue, _ := ethcl.StorageAt(context.Background(), addr, common.HexToHash(proof.Key), nil)
|
||||||
|
if have, want := common.BigToHash(proof.Value), common.BytesToHash(slotValue); have != want {
|
||||||
|
t.Fatalf("addr %x, invalid storage proof value: have: %v, want: %v", addr, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// test code
|
||||||
|
code, _ := ethcl.CodeAt(context.Background(), addr, nil)
|
||||||
|
if have, want := result.CodeHash, crypto.Keccak256Hash(code); have != want {
|
||||||
|
t.Fatalf("codehash wrong, have %v want %v ", have, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetProofCanonicalizeKeys(t *testing.T, client *rpc.Client) {
|
func testGetProofCanonicalizeKeys(t *testing.T, client *rpc.Client) {
|
||||||
|
@ -675,10 +675,6 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
|
|||||||
keys = make([]common.Hash, len(storageKeys))
|
keys = make([]common.Hash, len(storageKeys))
|
||||||
keyLengths = make([]int, len(storageKeys))
|
keyLengths = make([]int, len(storageKeys))
|
||||||
storageProof = make([]StorageResult, len(storageKeys))
|
storageProof = make([]StorageResult, len(storageKeys))
|
||||||
|
|
||||||
storageTrie state.Trie
|
|
||||||
storageHash = types.EmptyRootHash
|
|
||||||
codeHash = types.EmptyCodeHash
|
|
||||||
)
|
)
|
||||||
// Deserialize all keys. This prevents state access on invalid input.
|
// Deserialize all keys. This prevents state access on invalid input.
|
||||||
for i, hexKey := range storageKeys {
|
for i, hexKey := range storageKeys {
|
||||||
@ -688,23 +684,22 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
statedb, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if statedb == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if storageRoot := state.GetStorageRoot(address); storageRoot != types.EmptyRootHash && storageRoot != (common.Hash{}) {
|
codeHash := statedb.GetCodeHash(address)
|
||||||
|
storageRoot := statedb.GetStorageRoot(address)
|
||||||
|
|
||||||
|
if len(keys) > 0 {
|
||||||
|
var storageTrie state.Trie
|
||||||
|
if storageRoot != types.EmptyRootHash && storageRoot != (common.Hash{}) {
|
||||||
id := trie.StorageTrieID(header.Root, crypto.Keccak256Hash(address.Bytes()), storageRoot)
|
id := trie.StorageTrieID(header.Root, crypto.Keccak256Hash(address.Bytes()), storageRoot)
|
||||||
tr, err := trie.NewStateTrie(id, state.Database().TrieDB())
|
st, err := trie.NewStateTrie(id, statedb.Database().TrieDB())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
storageTrie = tr
|
storageTrie = st
|
||||||
}
|
|
||||||
// If we have a storageTrie, the account exists and we must update
|
|
||||||
// the storage root hash and the code hash.
|
|
||||||
if storageTrie != nil {
|
|
||||||
storageHash = storageTrie.Hash()
|
|
||||||
codeHash = state.GetCodeHash(address)
|
|
||||||
}
|
}
|
||||||
// Create the proofs for the storageKeys.
|
// Create the proofs for the storageKeys.
|
||||||
for i, key := range keys {
|
for i, key := range keys {
|
||||||
@ -718,7 +713,6 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
|
|||||||
} else {
|
} else {
|
||||||
outputKey = hexutil.Encode(key[:])
|
outputKey = hexutil.Encode(key[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
if storageTrie == nil {
|
if storageTrie == nil {
|
||||||
storageProof[i] = StorageResult{outputKey, &hexutil.Big{}, []string{}}
|
storageProof[i] = StorageResult{outputKey, &hexutil.Big{}, []string{}}
|
||||||
continue
|
continue
|
||||||
@ -727,12 +721,12 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
|
|||||||
if err := storageTrie.Prove(crypto.Keccak256(key.Bytes()), &proof); err != nil {
|
if err := storageTrie.Prove(crypto.Keccak256(key.Bytes()), &proof); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
value := (*hexutil.Big)(state.GetState(address, key).Big())
|
value := (*hexutil.Big)(statedb.GetState(address, key).Big())
|
||||||
storageProof[i] = StorageResult{outputKey, value, proof}
|
storageProof[i] = StorageResult{outputKey, value, proof}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Create the accountProof.
|
// Create the accountProof.
|
||||||
tr, err := trie.NewStateTrie(trie.StateTrieID(header.Root), state.Database().TrieDB())
|
tr, err := trie.NewStateTrie(trie.StateTrieID(header.Root), statedb.Database().TrieDB())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -743,12 +737,12 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
|
|||||||
return &AccountResult{
|
return &AccountResult{
|
||||||
Address: address,
|
Address: address,
|
||||||
AccountProof: accountProof,
|
AccountProof: accountProof,
|
||||||
Balance: (*hexutil.Big)(state.GetBalance(address)),
|
Balance: (*hexutil.Big)(statedb.GetBalance(address)),
|
||||||
CodeHash: codeHash,
|
CodeHash: codeHash,
|
||||||
Nonce: hexutil.Uint64(state.GetNonce(address)),
|
Nonce: hexutil.Uint64(statedb.GetNonce(address)),
|
||||||
StorageHash: storageHash,
|
StorageHash: storageRoot,
|
||||||
StorageProof: storageProof,
|
StorageProof: storageProof,
|
||||||
}, state.Error()
|
}, statedb.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeHash parses a hex-encoded 32-byte hash. The input may optionally
|
// decodeHash parses a hex-encoded 32-byte hash. The input may optionally
|
||||||
|
Loading…
Reference in New Issue
Block a user