@@ -6,6 +6,7 @@ package main
66import (
77 "bytes"
88 "context"
9+ "errors"
910 "fmt"
1011
1112 "github.com/ethereum/go-ethereum/common"
@@ -57,16 +58,11 @@ func fetchReceiptFromBlock(
5758 switch len (node ) {
5859 case 17 :
5960 // We hit a branch node, which has 16 children and a value.
60- if len (currentPath ) > = len (targetNibbles ) {
61+ if len (currentPath ) = = len (targetNibbles ) {
6162 // A branch node's 17th item could be the value, so we check if it contains the receipt.
6263 if valueBytes , ok := node [16 ].([]byte ); ok && len (valueBytes ) > 0 {
6364 // This branch node has the actual value as the last item, so we decode the receipt
64- receipt := new (types.Receipt )
65- receiptData := bytes .NewBuffer (valueBytes )
66- if err = rlp .Decode (receiptData , & receipt ); err != nil {
67- return nil , fmt .Errorf ("failed to decode receipt: %w" , err )
68- }
69- return receipt , nil
65+ return decodeReceipt (valueBytes )
7066 }
7167 return nil , fmt .Errorf ("no receipt found at target key" )
7268 }
@@ -85,37 +81,30 @@ func fetchReceiptFromBlock(
8581 return nil , fmt .Errorf ("invalid key path in node" )
8682 }
8783 // Check if it is a leaf or extension node.
88- if isLeaf (keyPath ) {
84+ leafKey := extractKeyNibbles (keyPath )
85+ expectedPath := make ([]byte , 0 )
86+ expectedPath = append (expectedPath , currentPath ... )
87+ expectedPath = append (expectedPath , leafKey ... )
88+
89+ leaf , err := isLeaf (keyPath )
90+ if err != nil {
91+ return nil , err
92+ }
93+ if leaf {
8994 // Check that the keyPath matches the target nibbles,
9095 // otherwise, the receipt does not exist in the trie.
91- leafKey := extractKeyNibbles (keyPath )
92- expectedPath := make ([]byte , 0 )
93- expectedPath = append (expectedPath , currentPath ... )
94- expectedPath = append (expectedPath , leafKey ... )
9596 if ! bytes .Equal (expectedPath , targetNibbles ) {
9697 return nil , fmt .Errorf ("leaf key does not match target nibbles" )
9798 }
98-
99- receipt := new (types.Receipt )
10099 rawData , ok := node [1 ].([]byte )
101100 if ! ok {
102101 return nil , fmt .Errorf ("invalid receipt data in leaf node" )
103102 }
104- receiptData := bytes .NewBuffer (rawData )
105- if err = rlp .Decode (receiptData , & receipt ); err != nil {
106- return nil , fmt .Errorf ("failed to decode receipt: %w" , err )
107- }
108- return receipt , nil
103+ return decodeReceipt (rawData )
109104 }
110105 // If the node is not a leaf node, it is an extension node.
111- // We extract the extension key path and append it to our current path.
112- extKey := extractKeyNibbles (keyPath )
113- newPath := make ([]byte , 0 )
114- newPath = append (newPath , currentPath ... )
115- newPath = append (newPath , extKey ... )
116-
117106 // Check if our target key matches this extension path.
118- if len (newPath ) > len (targetNibbles ) || ! bytes .Equal (newPath , targetNibbles [:len (newPath )]) {
107+ if len (expectedPath ) > len (targetNibbles ) || ! bytes .Equal (expectedPath , targetNibbles [:len (expectedPath )]) {
119108 return nil , fmt .Errorf ("extension path mismatch" )
120109 }
121110 nextNodeBytes , ok := node [1 ].([]byte )
@@ -124,14 +113,15 @@ func fetchReceiptFromBlock(
124113 }
125114 // We navigate to the next node in the trie.
126115 currentNodeHash = common .BytesToHash (nextNodeBytes )
127- currentPath = newPath
116+ currentPath = expectedPath
128117 default :
129118 return nil , fmt .Errorf ("invalid node structure: unexpected length %d" , len (node ))
130119 }
131120 }
132121}
133122
134123// Converts a byte slice key into a slice of nibbles (4-bit values).
124+ // Keys are encoded in big endian format, which is required by Ethereum MPTs.
135125func keyToNibbles (key []byte ) []byte {
136126 nibbles := make ([]byte , len (key )* 2 )
137127 for i , b := range key {
@@ -146,30 +136,30 @@ func extractKeyNibbles(keyPath []byte) []byte {
146136 if len (keyPath ) == 0 {
147137 return nil
148138 }
149-
150- firstByte := keyPath [0 ]
151- isOdd := (firstByte & 0x10 ) != 0
152-
153- var nibbles []byte
154- if isOdd {
155- // Odd length: first nibble is in the first byte.
156- nibbles = append (nibbles , firstByte & 0x0f )
157- keyPath = keyPath [1 :]
158- } else {
159- keyPath = keyPath [1 :]
160- }
161- // Convert remaining bytes to nibbles.
162- for _ , b := range keyPath {
163- nibbles = append (nibbles , b >> 4 )
164- nibbles = append (nibbles , b & 0x0f )
139+ nibbles := keyToNibbles (keyPath )
140+ if nibbles [0 ]& 1 != 0 {
141+ return nibbles [1 :]
165142 }
166-
167- return nibbles
143+ return nibbles [2 :]
168144}
169145
170- func isLeaf (keyPath []byte ) bool {
146+ func isLeaf (keyPath []byte ) ( bool , error ) {
171147 firstByte := keyPath [0 ]
172148 firstNibble := firstByte >> 4
173149 // 2 or 3 indicates leaf, while 0 or 1 indicates extension nodes in the Ethereum MPT specification.
174- return firstNibble >= 2
150+ if firstNibble > 3 {
151+ return false , errors .New ("first nibble cannot be greater than 3" )
152+ }
153+ return firstNibble >= 2 , nil
154+ }
155+
156+ func decodeReceipt (data []byte ) (* types.Receipt , error ) {
157+ if len (data ) == 0 {
158+ return nil , errors .New ("empty data cannot be decoded into receipt" )
159+ }
160+ rpt := new (types.Receipt )
161+ if err := rpt .UnmarshalBinary (data ); err != nil {
162+ return nil , err
163+ }
164+ return rpt , nil
175165}
0 commit comments