github.com/ethereum/go-ethereum@v1.16.1/trie/utils/verkle.go (about) 1 // Copyright 2023 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package utils 18 19 import ( 20 "encoding/binary" 21 "sync" 22 23 "github.com/crate-crypto/go-ipa/bandersnatch/fr" 24 "github.com/ethereum/go-ethereum/common/lru" 25 "github.com/ethereum/go-ethereum/metrics" 26 "github.com/ethereum/go-verkle" 27 "github.com/holiman/uint256" 28 ) 29 30 const ( 31 BasicDataLeafKey = 0 32 CodeHashLeafKey = 1 33 34 BasicDataVersionOffset = 0 35 BasicDataCodeSizeOffset = 5 36 BasicDataNonceOffset = 8 37 BasicDataBalanceOffset = 16 38 ) 39 40 var ( 41 zero = uint256.NewInt(0) 42 verkleNodeWidthLog2 = 8 43 headerStorageOffset = uint256.NewInt(64) 44 codeOffset = uint256.NewInt(128) 45 verkleNodeWidth = uint256.NewInt(256) 46 codeStorageDelta = uint256.NewInt(0).Sub(codeOffset, headerStorageOffset) 47 mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(1), 248-uint(verkleNodeWidthLog2)) 48 49 index0Point *verkle.Point // pre-computed commitment of polynomial [2+256*64] 50 51 // cacheHitGauge is the metric to track how many cache hit occurred. 52 cacheHitGauge = metrics.NewRegisteredGauge("trie/verkle/cache/hit", nil) 53 54 // cacheMissGauge is the metric to track how many cache miss occurred. 55 cacheMissGauge = metrics.NewRegisteredGauge("trie/verkle/cache/miss", nil) 56 ) 57 58 func init() { 59 // The byte array is the Marshalled output of the point computed as such: 60 // 61 // var ( 62 // config = verkle.GetConfig() 63 // fr verkle.Fr 64 // ) 65 // verkle.FromLEBytes(&fr, []byte{2, 64}) 66 // point := config.CommitToPoly([]verkle.Fr{fr}, 1) 67 index0Point = new(verkle.Point) 68 err := index0Point.SetBytes([]byte{34, 25, 109, 242, 193, 5, 144, 224, 76, 52, 189, 92, 197, 126, 9, 145, 27, 152, 199, 130, 165, 3, 210, 27, 193, 131, 142, 28, 110, 26, 16, 191}) 69 if err != nil { 70 panic(err) 71 } 72 } 73 74 // PointCache is the LRU cache for storing evaluated address commitment. 75 type PointCache struct { 76 lru lru.BasicLRU[string, *verkle.Point] 77 lock sync.RWMutex 78 } 79 80 // NewPointCache returns the cache with specified size. 81 func NewPointCache(maxItems int) *PointCache { 82 return &PointCache{ 83 lru: lru.NewBasicLRU[string, *verkle.Point](maxItems), 84 } 85 } 86 87 // Get returns the cached commitment for the specified address, or computing 88 // it on the flight. 89 func (c *PointCache) Get(addr []byte) *verkle.Point { 90 c.lock.Lock() 91 defer c.lock.Unlock() 92 93 p, ok := c.lru.Get(string(addr)) 94 if ok { 95 cacheHitGauge.Inc(1) 96 return p 97 } 98 cacheMissGauge.Inc(1) 99 p = evaluateAddressPoint(addr) 100 c.lru.Add(string(addr), p) 101 return p 102 } 103 104 // GetStem returns the first 31 bytes of the tree key as the tree stem. It only 105 // works for the account metadata whose treeIndex is 0. 106 func (c *PointCache) GetStem(addr []byte) []byte { 107 p := c.Get(addr) 108 return pointToHash(p, 0)[:31] 109 } 110 111 // GetTreeKey performs both the work of the spec's get_tree_key function, and that 112 // of pedersen_hash: it builds the polynomial in pedersen_hash without having to 113 // create a mostly zero-filled buffer and "type cast" it to a 128-long 16-byte 114 // array. Since at most the first 5 coefficients of the polynomial will be non-zero, 115 // these 5 coefficients are created directly. 116 func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte { 117 if len(address) < 32 { 118 var aligned [32]byte 119 address = append(aligned[:32-len(address)], address...) 120 } 121 // poly = [2+256*64, address_le_low, address_le_high, tree_index_le_low, tree_index_le_high] 122 var poly [5]fr.Element 123 124 // 32-byte address, interpreted as two little endian 125 // 16-byte numbers. 126 verkle.FromLEBytes(&poly[1], address[:16]) 127 verkle.FromLEBytes(&poly[2], address[16:]) 128 129 // treeIndex must be interpreted as a 32-byte aligned little-endian integer. 130 // e.g: if treeIndex is 0xAABBCC, we need the byte representation to be 0xCCBBAA00...00. 131 // poly[3] = LE({CC,BB,AA,00...0}) (16 bytes), poly[4]=LE({00,00,...}) (16 bytes). 132 // 133 // To avoid unnecessary endianness conversions for go-ipa, we do some trick: 134 // - poly[3]'s byte representation is the same as the *top* 16 bytes (trieIndexBytes[16:]) of 135 // 32-byte aligned big-endian representation (BE({00,...,AA,BB,CC})). 136 // - poly[4]'s byte representation is the same as the *low* 16 bytes (trieIndexBytes[:16]) of 137 // the 32-byte aligned big-endian representation (BE({00,00,...}). 138 trieIndexBytes := treeIndex.Bytes32() 139 verkle.FromBytes(&poly[3], trieIndexBytes[16:]) 140 verkle.FromBytes(&poly[4], trieIndexBytes[:16]) 141 142 cfg := verkle.GetConfig() 143 ret := cfg.CommitToPoly(poly[:], 0) 144 145 // add a constant point corresponding to poly[0]=[2+256*64]. 146 ret.Add(ret, index0Point) 147 148 return pointToHash(ret, subIndex) 149 } 150 151 // GetTreeKeyWithEvaluatedAddress is basically identical to GetTreeKey, the only 152 // difference is a part of polynomial is already evaluated. 153 // 154 // Specifically, poly = [2+256*64, address_le_low, address_le_high] is already 155 // evaluated. 156 func GetTreeKeyWithEvaluatedAddress(evaluated *verkle.Point, treeIndex *uint256.Int, subIndex byte) []byte { 157 var poly [5]fr.Element 158 159 // little-endian, 32-byte aligned treeIndex 160 var index [32]byte 161 for i := 0; i < len(treeIndex); i++ { 162 binary.LittleEndian.PutUint64(index[i*8:(i+1)*8], treeIndex[i]) 163 } 164 verkle.FromLEBytes(&poly[3], index[:16]) 165 verkle.FromLEBytes(&poly[4], index[16:]) 166 167 cfg := verkle.GetConfig() 168 ret := cfg.CommitToPoly(poly[:], 0) 169 170 // add the pre-evaluated address 171 ret.Add(ret, evaluated) 172 173 return pointToHash(ret, subIndex) 174 } 175 176 // BasicDataKey returns the verkle tree key of the basic data field for 177 // the specified account. 178 func BasicDataKey(address []byte) []byte { 179 return GetTreeKey(address, zero, BasicDataLeafKey) 180 } 181 182 // CodeHashKey returns the verkle tree key of the code hash field for 183 // the specified account. 184 func CodeHashKey(address []byte) []byte { 185 return GetTreeKey(address, zero, CodeHashLeafKey) 186 } 187 188 func codeChunkIndex(chunk *uint256.Int) (*uint256.Int, byte) { 189 var ( 190 chunkOffset = new(uint256.Int).Add(codeOffset, chunk) 191 treeIndex, subIndexMod = new(uint256.Int).DivMod(chunkOffset, verkleNodeWidth, new(uint256.Int)) 192 ) 193 return treeIndex, byte(subIndexMod.Uint64()) 194 } 195 196 // CodeChunkKey returns the verkle tree key of the code chunk for the 197 // specified account. 198 func CodeChunkKey(address []byte, chunk *uint256.Int) []byte { 199 treeIndex, subIndex := codeChunkIndex(chunk) 200 return GetTreeKey(address, treeIndex, subIndex) 201 } 202 203 func StorageIndex(storageKey []byte) (*uint256.Int, byte) { 204 // If the storage slot is in the header, we need to add the header offset. 205 var key uint256.Int 206 key.SetBytes(storageKey) 207 if key.Cmp(codeStorageDelta) < 0 { 208 // This addition is always safe; it can't ever overflow since pos<codeStorageDelta. 209 key.Add(headerStorageOffset, &key) 210 211 // In this branch, the tree-index is zero since we're in the account header, 212 // and the sub-index is the LSB of the modified storage key. 213 return zero, byte(key[0] & 0xFF) 214 } 215 // If the storage slot is in the main storage, we need to add the main storage offset. 216 217 // The first MAIN_STORAGE_OFFSET group will see its 218 // first 64 slots unreachable. This is either a typo in the 219 // spec or intended to conserve the 256-u256 220 // alignment. If we decide to ever access these 64 221 // slots, uncomment this. 222 // // Get the new offset since we now know that we are above 64. 223 // pos.Sub(&pos, codeStorageDelta) 224 // suffix := byte(pos[0] & 0xFF) 225 suffix := storageKey[len(storageKey)-1] 226 227 // We first divide by VerkleNodeWidth to create room to avoid an overflow next. 228 key.Rsh(&key, uint(verkleNodeWidthLog2)) 229 230 // We add mainStorageOffset/VerkleNodeWidth which can't overflow. 231 key.Add(&key, mainStorageOffsetLshVerkleNodeWidth) 232 233 // The sub-index is the LSB of the original storage key, since mainStorageOffset 234 // doesn't affect this byte, so we can avoid masks or shifts. 235 return &key, suffix 236 } 237 238 // StorageSlotKey returns the verkle tree key of the storage slot for the 239 // specified account. 240 func StorageSlotKey(address []byte, storageKey []byte) []byte { 241 treeIndex, subIndex := StorageIndex(storageKey) 242 return GetTreeKey(address, treeIndex, subIndex) 243 } 244 245 // BasicDataKeyWithEvaluatedAddress returns the verkle tree key of the basic data 246 // field for the specified account. The difference between BasicDataKey is the 247 // address evaluation is already computed to minimize the computational overhead. 248 func BasicDataKeyWithEvaluatedAddress(evaluated *verkle.Point) []byte { 249 return GetTreeKeyWithEvaluatedAddress(evaluated, zero, BasicDataLeafKey) 250 } 251 252 // CodeHashKeyWithEvaluatedAddress returns the verkle tree key of the code 253 // hash for the specified account. The difference between CodeHashKey is the 254 // address evaluation is already computed to minimize the computational overhead. 255 func CodeHashKeyWithEvaluatedAddress(evaluated *verkle.Point) []byte { 256 return GetTreeKeyWithEvaluatedAddress(evaluated, zero, CodeHashLeafKey) 257 } 258 259 // CodeChunkKeyWithEvaluatedAddress returns the verkle tree key of the code 260 // chunk for the specified account. The difference between CodeChunkKey is the 261 // address evaluation is already computed to minimize the computational overhead. 262 func CodeChunkKeyWithEvaluatedAddress(addressPoint *verkle.Point, chunk *uint256.Int) []byte { 263 treeIndex, subIndex := codeChunkIndex(chunk) 264 return GetTreeKeyWithEvaluatedAddress(addressPoint, treeIndex, subIndex) 265 } 266 267 // StorageSlotKeyWithEvaluatedAddress returns the verkle tree key of the storage 268 // slot for the specified account. The difference between StorageSlotKey is the 269 // address evaluation is already computed to minimize the computational overhead. 270 func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []byte) []byte { 271 treeIndex, subIndex := StorageIndex(storageKey) 272 return GetTreeKeyWithEvaluatedAddress(evaluated, treeIndex, subIndex) 273 } 274 275 func pointToHash(evaluated *verkle.Point, suffix byte) []byte { 276 retb := verkle.HashPointToBytes(evaluated) 277 retb[31] = suffix 278 return retb[:] 279 } 280 281 func evaluateAddressPoint(address []byte) *verkle.Point { 282 if len(address) < 32 { 283 var aligned [32]byte 284 address = append(aligned[:32-len(address)], address...) 285 } 286 var poly [3]fr.Element 287 288 // 32-byte address, interpreted as two little endian 289 // 16-byte numbers. 290 verkle.FromLEBytes(&poly[1], address[:16]) 291 verkle.FromLEBytes(&poly[2], address[16:]) 292 293 cfg := verkle.GetConfig() 294 ret := cfg.CommitToPoly(poly[:], 0) 295 296 // add a constant point 297 ret.Add(ret, index0Point) 298 return ret 299 }