diff --git a/core/state/state_object.go b/core/state/state_object.go index 5c0f80c76311..11dcd92a9419 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" trieUtils "github.com/ethereum/go-ethereum/trie/utils" + "github.com/gballet/go-verkle" "github.com/holiman/uint256" ) @@ -72,6 +73,8 @@ type stateObject struct { data types.StateAccount db *StateDB + pointEval *verkle.Point + // DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs @@ -134,6 +137,7 @@ func newObject(db *StateDB, address common.Address, data types.StateAccount) *st db: db, address: address, addrHash: crypto.Keccak256Hash(address[:]), + pointEval: trieUtils.EvaluateAddressPoint(address.Bytes()), data: data, originStorage: make(Storage), pendingStorage: make(Storage), @@ -282,9 +286,8 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has if err != nil { return common.Hash{} } - addr := s.Address() loc := new(uint256.Int).SetBytes(key[:]) - index := trieUtils.GetTreeKeyStorageSlot(addr[:], loc) + index := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(s.pointEval, loc) if len(enc) > 0 { s.db.Witness().SetLeafValue(index, value.Bytes()) } else { @@ -391,7 +394,7 @@ func (s *stateObject) updateTrie(db Database) Trie { var v []byte if (value == common.Hash{}) { if tr.IsVerkle() { - k := trieUtils.GetTreeKeyStorageSlot(s.address[:], new(uint256.Int).SetBytes(key[:])) + k := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(s.pointEval, new(uint256.Int).SetBytes(key[:])) s.setError(tr.TryDelete(k)) //s.db.db.TrieDB().DiskDB().Delete(append(s.address[:], key[:]...)) } else { @@ -404,7 +407,7 @@ func (s *stateObject) updateTrie(db Database) Trie { if !tr.IsVerkle() { s.setError(tr.TryUpdate(key[:], v)) } else { - k := trieUtils.GetTreeKeyStorageSlot(s.address[:], new(uint256.Int).SetBytes(key[:])) + k := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(s.pointEval, new(uint256.Int).SetBytes(key[:])) // Update the trie, with v as a value s.setError(tr.TryUpdate(k, value[:])) } diff --git a/core/vm/contract.go b/core/vm/contract.go index 51213958536b..d2074440af27 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -20,6 +20,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/trie/utils" + "github.com/gballet/go-verkle" "github.com/holiman/uint256" ) @@ -49,6 +51,7 @@ type Contract struct { CallerAddress common.Address caller ContractRef self ContractRef + addressPoint *verkle.Point jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis. analysis bitvec // Locally cached result of JUMPDEST analysis @@ -175,6 +178,14 @@ func (c *Contract) Address() common.Address { return c.self.Address() } +func (c *Contract) AddressPoint() *verkle.Point { + if c.addressPoint == nil { + c.addressPoint = utils.EvaluateAddressPoint(c.Address().Bytes()) + } + + return c.addressPoint +} + // Value returns the contract's value (sent to it from it's caller) func (c *Contract) Value() *big.Int { return c.value diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index df990c3901e7..3c7ce8935450 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -161,8 +161,7 @@ func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySiz if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { where := stack.Back(0) - addr := contract.Address() - index := trieUtils.GetTreeKeyStorageSlot(addr[:], where) + index := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(contract.AddressPoint(), where) usedGas += evm.Accesses.TouchAddressOnReadAndComputeGas(index) } diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 16b195eb901a..b266959885d0 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -53,8 +53,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { value := common.Hash(y.Bytes32()) if evm.chainConfig.IsCancun(evm.Context.BlockNumber) { - addr := contract.Address() - index := trieUtils.GetTreeKeyStorageSlot(addr[:], x) + index := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(contract.AddressPoint(), x) cost += evm.Accesses.TouchAddressOnWriteAndComputeGas(index) } diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go index d3585aca0477..687a9d8123a1 100644 --- a/trie/utils/verkle.go +++ b/trie/utils/verkle.go @@ -38,8 +38,17 @@ var ( MainStorageOffset = new(uint256.Int).Lsh(uint256.NewInt(256), 31) VerkleNodeWidth = uint256.NewInt(256) codeStorageDelta = uint256.NewInt(0).Sub(CodeOffset, HeaderStorageOffset) + + getTreePolyIndex0Fr [1]verkle.Fr + getTreePolyIndex0Point *verkle.Point ) +func init() { + cfg, _ := verkle.GetConfig() + verkle.FromLEBytes(&getTreePolyIndex0Fr[0], []byte{2, 64}) + getTreePolyIndex0Point = cfg.CommitToPoly(getTreePolyIndex0Fr[:], 1) +} + // GetTreeKey performs both the work of the spec's get_tree_key function, and that // of pedersen_hash: it builds the polynomial in pedersen_hash without having to // create a mostly zero-filled buffer and "type cast" it to a 128-long 16-byte @@ -52,8 +61,7 @@ func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte { } var poly [5]fr.Element - // (2 + 256 * length) little endian, length = 64 bytes - verkle.FromLEBytes(&poly[0], []byte{2, 64}) + poly[0].SetZero() // 32-byte address, interpreted as two little endian // 16-byte numbers. @@ -71,6 +79,9 @@ func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte { cfg, _ := verkle.GetConfig() ret := cfg.CommitToPoly(poly[:], 0) + // add a constant point + ret.Add(ret, getTreePolyIndex0Point) + // The output of Byte() is big engian for banderwagon. This // introduces an inbalance in the tree, because hashes are // elements of a 253-bit field. This means more than half the @@ -142,3 +153,83 @@ func GetTreeKeyStorageSlot(address []byte, storageKey *uint256.Int) []byte { } return GetTreeKey(address, treeIndex, subIndex) } + +func getTreeKeyWithEvaluatedAddess(evaluated *verkle.Point, treeIndex *uint256.Int, subIndex byte) []byte { + var poly [5]fr.Element + + poly[0].SetZero() + poly[1].SetZero() + poly[2].SetZero() + + // little-endian, 32-byte aligned treeIndex + var index [32]byte + for i, b := range treeIndex.Bytes() { + index[len(treeIndex.Bytes())-1-i] = b + } + verkle.FromLEBytes(&poly[3], index[:16]) + verkle.FromLEBytes(&poly[4], index[16:]) + + cfg, _ := verkle.GetConfig() + ret := cfg.CommitToPoly(poly[:], 0) + + // add the pre-evaluated address + ret.Add(ret, evaluated) + + // The output of Byte() is big engian for banderwagon. This + // introduces an inbalance in the tree, because hashes are + // elements of a 253-bit field. This means more than half the + // tree would be empty. To avoid this problem, use a little + // endian commitment and chop the MSB. + var retb [32]byte + retb = ret.Bytes() + for i := 0; i < 16; i++ { + retb[31-i], retb[i] = retb[i], retb[31-i] + } + retb[31] = subIndex + return retb[:] + +} + +func EvaluateAddressPoint(address []byte) *verkle.Point { + if len(address) < 32 { + var aligned [32]byte + address = append(aligned[:32-len(address)], address...) + } + var poly [3]fr.Element + + poly[0].SetZero() + + // 32-byte address, interpreted as two little endian + // 16-byte numbers. + verkle.FromLEBytes(&poly[1], address[:16]) + verkle.FromLEBytes(&poly[2], address[16:]) + + cfg, _ := verkle.GetConfig() + ret := cfg.CommitToPoly(poly[:], 0) + + // add a constant point + ret.Add(ret, getTreePolyIndex0Point) + + return ret +} + +func GetTreeKeyStorageSlotWithEvaluatedAddress(evaluated *verkle.Point, storageKey *uint256.Int) []byte { + pos := storageKey.Clone() + if storageKey.Cmp(codeStorageDelta) < 0 { + pos.Add(HeaderStorageOffset, storageKey) + } else { + pos.Add(MainStorageOffset, storageKey) + } + treeIndex := new(uint256.Int).Div(pos, VerkleNodeWidth) + // calculate the sub_index, i.e. the index in the stem tree. + // Because the modulus is 256, it's the last byte of treeIndex + subIndexMod := new(uint256.Int).Mod(pos, VerkleNodeWidth).Bytes() + var subIndex byte + if len(subIndexMod) != 0 { + // uint256 is broken into 4 little-endian quads, + // each with native endianness. Extract the least + // significant byte. + subIndex = byte(subIndexMod[0] & 0xFF) + } + return getTreeKeyWithEvaluatedAddess(evaluated, treeIndex, subIndex) +}