Skip to content

Commit

Permalink
reuse address point evaluation in code chunking (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
gballet authored Jun 29, 2022
1 parent fbcdf09 commit e15eaf7
Show file tree
Hide file tree
Showing 15 changed files with 247 additions and 79 deletions.
45 changes: 0 additions & 45 deletions .circleci/config.yml

This file was deleted.

7 changes: 5 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
branches: [ master, verkle-trie-proof-in-block-rebased ]

jobs:

Expand Down Expand Up @@ -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 ./...
3 changes: 1 addition & 2 deletions common/fdlimit/fdlimit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package fdlimit

import (
"fmt"
"testing"
)

Expand All @@ -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 {
Expand Down
16 changes: 12 additions & 4 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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[:]))
}
Expand Down
4 changes: 2 additions & 2 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
//"bytes"
"bytes"
"crypto/ecdsa"

//"fmt"
"math/big"
//"os"
Expand All @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down
11 changes: 11 additions & 0 deletions core/vm/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
})
Expand Down
7 changes: 3 additions & 4 deletions core/vm/gas_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
}

Expand Down
20 changes: 13 additions & 7 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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
Expand All @@ -409,15 +415,15 @@ 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))
if overflow {
panic("overflow when adding gas")
}

if code != nil && len(code) > 0 {
if len(code) > 0 {
if deployment {
accesses.SetLeafValue(index[:], nil)
} else {
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
}

Expand Down
2 changes: 1 addition & 1 deletion core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions core/vm/operations_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Loading

0 comments on commit e15eaf7

Please sign in to comment.