mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 19:50:36 +00:00
4e9b378a5d
This addresses the last known deficiency of the eth_getProof implementation. The previous code would return an error in the event that the element was not found in the trie. EIP-1186 allows for 'negative' proofs where a proof demonstrates that an element cannot be in the trie, so this commit updates the logic to support that case. Co-authored-by: Jason Yellick <jason@enya.ai>
152 lines
4.6 KiB
Go
152 lines
4.6 KiB
Go
package trie
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/holiman/uint256"
|
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
|
"github.com/ledgerwatch/erigon/common/hexutil"
|
|
"github.com/ledgerwatch/erigon/core/types/accounts"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func FakePreimage(hash libcommon.Hash) libcommon.Hash {
|
|
result := hash
|
|
for i, b := range hash {
|
|
result[i] = b ^ 1
|
|
}
|
|
return result
|
|
}
|
|
|
|
// NewManualProofRetainer is a way to allow external tests in this package to
|
|
// manually construct a ProofRetainer based on a set of keys. This is
|
|
// especially useful for tests which want to manually manipulate the hash
|
|
// databases without worrying about generating and tracking pre-images.
|
|
func NewManualProofRetainer(t *testing.T, acc *accounts.Account, rl *RetainList, keys [][]byte) *ProofRetainer {
|
|
var accHexKey []byte
|
|
var storageKeys []libcommon.Hash
|
|
var storageHexKeys [][]byte
|
|
for _, key := range keys {
|
|
switch len(key) {
|
|
case 32:
|
|
require.Nil(t, accHexKey, "only one account key may be provided")
|
|
accHexKey = rl.AddKey(key)
|
|
case 72:
|
|
if accHexKey == nil {
|
|
accHexKey = rl.AddKey(key[:32])
|
|
}
|
|
storageKeys = append(storageKeys, FakePreimage(libcommon.BytesToHash(key[40:])))
|
|
storageHexKeys = append(storageHexKeys, rl.AddKey(key))
|
|
require.Equal(t, accHexKey, storageHexKeys[0][:64], "all storage keys must be for the same account")
|
|
default:
|
|
require.Fail(t, "unexpected key length %d", len(key))
|
|
}
|
|
}
|
|
return &ProofRetainer{
|
|
rl: rl,
|
|
acc: acc,
|
|
accHexKey: accHexKey,
|
|
storageKeys: storageKeys,
|
|
storageHexKeys: storageHexKeys,
|
|
}
|
|
}
|
|
|
|
func TestProofRetainerConstruction(t *testing.T) {
|
|
rl := NewRetainList(0)
|
|
pr, err := NewProofRetainer(
|
|
libcommon.Address{0x1},
|
|
&accounts.Account{
|
|
Initialised: true,
|
|
Nonce: 2,
|
|
Balance: *uint256.NewInt(6e9),
|
|
CodeHash: libcommon.Hash{3},
|
|
Incarnation: 3,
|
|
},
|
|
[]libcommon.Hash{{1}, {2}, {3}},
|
|
rl,
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, rl.hexes, 4)
|
|
|
|
validKeys := [][]byte{
|
|
pr.storageHexKeys[2][:],
|
|
pr.storageHexKeys[2][:98],
|
|
pr.storageHexKeys[2][:95],
|
|
pr.storageHexKeys[1][:],
|
|
pr.storageHexKeys[1][:90],
|
|
pr.storageHexKeys[0][:],
|
|
pr.storageHexKeys[0][:85],
|
|
pr.accHexKey[:],
|
|
pr.accHexKey[:15],
|
|
{},
|
|
}
|
|
|
|
invalidKeys := [][]byte{
|
|
pr.accHexKey[1:16],
|
|
pr.storageHexKeys[0][12:80],
|
|
pr.storageHexKeys[2][19:90],
|
|
}
|
|
|
|
for _, key := range validKeys {
|
|
pe := pr.ProofElement(key)
|
|
require.NotNil(t, pe)
|
|
require.Equal(t, pe.hexKey, key)
|
|
switch len(key) {
|
|
case 64: // Account leaf key
|
|
pe.storageRoot = libcommon.Hash{3}
|
|
pe.storageRootKey = key
|
|
case 144: // Storage leaf key
|
|
pe.storageValue = uint256.NewInt(5)
|
|
pe.storageKey = key[2*(32+8):]
|
|
}
|
|
pe.proof.Write(key)
|
|
}
|
|
for _, key := range invalidKeys {
|
|
pe := pr.ProofElement(key)
|
|
require.Nil(t, pe)
|
|
}
|
|
require.Equal(t, len(validKeys), len(pr.proofs))
|
|
|
|
accProof, err := pr.ProofResult()
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, accProof.AccountProof, 3)
|
|
require.Equal(t, []byte(nil), []byte(accProof.AccountProof[0]))
|
|
require.Equal(t, validKeys[8], []byte(accProof.AccountProof[1]))
|
|
require.Equal(t, validKeys[7], []byte(accProof.AccountProof[2]))
|
|
|
|
require.Len(t, accProof.StorageProof, 3)
|
|
require.Equal(t, accProof.StorageProof[0].Key, libcommon.Hash{1})
|
|
require.Len(t, accProof.StorageProof[0].Proof, 2)
|
|
require.Equal(t, validKeys[6], []byte(accProof.StorageProof[0].Proof[0]))
|
|
require.Equal(t, validKeys[5], []byte(accProof.StorageProof[0].Proof[1]))
|
|
|
|
require.Equal(t, accProof.StorageProof[1].Key, libcommon.Hash{2})
|
|
require.Len(t, accProof.StorageProof[1].Proof, 2)
|
|
require.Equal(t, validKeys[4], []byte(accProof.StorageProof[1].Proof[0]))
|
|
require.Equal(t, validKeys[3], []byte(accProof.StorageProof[1].Proof[1]))
|
|
|
|
require.Equal(t, accProof.StorageProof[2].Key, libcommon.Hash{3})
|
|
require.Len(t, accProof.StorageProof[2].Proof, 3)
|
|
require.Equal(t, validKeys[2], []byte(accProof.StorageProof[2].Proof[0]))
|
|
require.Equal(t, validKeys[1], []byte(accProof.StorageProof[2].Proof[1]))
|
|
require.Equal(t, validKeys[0], []byte(accProof.StorageProof[2].Proof[2]))
|
|
|
|
t.Run("missingStorageRoot", func(t *testing.T) {
|
|
oldStorageHash := pr.proofs[2].storageRoot
|
|
pr.proofs[2].storageRoot = libcommon.Hash{}
|
|
defer func() { pr.proofs[2].storageRoot = oldStorageHash }()
|
|
_, err := pr.ProofResult()
|
|
require.Error(t, err, "did not find storage root in proof elements")
|
|
})
|
|
|
|
t.Run("missingStorageValue", func(t *testing.T) {
|
|
oldKey := pr.proofs[4].storageValue
|
|
pr.proofs[4].storageValue = nil
|
|
defer func() { pr.proofs[4].storageValue = oldKey }()
|
|
accProof, err := pr.ProofResult()
|
|
require.NoError(t, err)
|
|
require.Equal(t, &hexutil.Big{}, accProof.StorageProof[0].Value)
|
|
})
|
|
}
|