Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

storage.gno

3.99 Kb · 162 lines
  1package storage
  2
  3import (
  4	"bytes"
  5	"crypto/keccak256"
  6	"encoding/binary"
  7	"errors"
  8
  9	"gno.land/p/onbloc/encoding/rlp"
 10	"gno.land/p/onbloc/verifier/evm/mpt"
 11)
 12
 13var (
 14	ErrInvalidRootLength  = errors.New("storage: root must be 32 bytes")
 15	ErrInvalidKeyLength   = errors.New("storage: key must be 32 bytes")
 16	ErrInvalidPathLength  = errors.New("storage: path must be 32 bytes")
 17	ErrInvalidValueLength = errors.New("storage: value must be 32 bytes")
 18	ErrValueMismatch      = errors.New("storage: value mismatch")
 19)
 20
 21// Proof is the storage proof shape returned by eth_getProof for one slot,
 22// normalized to bytes. Key and Value are 32-byte EVM words. Proof contains the
 23// raw RLP-encoded trie nodes from root to leaf.
 24//
 25// It is also the normalized form of Union's
 26// ethereum_light_client_types::StorageProof wire type:
 27// https://github.com/unionlabs/union/blob/main/lib/ethereum-light-client-types/src/storage_proof.rs
 28type Proof struct {
 29	Key   []byte
 30	Value []byte
 31	Proof [][]byte
 32}
 33
 34// VerifyProof verifies that proof.Key stores proof.Value under storageRoot.
 35// Ethereum storage tries are keyed by keccak256(slot), and the leaf payload is
 36// the RLP encoding of the storage value with leading zero bytes removed.
 37func VerifyProof(storageRoot []byte, proof Proof) error {
 38	err := validateWord(storageRoot, ErrInvalidRootLength)
 39	if err != nil {
 40		return err
 41	}
 42
 43	err = validateWord(proof.Key, ErrInvalidKeyLength)
 44	if err != nil {
 45		return err
 46	}
 47
 48	err = validateWord(proof.Value, ErrInvalidValueLength)
 49	if err != nil {
 50		return err
 51	}
 52
 53	path := keccak256.Sum256(proof.Key)
 54
 55	return mpt.Verify(storageRoot, path[:], encodeStorageValue(proof.Value), proof.Proof)
 56}
 57
 58// VerifyAbsence verifies that proof.Key is absent under storageRoot. The Value
 59// field is intentionally ignored for absence proofs because eth_getProof returns
 60// zero for missing slots.
 61func VerifyAbsence(storageRoot []byte, proof Proof) error {
 62	err := validateWord(storageRoot, ErrInvalidRootLength)
 63	if err != nil {
 64		return err
 65	}
 66
 67	err = validateWord(proof.Key, ErrInvalidKeyLength)
 68	if err != nil {
 69		return err
 70	}
 71
 72	path := keccak256.Sum256(proof.Key)
 73
 74	return mpt.VerifyAbsence(storageRoot, path[:], proof.Proof)
 75}
 76
 77// CheckValue verifies only the value reported by the proof object. This is
 78// useful when a caller receives an expected 32-byte commitment separately.
 79func CheckValue(proof Proof, expected []byte) error {
 80	err := validateWord(proof.Value, ErrInvalidValueLength)
 81	if err != nil {
 82		return err
 83	}
 84
 85	err = validateWord(expected, ErrInvalidValueLength)
 86	if err != nil {
 87		return err
 88	}
 89
 90	if !bytes.Equal(proof.Value, expected) {
 91		return ErrValueMismatch
 92	}
 93
 94	return nil
 95}
 96
 97// StoragePath returns the MPT path used for an EVM storage slot.
 98func StoragePath(slot []byte) ([32]byte, error) {
 99	err := validateWord(slot, ErrInvalidKeyLength)
100	if err != nil {
101		return [32]byte{}, err
102	}
103
104	return keccak256.Sum256(slot), nil
105}
106
107// IBCCommitmentKey mirrors Union's ethereum::ibc_commitment_key for
108// mapping(bytes32 => bytes32) commitments at slot 0.
109//
110// Union reference:
111// https://github.com/unionlabs/union/blob/main/lib/unionlabs/src/ethereum.rs
112func IBCCommitmentKey(path []byte) ([32]byte, error) {
113	err := validateWord(path, ErrInvalidPathLength)
114	if err != nil {
115		return [32]byte{}, err
116	}
117
118	var input [64]byte
119
120	copy(input[0:32], path)
121
122	return keccak256.Sum256(input[:]), nil
123}
124
125// EncodeStorageValue returns the leaf payload expected by the storage trie.
126func EncodeStorageValue(word []byte) ([]byte, error) {
127	err := validateWord(word, ErrInvalidValueLength)
128	if err != nil {
129		return nil, err
130	}
131
132	return encodeStorageValue(word), nil
133}
134
135func validateWord(word []byte, err error) error {
136	if len(word) != 32 {
137		return err
138	}
139
140	return nil
141}
142
143func encodeStorageValue(word []byte) []byte {
144	return rlp.EncodeBytes(trimLeadingZeroes(word))
145}
146
147func trimLeadingZeroes(word []byte) []byte {
148	i := 0
149
150	for i < len(word) && word[i] == 0 {
151		i++
152	}
153
154	return word[i:]
155}
156
157func WordFromUint64(v uint64) []byte {
158	out := make([]byte, 32)
159	binary.BigEndian.PutUint64(out[24:], v)
160
161	return out
162}