From 67039e06b0b4bc75808321b5fa370039676a124a Mon Sep 17 00:00:00 2001 From: Eduard S Date: Mon, 5 Jul 2021 15:48:35 +0200 Subject: [PATCH] Replace proof Verification by go-ethereum impl - Remove the custom Merkle Patricia Trie proof verification implementation - Remove the custom RLP encoding/decoding implementation - Use the Merkle Patricia Trie proof verification function from go-ethereum - This adds support to all types of non-existence proofs - Use the RLP encoding/decoding functions from go-ethereum - Add BytesHex and SliceBytesHex to abstract encoding of byte slices in the StorageProof types --- cmd/ethproof/ethproof.go | 1 + ethstorageproof/ethstorageproof.go | 354 ++---------------------- ethstorageproof/ethstorageproof_test.go | 220 ++------------- ethstorageproof/types.go | 109 +++++++- examples/verify-top-50-erc20/test.sh | 2 + token/mapbased/mapbased.go | 10 +- token/minime/helpers.go | 24 +- 7 files changed, 170 insertions(+), 550 deletions(-) diff --git a/cmd/ethproof/ethproof.go b/cmd/ethproof/ethproof.go index 41802c8..2f0677c 100644 --- a/cmd/ethproof/ethproof.go +++ b/cmd/ethproof/ethproof.go @@ -89,6 +89,7 @@ func main() { log.Printf("balance on block %s: %s", block.String(), balance.String()) log.Printf("hex balance: %x\n", fullBalance.Bytes()) log.Printf("storage root: %x\n", sproof.StorageHash) + if err := minime.VerifyProof( common.HexToAddress(*holder), sproof.StorageHash, diff --git a/ethstorageproof/ethstorageproof.go b/ethstorageproof/ethstorageproof.go index 05c869b..a48a626 100644 --- a/ethstorageproof/ethstorageproof.go +++ b/ethstorageproof/ethstorageproof.go @@ -4,16 +4,13 @@ package ethstorageproof import ( "bytes" - "encoding/binary" - "encoding/hex" "errors" - "fmt" - "math" + "math/big" "github.com/ethereum/go-ethereum/common" - - //"github.com/ethereum/go-ethereum/trie" - "golang.org/x/crypto/sha3" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" ) const ( @@ -49,30 +46,13 @@ func VerifyEIP1186(proof *StorageProof) (bool, error) { // VerifyEthAccountProof verifies an Ethereum account proof against the StateRoot. // It does not verify the storage proof(s). func VerifyEthAccountProof(proof *StorageProof) (bool, error) { - var nonce RlpString - var balance RlpString - var storageroot RlpString - var codehash RlpString - var err error - - // "0x0" means empty on RLP encoding, so if that is the case, do not decode - if proof.Nonce.String() != "0x0" { - nonce, err = hex.DecodeString(removeHexPrefix(proof.Nonce.String())) - if err != nil { - return false, err - } - } - if proof.Balance.String() != "0x0" { - balance, err = hex.DecodeString(removeHexPrefix(proof.Balance.String())) - if err != nil { - return false, err - } + value, err := rlp.EncodeToBytes([]interface{}{ + proof.Nonce, proof.Balance.ToInt(), proof.StorageHash, proof.CodeHash}) + if err != nil { + return false, err } - storageroot = proof.StorageHash.Bytes() - codehash = proof.CodeHash.Bytes() - values := RlpList{nonce, balance, storageroot, codehash} - return VerifyProof(proof.Address.Bytes(), values, proof.StateRoot.Bytes(), ProofToBytes(proof.AccountProof)) + return VerifyProof(proof.StateRoot, proof.Address.Bytes(), value, proof.AccountProof) } type proofList [][]byte @@ -90,312 +70,32 @@ func (n *proofList) Get(key []byte) ([]byte, error) { // It does not verify the account proof against the Ethereum StateHash. func VerifyEthStorageProof(proof *StorageResult, storageHash common.Hash) (bool, error) { var err error - var value RlpString - var key []byte - if proof.Value.String() != "0x0" { - value, err = hex.DecodeString(removeHexPrefix(proof.Value.String())) - if err != nil { - return false, err - } - } else { - value = RlpString{0x0} - } - if proof.Key != "0x0" { - key, err = hex.DecodeString(removeHexPrefix(proof.Key)) - if err != nil { - return false, err - } - } else { - return false, fmt.Errorf("proof key is nil (0x0)") - } - return VerifyProof(key, &value, storageHash.Bytes(), ProofToBytes(proof.Proof)) -} + var value []byte -// VerifyProof verifies an Ethereum Merkle tree storage proof. -// This function verifies a raw proof. -func VerifyProof(key []byte, value RlpObject, expectedHash []byte, proof [][]byte) (bool, error) { - if len(key) == 0 || len(proof) == 0 || value == nil { - return false, fmt.Errorf("key, value or proof are empty") - } - key = []byte(hex.EncodeToString(keccak256(key))) - valueRlpEncoded := RlpEncode(value) - ks := keyStream{bytes.NewBuffer(key)} - for i, p := range proof { - if ((i != 0 && len(p) < 32) || !bytes.Equal(expectedHash, keccak256(p))) && !bytes.Equal(expectedHash, p) { - return false, fmt.Errorf("expected hash does not match") - } - n := decodeRlpTrieNode(p) - switch len(n) { - case shortNode: - if len(n[0]) == 0 { - return false, fmt.Errorf("a short node is empty") - } - leaf, sharedNibbles, err := decodeHpHeader(n[0][0]) - if err != nil { - return false, fmt.Errorf("cannot decode leaf: %w", err) - } - sharedNibbles = append(sharedNibbles, []byte(hex.EncodeToString(n[0][1:]))...) - if len(sharedNibbles) == 0 { - return false, fmt.Errorf("no nibbles in short node") - } - if leaf { - if bytes.Equal(sharedNibbles, ks.key(-1)) && bytes.Equal(n[1], valueRlpEncoded) { - return true, nil - } - return false, fmt.Errorf("leaf node does not match value (%x != %x)", n[1], valueRlpEncoded) - } - if !bytes.Equal(sharedNibbles, ks.key(len(sharedNibbles))) { - return false, fmt.Errorf("key path does not match on short node") - } - expectedHash = n[1] - case branchNode: - if ks.Len() == 0 { - if bytes.Equal(n[16], valueRlpEncoded) { - return true, nil - } - return false, fmt.Errorf("key path ended but leaf value not found") - } - k := ks.index() - if k > 0x0f { - return false, fmt.Errorf("key path does not match on branch node") - } - expectedHash = n[k] - // If last node is a branchNode, proof of not existing value - if len(expectedHash) == 0 && bytes.Equal(valueRlpEncoded, []byte{0x00}) { - return true, nil - } - default: - return false, fmt.Errorf("unknown type of node") - } - } - return false, nil -} - -func decodeRlpTrieNode(data []byte) rlpNode { - var ( - dataLen = uint64(len(data)) - node rlpNode - ) - if dataLen == uint64(0) { - return nil - } - switch { - case data[0] >= 0xf8: - lenLen := int(data[0]) - 0xf7 - l, err := decodeLen(data[1:], lenLen) - if err != nil { - return nil - } - if dataLen != uint64(1)+uint64(lenLen)+l { - return nil - } - node = toList(data[1+lenLen:], l) - case data[0] >= 0xc0: - l := uint64(data[0]) - 0xc0 - if dataLen != uint64(1+l) { - return nil - } - node = toList(data[1:], l) - } - return node -} - -func decodeLen(data []byte, lenLen int) (uint64, error) { - if len(data) <= lenLen || lenLen > 8 { - return 0, errDecode - } - switch lenLen { - case 1: - return uint64(data[0]), nil - default: - start := int(8 - lenLen) - copy(lenBuf[:], nilBuf[:start]) - copy(lenBuf[start:], data[:lenLen]) - return binary.BigEndian.Uint64(lenBuf), nil - } -} - -func toList(data []byte, dataLen uint64) rlpNode { - var ( - node rlpNode - offset = uint64(0) - ) - for { - e, l, err := toString(data[offset:]) + v := big.Int(*proof.Value) + if v.BitLen() != 0 { + value, err = rlp.EncodeToBytes(&v) if err != nil { - return nil - } - node = append(node, e) - offset += l - if dataLen == offset { - break - } - if dataLen < offset { - return nil - } - } - nodeLen := uint64(len(node)) - if nodeLen != uint64(2) && nodeLen != uint64(17) { - return nil - } - return node -} - -func toString(data []byte) ([]byte, uint64, error) { - if len(data) == 0 { - return nil, 0, errDecode - } - switch { - case data[0] <= 0x7f: // a single byte - return data[0:1], 1, nil - case data[0] <= 0xb7: // string <= 55 - end := 1 + data[0] - 0x80 - return data[1:end], uint64(end), nil - case data[0] <= 0xbf: // string > 55 - lenLen := data[0] - 0xb7 - l, err := decodeLen(data[1:], int(lenLen)) - if err != nil { - return nil, 0, err + return false, err } - start := 1 + lenLen - end := uint64(start) + l - return data[start:end], end, nil - default: - return nil, 0, errDecode - } -} - -func keccak256(data ...[]byte) []byte { - h := sha3.NewLegacyKeccak256() - for _, d := range data { - h.Write(d) - } - return h.Sum(nil) -} - -func keccak256Hex(data ...[]byte) string { - return hex.EncodeToString(keccak256(data...)) -} - -func decodeHpHeader(b byte) (bool, []byte, error) { - switch b >> 4 { - case 0: - return false, []byte{}, nil - case 1: - return false, []byte{hexChar[b&0x0f]}, nil - case 2: - return true, []byte{}, nil - case 3: - return true, []byte{hexChar[b&0x0f]}, nil - default: - return false, []byte{}, errDecode } + return VerifyProof(storageHash, proof.Key, value, proof.Proof) } -func hexToIndex(c byte) (byte, error) { - switch { - case '0' <= c && c <= '9': - return c - '0', nil - case 'a' <= c && c <= 'f': - return c - 'a' + 10, nil - case 'A' <= c && c <= 'F': - return c - 'A' + 10, nil +// VerifyProof verifies that the path generated from key, following the nodes +// in proof leads to a leaf with value, where the hashes are correct up to the +// rootHash. +func VerifyProof(rootHash common.Hash, key []byte, value []byte, proof [][]byte) (bool, error) { + proofDB := NewMemDB() + for _, node := range proof { + key := crypto.Keccak256(node) + proofDB.Put(key, node) } - return 0, errDecode -} + path := crypto.Keccak256(key) -func (ks keyStream) index() byte { - b, err := ks.ReadByte() - if err != nil { - return 0x10 - } - i, err := hexToIndex(b) + res, err := trie.VerifyProof(rootHash, path, proofDB) if err != nil { - return 0x10 - } - return i -} - -func (ks keyStream) key(l int) []byte { - if l == -1 { - return ks.Buffer.Bytes() - } - return ks.Buffer.Next(l) -} - -func RlpEncode(o RlpObject) []byte { - return o.RlpEncode() -} - -type RlpObject interface { - RlpEncode() []byte -} - -type RlpString []byte - -func (s RlpString) RlpEncode() []byte { - var rlpBytes []byte - l := len(s) - if l == 1 && s[0] < 0x80 { - rlpBytes = append(rlpBytes, s[0]) - } else { - rlpBytes = append(rlpBytes, rlpLength(l, 0x80)...) - rlpBytes = append(rlpBytes, s...) - } - return rlpBytes -} - -type RlpList []RlpObject - -func (l RlpList) RlpEncode() []byte { - var rlpBytes []byte - for _, item := range l { - rlpBytes = append(rlpBytes, item.RlpEncode()...) - } - length := rlpLength(len(rlpBytes), 0xc0) - return append(length, rlpBytes...) -} - -func rlpLength(dataLen int, offset byte) []byte { - if dataLen < 56 { - return []byte{byte(dataLen) + offset} - } else if dataLen < math.MaxInt32 { - var output []byte - b := toBinary(dataLen) - output = append(output, byte(len(b)+int(offset)+55)) - return append(output, b...) - } else { - return []byte{} - } -} - -func toBinary(d int) []byte { - var b []byte - for d > 0 { - b = append([]byte{byte(d % 256)}, b...) - d /= 256 - } - return b -} - -func removeHexPrefix(s string) string { - if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { - s = s[2:] - } - if len(s)&1 == 1 { - s = "0" + s - } - return s -} - -func ProofToBytes(proof []string) [][]byte { - var r [][]byte - for _, n := range proof { - d, err := hex.DecodeString(removeHexPrefix(n)) - if err != nil { - return [][]byte{} - } - r = append(r, d) + return false, err } - return r + return bytes.Equal(value, res), nil } diff --git a/ethstorageproof/ethstorageproof_test.go b/ethstorageproof/ethstorageproof_test.go index a80dff8..bc5a0ee 100644 --- a/ethstorageproof/ethstorageproof_test.go +++ b/ethstorageproof/ethstorageproof_test.go @@ -1,18 +1,19 @@ package ethstorageproof import ( - "bytes" "encoding/hex" "encoding/json" - "fmt" - "reflect" + "strings" "testing" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" ) func TestVerify(t *testing.T) { tests := []struct { key []byte - value []byte + value interface{} proof [][]byte verify bool }{ @@ -44,7 +45,7 @@ func TestVerify(t *testing.T) { }, { toBytes("0x0000000000000000000000000000000000000000000000000000000000000000"), - toBytes("0x9"), + uint32(0x9), proofToBytes([]string{ "0xf8518080a05d2423f2a53dd794285a39f2c7ba5c0c24cba719d05e3bdd1f5eefae445b34358080808080808080a01ec473dfa012cb440907fa4f2c34be3e733c92430d98a48831700bc8ab159f5d8080808080", @@ -91,108 +92,22 @@ func TestVerify(t *testing.T) { } for i, tt := range tests { - if vp, err := VerifyProof(tt.key, RlpString(tt.value), keccak256(tt.proof[0]), tt.proof); vp != tt.verify { - t.Errorf("testcase %d: want %v, got %v (err: %s)\n", i, tt.verify, !tt.verify, err) - } - } -} - -func TestDecodeRlpTrieNode(t *testing.T) { - tests := []struct { - data []byte - length int - index int - expect []byte - }{ - { - []byte{0xc5, 0x83, 'c', 'a', 't', 0x80}, - 2, - -1, - []byte{}, - }, - { - []byte{0xc8, 0x83, 'c', 'a', 't', 0x83, 'd', 'o', 'g'}, - 2, - -1, - []byte{}, - }, - { - []byte{0xC0}, - 0, - -1, - []byte{}, - }, - { - []byte{0xd1, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q'}, - 17, - -1, - []byte{}, - }, - { - []byte{0xd1, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r'}, - 0, - -1, - []byte{}, - }, - { - []byte{0xd1, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 0x80}, - 17, - -1, - []byte{}, - }, - { - []byte{0xd1, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 0x80}, - 17, - 0, - []byte{'a'}, - }, - { - []byte{0xd1, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 0x80}, - 17, - 16, - []byte{}, - }, - { - []byte{0xd3, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 0x82, 'm', 'n', 'o', 'p', 0x80, 'x'}, - 17, - 12, - []byte{'m', 'n'}, - }, - } - for i, tt := range tests { - n := decodeRlpTrieNode(tt.data) - if len(n) != tt.length { - t.Errorf("testcase %d: want %d, got %d\n", i, tt.length, len(n)) - continue + value, err := rlp.EncodeToBytes(tt.value) + if err != nil { + panic(err) } - if tt.index != -1 { - if !bytes.Equal(tt.expect, n[tt.index]) { - t.Errorf("testcase %d: want %v, got %v\n", i, tt.expect, n[tt.index]) - } + if vp, err := VerifyProof(crypto.Keccak256Hash(tt.proof[0]), tt.key, + value, tt.proof); vp != tt.verify { + t.Errorf("testcase %d: want %v, got %v (err: %v)\n", i, tt.verify, !tt.verify, err) } } } -func TestKeccak256(t *testing.T) { - tests := []struct { - msg string - h string - }{ - { - "abc", - "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", - }, - { - "aergo", - "e98bb03ab37161f8bbfe131f711dcccf3002a9cd9ec31bbd52edf181f7ab09a0", - }, - } - for _, tt := range tests { - h := keccak256Hex([]byte(tt.msg)) - if tt.h != h { - t.Errorf("want %s, got %s\n", tt.h, h) - } +func removeHexPrefix(s string) string { + if strings.HasPrefix(s, "0x") { + return s[2:] } + return s } func toBytes(s string) []byte { @@ -212,101 +127,6 @@ func proofToBytes(proof []string) [][]byte { return r } -func Test_rlpEncode(t *testing.T) { - l := "Lorem ipsum dolor sit amet, consectetur adipisicing elit" - type args struct { - o RlpObject - } - tests := []struct { - name string - args args - want []byte - }{ - { - `"dog"`, - args{RlpString("dog")}, - []byte{0x83, 'd', 'o', 'g'}, - }, - { - `["cat","dog"]`, - args{RlpList{RlpString("cat"), RlpString("dog")}}, - []byte{0xc8, 0x83, 'c', 'a', 't', 0x83, 'd', 'o', 'g'}, - }, - { - `""`, - args{RlpString("")}, - []byte{0x80}, - }, - { - `[]`, - args{RlpList{}}, - []byte{0xc0}, - }, - { - `\x00`, - args{RlpString{0x00}}, - []byte{0x00}, - }, - { - `\x0f`, - args{RlpString{0x0f}}, - []byte{0x0f}, - }, - { - `\x0f\x00`, - args{RlpString{0x04, 0x00}}, - []byte{0x82, 0x04, 0x00}, - }, - { - `[[],[[]],[[],[[]]]]`, - args{RlpList{RlpList{}, RlpList{RlpList{}}, RlpList{RlpList{}, RlpList{RlpList{}}}}}, - []byte{0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0}, - }, - { - fmt.Sprintf(`"%s"`, l), - args{RlpString(l)}, - append([]byte{0xb8, 0x38}, []byte(l)...), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := RlpEncode(tt.args.o); !reflect.DeepEqual(got, tt.want) { - t.Errorf("rlpEncode() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_rlpLength(t *testing.T) { - type args struct { - dataLen int - offset byte - } - tests := []struct { - name string - args args - want []byte - }{ - { - "string-1024", - args{1024, 0x80}, - []byte{0xb9, 0x04, 0x00}, - }, - { - "list-1024", - args{1024, 0xc0}, - []byte{0xf9, 0x04, 0x00}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := rlpLength(tt.args.dataLen, tt.args.offset); !reflect.DeepEqual(got, tt.want) { - t.Errorf("rlpLength() = %v, want %v", got, tt.want) - } - }) - } -} - var EIP1186Proof = ` { "stateRoot": "0xfd725b0325c2bda54cf7e33e3b9f6bc9b7927beb7ba6a2ef5feef7d20b394168", @@ -350,15 +170,15 @@ func Test_eipProof(t *testing.T) { t.Error(err) } if ok, err := VerifyEIP1186(&sp); !ok { - t.Errorf("proof must be valid but it is invalid: (%s)", err) + t.Errorf("proof must be valid but it is invalid: (%v)", err) } if ok, err := VerifyEthAccountProof(&sp); !ok { - t.Errorf("proof must be valid but it is invalid: (%s)", err) + t.Errorf("proof must be valid but it is invalid: (%v)", err) } if ok, err := VerifyEthStorageProof(&sp.StorageProof[0], sp.StorageHash); !ok { - t.Errorf("proof must be valid but it is invalid: (%s)", err) + t.Errorf("proof must be valid but it is invalid: (%v)", err) } - sp.StorageProof[0].Key = "49c4c8b2db715e9f7e1d3306b9f6860a389635dfb3943db23f1005544a50fbb2" + sp.StorageProof[0].Key = toBytes("0x49c4c8b2db715e9f7e1d3306b9f6860a389635dfb3943db23f1005544a50fbb2") if ok, _ := VerifyEIP1186(&sp); ok { t.Errorf("proof must be invalid but it is valid") } diff --git a/ethstorageproof/types.go b/ethstorageproof/types.go index edcf7b4..3eac40f 100644 --- a/ethstorageproof/types.go +++ b/ethstorageproof/types.go @@ -1,25 +1,126 @@ package ethstorageproof import ( + "bytes" + "encoding/hex" + "encoding/json" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ) +// BytesHex marshals/unmarshals as a JSON string in hex with 0x prefix. The empty +// slice marshals as "0x". +type BytesHex []byte + +// MarshalText implements encoding.TextMarshaler +func (b BytesHex) MarshalText() ([]byte, error) { + result := make([]byte, len(b)*2+2) + copy(result, `0x`) + hex.Encode(result[2:], b) + return result, nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (b *BytesHex) UnmarshalText(input []byte) error { + if bytes.HasPrefix(input, []byte("0x")) { + input = input[2:] + } + dec := make([]byte, len(input)/2) + if _, err := hex.Decode(dec, input); err != nil { + return err + } else { + *b = dec + return nil + } +} + +// SliceBytesHex marshals/unmarshals as a JSON vector of strings with in hex with +// 0x prefix. +type SliceBytesHex [][]byte + +// MarshalText implements encoding.TextMarshaler +func (s SliceBytesHex) MarshalJSON() ([]byte, error) { + bs := make([]BytesHex, len(s)) + for i, b := range s { + bs[i] = b + } + return json.Marshal(bs) +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (s *SliceBytesHex) UnmarshalJSON(data []byte) error { + var bs []BytesHex + if err := json.Unmarshal(data, &bs); err != nil { + return err + } + *s = make([][]byte, len(bs)) + for i, b := range bs { + (*s)[i] = b + } + return nil +} + +// StorageProof allows unmarshaling the object returned by `eth_getProof`: +// https://eips.ethereum.org/EIPS/eip-1186 type StorageProof struct { StateRoot common.Hash `json:"stateRoot"` Height *big.Int `json:"height"` Address common.Address `json:"address"` - AccountProof []string `json:"accountProof"` + AccountProof SliceBytesHex `json:"accountProof"` Balance *hexutil.Big `json:"balance"` CodeHash common.Hash `json:"codeHash"` Nonce hexutil.Uint64 `json:"nonce"` StorageHash common.Hash `json:"storageHash"` StorageProof []StorageResult `json:"storageProof"` } + +// StorageResult is an object from StorageProof that contains a proof of +// storage. type StorageResult struct { - Key string `json:"key"` - Value *hexutil.Big `json:"value"` - Proof []string `json:"proof"` + Key BytesHex `json:"key"` + Value *hexutil.Big `json:"value"` + Proof SliceBytesHex `json:"proof"` +} + +// MemDB is an ethdb.KeyValueReader implementation which is not thread safe and +// assumes that all keys are common.Hash. +type MemDB struct { + kvs map[common.Hash][]byte +} + +// NewMemDB creates a new empty MemDB +func NewMemDB() *MemDB { + return &MemDB{ + kvs: make(map[common.Hash][]byte), + } +} + +// Has returns true if the MemBD contains the key +func (m *MemDB) Has(key []byte) (bool, error) { + var h common.Hash + copy(h[:], key) + _, ok := m.kvs[h] + return ok, nil +} + +// Get returns the value of the key, or nit if it's not found +func (m *MemDB) Get(key []byte) ([]byte, error) { + var h common.Hash + copy(h[:], key) + value, ok := m.kvs[h] + if ok { + return value, nil + } else { + return nil, nil + } +} + +// Put sets or updates the value at key +func (m *MemDB) Put(key []byte, value []byte) error { + var h common.Hash + copy(h[:], key) + m.kvs[h] = value + return nil } diff --git a/examples/verify-top-50-erc20/test.sh b/examples/verify-top-50-erc20/test.sh index 0f64801..5dbef83 100755 --- a/examples/verify-top-50-erc20/test.sh +++ b/examples/verify-top-50-erc20/test.sh @@ -1,4 +1,6 @@ #!/bin/bash +set -e + cat 50-top-contracts-with-holder.txt | while read l; do c=$(echo $l| cut -d " " -f1) h=$(echo $l|cut -d " " -f2) diff --git a/token/mapbased/mapbased.go b/token/mapbased/mapbased.go index ff91dff..6c7c310 100644 --- a/token/mapbased/mapbased.go +++ b/token/mapbased/mapbased.go @@ -143,7 +143,7 @@ func VerifyProof(holder common.Address, storageRoot common.Hash, if proof.Value == nil { return fmt.Errorf("value is nil") } - if len(proof.Key) != 64 { + if len(proof.Key) != 32 { return fmt.Errorf("key length is wrong (%d)", len(proof.Key)) } if len(proof.Proof) < 4 { @@ -158,12 +158,8 @@ func VerifyProof(holder common.Address, storageRoot common.Hash, if err != nil { return err } - proofKey, err := hex.DecodeString(helpers.TrimHex(proof.Key)) - if err != nil { - return err - } - if !bytes.Equal(keySlot[:], proofKey) { - return fmt.Errorf("proof key and leafData do not match (%x != %x)", keySlot, proofKey) + if !bytes.Equal(keySlot[:], proof.Key) { + return fmt.Errorf("proof key and leafData do not match (%x != %x)", keySlot, proof.Key) } // Check value balances matches diff --git a/token/minime/helpers.go b/token/minime/helpers.go index 96863f9..923085e 100644 --- a/token/minime/helpers.go +++ b/token/minime/helpers.go @@ -23,7 +23,7 @@ func VerifyProof(holder common.Address, storageRoot common.Hash, if p.Value == nil { return fmt.Errorf("value is nil") } - if len(p.Key) != 64 { + if len(p.Key) != 32 { return fmt.Errorf("key length is wrong (%d)", len(p.Key)) } if len(p.Proof) < 4 { @@ -120,7 +120,7 @@ func ParseMinimeValue(hexValue string, decimals int) (*big.Float, *big.Int, *big // CheckMinimeKeys checks the validity of a storage proof key (RLP hexadecimal string) for a // specific token holder address. As MiniMe includes checkpoints and each one adds +1 to // the key, there is a maximum hardcoded tolerance of 2^16 positions for the key. -func CheckMinimeKeys(hexKey1, hexKey2 string, holder common.Address, mapIndexSlot int) error { +func CheckMinimeKeys(key1, key2 []byte, holder common.Address, mapIndexSlot int) error { mapSlot, err := helpers.GetMapSlot(holder.Hex(), mapIndexSlot) if err != nil { return err @@ -131,17 +131,17 @@ func CheckMinimeKeys(hexKey1, hexKey2 string, holder common.Address, mapIndexSlo } holderMapUindex := new(big.Int).SetBytes(vf[:]).Uint64() - rawKey1, err := hex.DecodeString(helpers.TrimHex(hexKey1)) - if err != nil { - return err - } - key1Uindex := new(big.Int).SetBytes(rawKey1).Uint64() + // rawKey1, err := hex.DecodeString(hexKey1) + // if err != nil { + // return err + // } + key1Uindex := new(big.Int).SetBytes(key1).Uint64() - rawKey2, err := hex.DecodeString(helpers.TrimHex(hexKey2)) - if err != nil { - return err - } - if key1Uindex+1 != new(big.Int).SetBytes(rawKey2).Uint64() { + // rawKey2, err := hex.DecodeString(hexKey2) + // if err != nil { + // return err + // } + if key1Uindex+1 != new(big.Int).SetBytes(key2).Uint64() { return fmt.Errorf("keys are not consecutive") }