diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index f5340d8cc2a7..000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,45 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/2.0/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/2.0/configuration-reference/#jobs -jobs: - build: - working_directory: ~/repo - # Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor - docker: - - image: circleci/golang:1.16.10 - # Add steps to the job - # See: https://circleci.com/docs/2.0/configuration-reference/#steps - steps: - - checkout - - restore_cache: - keys: - - go-mod-v4-{{ checksum "go.sum" }} - - run: - name: Install Dependencies - command: go mod download - - save_cache: - key: go-mod-v4-{{ checksum "go.sum" }} - paths: - - "/go/pkg/mod" - #- run: - # name: Run linter - # command: | - # go run build/ci.go lint - - run: - name: Run tests - command: | - go run build/ci.go test -coverage - - store_test_results: - path: /tmp/test-reports - -# Invoke jobs via workflows -# See: https://circleci.com/docs/2.0/configuration-reference/#workflows -workflows: - sample: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - build diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 703b77ea0fdd..eb3bcae30e5a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -4,7 +4,7 @@ on: push: branches: [ master ] pull_request: - branches: [ master ] + branches: [ master, verkle-trie-proof-in-block-rebased ] jobs: @@ -50,5 +50,8 @@ jobs: with: go-version: 1.18 + - name: Download precomputed points + run: wget -nv https://github.com/gballet/go-verkle/releases/download/banderwagon/precomp + - name: Test - run: go test -v ./... + run: go test ./... diff --git a/common/fdlimit/fdlimit_test.go b/common/fdlimit/fdlimit_test.go index 21362b8463a3..9fd5e9fc3cbd 100644 --- a/common/fdlimit/fdlimit_test.go +++ b/common/fdlimit/fdlimit_test.go @@ -17,7 +17,6 @@ package fdlimit import ( - "fmt" "testing" ) @@ -30,7 +29,7 @@ func TestFileDescriptorLimits(t *testing.T) { t.Fatal(err) } if hardlimit < target { - t.Skip(fmt.Sprintf("system limit is less than desired test target: %d < %d", hardlimit, target)) + t.Skipf("system limit is less than desired test target: %d < %d", hardlimit, target) } if limit, err := Current(); err != nil || limit <= 0 { diff --git a/core/state/state_object.go b/core/state/state_object.go index 5c0f80c76311..1a0b3a2963a9 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 @@ -130,10 +133,16 @@ func newObject(db *StateDB, address common.Address, data types.StateAccount) *st if data.Root == (common.Hash{}) { data.Root = emptyRoot } + var pointEval *verkle.Point + if db.GetTrie().IsVerkle() { + pointEval = trieUtils.EvaluateAddressPoint(address.Bytes()) + } + return &stateObject{ db: db, address: address, addrHash: crypto.Keccak256Hash(address[:]), + pointEval: pointEval, data: data, originStorage: make(Storage), pendingStorage: make(Storage), @@ -282,9 +291,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 +399,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 +412,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/state/statedb.go b/core/state/statedb.go index 6535f3633092..949bd2e4e844 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -519,7 +519,7 @@ func (s *StateDB) updateStateObject(obj *stateObject) { if obj.dirtyCode { if chunks, err := trie.ChunkifyCode(obj.code); err == nil { for i := range chunks { - s.trie.TryUpdate(trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(uint64(i))), chunks[i][:]) + s.trie.TryUpdate(trieUtils.GetTreeKeyCodeChunkWithEvaluatedAddress(obj.pointEval, uint256.NewInt(uint64(i))), chunks[i][:]) } } else { s.setError(err) @@ -1016,7 +1016,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { if s.trie.IsVerkle() { if chunks, err := trie.ChunkifyCode(obj.code); err == nil { for i := range chunks { - s.trie.TryUpdate(trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(uint64(i))), chunks[i][:]) + s.trie.TryUpdate(trieUtils.GetTreeKeyCodeChunkWithEvaluatedAddress(obj.pointEval, uint256.NewInt(uint64(i))), chunks[i][:]) } } else { s.setError(err) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index d2a9932395a6..0b4201a903a0 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -20,6 +20,7 @@ import ( //"bytes" "bytes" "crypto/ecdsa" + //"fmt" "math/big" //"os" @@ -35,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + //"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "golang.org/x/crypto/sha3" @@ -501,7 +503,7 @@ func TestProcessVerkleCodeDeployExec(t *testing.T) { t.Fatalf("block imported with error: %v", err) } - // Check that the location for the contract is availabe in the witness + // Check that the location for the contract is available in the witness // and is reported as not present. b1 := blockchain.GetBlockByNumber(1) if b1 == nil { 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/contracts_test.go b/core/vm/contracts_test.go index 30d9b49f719b..aa8d2f1eb38b 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -185,7 +185,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { return } if common.Bytes2Hex(res) != test.Expected { - bench.Error(fmt.Sprintf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))) + bench.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) return } }) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index df990c3901e7..1284f0071df7 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -122,7 +122,7 @@ func gasCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory uint64Length = 0xffffffffffffffff } _, offset, nonPaddedSize := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, uint64Length) - statelessGas = touchEachChunksOnReadAndChargeGas(offset, nonPaddedSize, contract.Address().Bytes()[:], nil, evm.Accesses, contract.IsDeployment) + statelessGas = touchEachChunksOnReadAndChargeGas(offset, nonPaddedSize, contract.AddressPoint(), nil, evm.Accesses, contract.IsDeployment) } usedGas, err := gasCodeCopyStateful(evm, contract, stack, mem, memorySize) return usedGas + statelessGas, err @@ -150,7 +150,7 @@ func gasExtCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem // behavior from CODECOPY which only charges witness access costs for the part of the range // which overlaps in the account code. TODO: clarify this is desired behavior and amend the // spec. - statelessGas = touchEachChunksOnReadAndChargeGas(uint64CodeOffset, uint64Length, targetAddr[:], nil, evm.Accesses, contract.IsDeployment) + statelessGas = touchEachChunksOnReadAndChargeGasWithAddress(uint64CodeOffset, uint64Length, targetAddr[:], nil, evm.Accesses, contract.IsDeployment) } usedGas, err := gasExtCodeCopyStateful(evm, contract, stack, mem, memorySize) return usedGas + statelessGas, err @@ -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/instructions.go b/core/vm/instructions.go index e1e99da935fd..19527339bf4e 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" trieUtils "github.com/ethereum/go-ethereum/trie/utils" + "github.com/gballet/go-verkle" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -375,14 +376,19 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64()) if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) { - touchEachChunksOnReadAndChargeGas(copyOffset, nonPaddedCopyLength, scope.Contract.Address().Bytes()[:], scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) + touchEachChunksOnReadAndChargeGas(copyOffset, nonPaddedCopyLength, scope.Contract.AddressPoint(), scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) } scope.Memory.Set(memOffset.Uint64(), uint64(len(paddedCodeCopy)), paddedCodeCopy) return nil, nil } +func touchEachChunksOnReadAndChargeGasWithAddress(offset, size uint64, address, code []byte, accesses *types.AccessWitness, deployment bool) uint64 { + addrPoint := trieUtils.EvaluateAddressPoint(address) + return touchEachChunksOnReadAndChargeGas(offset, size, addrPoint, code, accesses, deployment) +} + // touchEachChunksAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs -func touchEachChunksOnReadAndChargeGas(offset, size uint64, address []byte, code []byte, accesses *types.AccessWitness, deployment bool) uint64 { +func touchEachChunksOnReadAndChargeGas(offset, size uint64, addrPoint *verkle.Point, code []byte, accesses *types.AccessWitness, deployment bool) uint64 { // note that in the case where the copied code is outside the range of the // contract code but touches the last leaf with contract code in it, // we don't include the last leaf of code in the AccessWitness. The @@ -409,7 +415,7 @@ func touchEachChunksOnReadAndChargeGas(offset, size uint64, address []byte, code // endOffset - 1 since if the end offset is aligned on a chunk boundary, // the last chunk should not be included. for i := offset / 31; i <= (endOffset-1)/31; i++ { - index := trieUtils.GetTreeKeyCodeChunk(address, uint256.NewInt(i)) + index := trieUtils.GetTreeKeyCodeChunkWithEvaluatedAddress(addrPoint, uint256.NewInt(i)) var overflow bool statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, accesses.TouchAddressOnReadAndComputeGas(index)) @@ -417,7 +423,7 @@ func touchEachChunksOnReadAndChargeGas(offset, size uint64, address []byte, code panic("overflow when adding gas") } - if code != nil && len(code) > 0 { + if len(code) > 0 { if deployment { accesses.SetLeafValue(index[:], nil) } else { @@ -445,7 +451,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) { code := interpreter.evm.StateDB.GetCode(addr) paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - touchEachChunksOnReadAndChargeGas(copyOffset, nonPaddedCopyLength, addr[:], code, interpreter.evm.Accesses, false) + touchEachChunksOnReadAndChargeGasWithAddress(copyOffset, nonPaddedCopyLength, addr[:], code, interpreter.evm.Accesses, false) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) } else { codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) @@ -922,7 +928,7 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) && *pc%31 == 0 { // touch next chunk if PUSH1 is at the boundary. if so, *pc has // advanced past this boundary. - statelessGas := touchEachChunksOnReadAndChargeGas(uint64(*pc+1), uint64(1), scope.Contract.Address().Bytes()[:], scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) + statelessGas := touchEachChunksOnReadAndChargeGas(*pc+1, uint64(1), scope.Contract.AddressPoint(), scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) scope.Contract.UseGas(statelessGas) } } else { @@ -947,7 +953,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { } if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) { - statelessGas := touchEachChunksOnReadAndChargeGas(uint64(startMin), uint64(pushByteSize), scope.Contract.Address().Bytes()[:], scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) + statelessGas := touchEachChunksOnReadAndChargeGas(uint64(startMin), uint64(pushByteSize), scope.Contract.AddressPoint(), scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) scope.Contract.UseGas(statelessGas) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index fb51cb482e75..3394444ae336 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -194,7 +194,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( if in.evm.ChainConfig().IsCancun(in.evm.Context.BlockNumber) && !contract.IsDeployment { // if the PC ends up in a new "page" of verkleized code, charge the // associated witness costs. - contract.Gas -= touchEachChunksOnReadAndChargeGas(pc, 1, contract.Address().Bytes()[:], contract.Code, in.evm.TxContext.Accesses, contract.IsDeployment) + contract.Gas -= touchEachChunksOnReadAndChargeGas(pc, 1, contract.AddressPoint(), contract.Code, in.evm.TxContext.Accesses, contract.IsDeployment) } // If we are in witness mode, then raise an error 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..3a07a69c0e83 100644 --- a/trie/utils/verkle.go +++ b/trie/utils/verkle.go @@ -38,8 +38,19 @@ 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 = new(verkle.Point) ) +func init() { + // The byte array is the Marshalled output of the point computed as such: + //cfg, _ := verkle.GetConfig() + //verkle.FromLEBytes(&getTreePolyIndex0Fr[0], []byte{2, 64}) + //= cfg.CommitToPoly(getTreePolyIndex0Fr[:], 1) + getTreePolyIndex0Point.Unmarshal([]byte{105, 89, 33, 220, 163, 177, 108, 92, 200, 80, 233, 76, 221, 99, 245, 115, 196, 103, 102, 158, 137, 206, 200, 137, 53, 208, 52, 116, 214, 189, 249, 212}) +} + // 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 +63,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,13 +81,15 @@ 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 + // introduces an imbalance 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() + retb := ret.Bytes() for i := 0; i < 16; i++ { retb[31-i], retb[i] = retb[i], retb[31-i] } @@ -121,6 +133,17 @@ func GetTreeKeyCodeChunk(address []byte, chunk *uint256.Int) []byte { return GetTreeKey(address, treeIndex, subIndex) } +func GetTreeKeyCodeChunkWithEvaluatedAddress(addressPoint *verkle.Point, chunk *uint256.Int) []byte { + chunkOffset := new(uint256.Int).Add(CodeOffset, chunk) + treeIndex := new(uint256.Int).Div(chunkOffset, VerkleNodeWidth) + subIndexMod := new(uint256.Int).Mod(chunkOffset, VerkleNodeWidth).Bytes() + var subIndex byte + if len(subIndexMod) != 0 { + subIndex = subIndexMod[0] + } + return getTreeKeyWithEvaluatedAddess(addressPoint, treeIndex, subIndex) +} + func GetTreeKeyStorageSlot(address []byte, storageKey *uint256.Int) []byte { pos := storageKey.Clone() if storageKey.Cmp(codeStorageDelta) < 0 { @@ -138,7 +161,86 @@ func GetTreeKeyStorageSlot(address []byte, storageKey *uint256.Int) []byte { // uint256 is broken into 4 little-endian quads, // each with native endianness. Extract the least // significant byte. - subIndex = byte(subIndexMod[0] & 0xFF) + subIndex = subIndexMod[0] & 0xFF } 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 imbalance 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. + 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 = subIndexMod[0] & 0xFF + } + return getTreeKeyWithEvaluatedAddess(evaluated, treeIndex, subIndex) +} diff --git a/trie/utils/verkle_test.go b/trie/utils/verkle_test.go new file mode 100644 index 000000000000..6a98c6acb185 --- /dev/null +++ b/trie/utils/verkle_test.go @@ -0,0 +1,86 @@ +// Copyright 2022 go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package utils + +import ( + "crypto/sha256" + "math/big" + "math/rand" + "testing" + + "github.com/gballet/go-verkle" + "github.com/holiman/uint256" +) + +func TestGetTreeKey(t *testing.T) { + var addr [32]byte + for i := 0; i < 16; i++ { + addr[1+2*i] = 0xff + } + n := uint256.NewInt(1) + n = n.Lsh(n, 129) + n.Add(n, uint256.NewInt(3)) + GetTreeKey(addr[:], n, 1) +} + +func TestConstantPoint(t *testing.T) { + cfg, _ := verkle.GetConfig() + verkle.FromLEBytes(&getTreePolyIndex0Fr[0], []byte{2, 64}) + expected := cfg.CommitToPoly(getTreePolyIndex0Fr[:], 1) + + if !verkle.Equal(expected, getTreePolyIndex0Point) { + t.Fatal("Marshalled constant value is incorrect") + } +} + +func BenchmarkPedersenHash(b *testing.B) { + var addr, v [32]byte + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + rand.Read(v[:]) + rand.Read(addr[:]) + GetTreeKeyCodeSize(addr[:]) + } +} + +func sha256GetTreeKeyCodeSize(addr []byte) []byte { + digest := sha256.New() + digest.Write(addr) + treeIndexBytes := new(big.Int).Bytes() + var payload [32]byte + copy(payload[:len(treeIndexBytes)], treeIndexBytes) + digest.Write(payload[:]) + h := digest.Sum(nil) + h[31] = CodeKeccakLeafKey + return h +} + +func BenchmarkSha256Hash(b *testing.B) { + var addr, v [32]byte + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + rand.Read(v[:]) + rand.Read(addr[:]) + sha256GetTreeKeyCodeSize(addr[:]) + } +} diff --git a/trie/verkle.go b/trie/verkle.go index b33e413af416..faa424382a45 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -305,11 +305,9 @@ func deserializeVerkleProof(serialized []byte) (*verkle.Proof, []*verkle.Point, // another stem is found, build it node.SetStem(proof.PoaStems[lastpoa]) lastpoa++ - break case 2: // stem is present node.SetStem(stem[:31]) - break default: return nil, nil, nil, nil, nil, fmt.Errorf("verkle proof deserialization error: invalid extension status %d", status) }