From 1dfca6a94c2e0c54ad8ce2045214bd9a717fbc44 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 27 Jun 2024 18:17:04 +0200 Subject: [PATCH] implement post-nyota verkle costs --- cmd/geth/verkle.go | 2 +- core/overlay/conversion.go | 26 ++++++------- core/state/access_witness.go | 43 ++++++--------------- core/state/database.go | 2 +- core/state_processor_test.go | 75 ++++++++++++++++++++++++++++-------- core/vm/operations_verkle.go | 25 ++++-------- trie/utils/verkle.go | 38 +++++++----------- trie/utils/verkle_test.go | 2 +- trie/verkle.go | 63 ++++++++++++++---------------- trie/verkle_iterator.go | 2 +- trie/verkle_iterator_test.go | 2 +- trie/verkle_test.go | 2 +- 12 files changed, 138 insertions(+), 144 deletions(-) diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index ea424ebaf138..c3e68a29cfa0 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -180,7 +180,7 @@ func convertToVerkle(ctx *cli.Context) error { return fmt.Errorf("could not find preimage for address %x %v %v", accIt.Hash(), acc, accIt.Error()) } addrPoint := tutils.EvaluateAddressPoint(addr) - stem := tutils.GetTreeKeyVersionWithEvaluatedAddress(addrPoint) + stem := tutils.GetTreeKeyBasicDataEvaluatedAddress(addrPoint) // Store the account code if present if !bytes.Equal(acc.CodeHash, types.EmptyRootHash[:]) { diff --git a/core/overlay/conversion.go b/core/overlay/conversion.go index 0e83e2066353..06f3e84617dc 100644 --- a/core/overlay/conversion.go +++ b/core/overlay/conversion.go @@ -95,29 +95,29 @@ func (kvm *keyValueMigrator) addStorageSlot(addr []byte, slotNumber []byte, slot func (kvm *keyValueMigrator) addAccount(addr []byte, acc *types.StateAccount) { leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, &zeroTreeIndex)) - var version [verkle.LeafValueSize]byte - leafNodeData.Values[utils.VersionLeafKey] = version[:] + var basicData [verkle.LeafValueSize]byte + basicData[utils.BasicDataVersionOffset] = 0 // version - var balance [verkle.LeafValueSize]byte - for i, b := range acc.Balance.Bytes() { - balance[len(acc.Balance.Bytes())-1-i] = b + // get the lower 16 bytes of water and change its endianness + balanceBytes := acc.Balance.Bytes() + for i := 0; i < 16 && i < len(balanceBytes); i++ { + basicData[utils.BasicDataBalanceOffset+i] = balanceBytes[len(balanceBytes)-1-i] } - leafNodeData.Values[utils.BalanceLeafKey] = balance[:] - - var nonce [verkle.LeafValueSize]byte - binary.LittleEndian.PutUint64(nonce[:8], acc.Nonce) - leafNodeData.Values[utils.NonceLeafKey] = nonce[:] + binary.LittleEndian.PutUint64(basicData[utils.BasicDataNonceOffset:], acc.Nonce) + leafNodeData.Values[utils.BasicDataLeafKey] = basicData[:] leafNodeData.Values[utils.CodeHashLeafKey] = acc.CodeHash[:] } +// addAccountCode needs to be called AFTER addAccount, as it will reuse the leaf +// that was created in there. func (kvm *keyValueMigrator) addAccountCode(addr []byte, codeSize uint64, chunks []byte) { leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, &zeroTreeIndex)) // Save the code size. - var codeSizeBytes [verkle.LeafValueSize]byte - binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize) - leafNodeData.Values[utils.CodeSizeLeafKey] = codeSizeBytes[:] + var cs [8]byte + binary.LittleEndian.PutUint64(cs[:], codeSize) + copy(leafNodeData.Values[utils.BasicDataLeafKey][utils.BasicDataCodeSizeOffset:utils.BasicDataNonceOffset], cs[:3]) // The first 128 chunks are stored in the account header leaf. for i := 0; i < 128 && i < len(chunks)/32; i++ { diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 4c9d3218ad96..39a1d7674a77 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -91,7 +91,7 @@ func (aw *AccessWitness) Copy() *AccessWitness { func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool) uint64 { var gas uint64 - for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ { + for i := utils.BasicDataLeafKey; i <= utils.CodeHashLeafKey; i++ { gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), isWrite) } return gas @@ -99,15 +99,14 @@ func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool) uint64 { func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte) uint64 { var gas uint64 - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false) - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, false) + gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false) return gas } func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 { var gas uint64 - gas += aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true) - gas += aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true) + gas += aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) + gas += aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) return gas } @@ -115,17 +114,13 @@ func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []by // a contract creation func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64 { var gas uint64 - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true) - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true) - if createSendsValue { - gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true) - } + gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true) return gas } func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 { - for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ { - aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BalanceLeafKey || i == utils.NonceLeafKey) + for i := utils.BasicDataLeafKey; i <= utils.CodeHashLeafKey; i++ { + aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BasicDataLeafKey) } // Kaustinen note: we're currently experimenting with stop chargin gas for the origin address @@ -136,14 +131,10 @@ func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 { } func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 { - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.VersionLeafKey, false) - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false) + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, false) aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false) - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.NonceLeafKey, false) if sendsValue { - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true) - } else { - aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, false) + aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) } // Kaustinen note: we're currently experimenting with stop chargin gas for the origin address @@ -276,20 +267,8 @@ func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, s return statelessGasCharged } -func (aw *AccessWitness) TouchVersion(addr []byte, isWrite bool) uint64 { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite) -} - -func (aw *AccessWitness) TouchBalance(addr []byte, isWrite bool) uint64 { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite) -} - -func (aw *AccessWitness) TouchNonce(addr []byte, isWrite bool) uint64 { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite) -} - -func (aw *AccessWitness) TouchCodeSize(addr []byte, isWrite bool) uint64 { - return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite) +func (aw *AccessWitness) TouchBasicData(addr []byte, isWrite bool) uint64 { + return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite) } func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool) uint64 { diff --git a/core/state/database.go b/core/state/database.go index f2592f7de03a..f8327c4e383b 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -329,7 +329,7 @@ func (db *cachingDB) openMPTTrie(root common.Hash) (Trie, error) { return tr, nil } -func (db *cachingDB) openVKTrie(root common.Hash) (Trie, error) { +func (db *cachingDB) openVKTrie(_ common.Hash) (Trie, error) { payload, err := db.DiskDB().Get(trie.FlatDBVerkleNodeKeyPrefix) if err != nil { return trie.NewVerkleTrie(verkle.New(), db.triedb, db.addrToPoint, db.CurrentTransitionState.Ended), nil diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 991f4e2de68e..6fe37cc631dc 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -498,8 +498,49 @@ func TestProcessVerkle(t *testing.T) { txCost1 := params.TxGas txCost2 := params.TxGas - contractCreationCost := intrinsicContractCreationGas + uint64(5600+700+700+700 /* creation with value */ +1439 /* execution costs */) - codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(5600+700 /* creation */ +44044 /* execution costs */) + contractCreationCost := intrinsicContractCreationGas + uint64( + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+params.WitnessBranchReadCost+params.WitnessBranchWriteCost+ /* creation */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* creation with value */ + 739 /* execution costs */) + codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64( + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+params.WitnessBranchReadCost+params.WitnessBranchWriteCost+ /* creation (tx) */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+params.WitnessBranchReadCost+params.WitnessBranchWriteCost+ /* creation (CREATE at pc=0x20) */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* write code hash */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #0 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #1 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #2 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #3 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #4 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #5 */ + params.WitnessChunkReadCost+ /* SLOAD in constructor */ + params.WitnessChunkWriteCost+ /* SSTORE in constructor */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+params.WitnessBranchReadCost+params.WitnessBranchWriteCost+ /* creation (CREATE at PC=0x121) */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* write code hash */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #0 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #1 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #2 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #3 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #4 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #5 */ + params.WitnessChunkReadCost+ /* SLOAD in constructor */ + params.WitnessChunkWriteCost+ /* SSTORE in constructor */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* write code hash for tx creation */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #0 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #1 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #2 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #3 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #4 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #5 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #6 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #7 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #8 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #9 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #10 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #11 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #12 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #13 */ + params.WitnessChunkReadCost+params.WitnessChunkWriteCost+ /* code chunk #14 */ + 4844 /* execution costs */) blockGasUsagesExpected := []uint64{ txCost1*2 + txCost2, txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, @@ -939,7 +980,7 @@ func TestProcessVerklExtCodeHashOpcode(t *testing.T) { } }) - contractKeccakTreeKey := utils.GetTreeKeyCodeKeccak(dummyContractAddr[:]) + contractKeccakTreeKey := utils.GetTreeKeyCodeHash(dummyContractAddr[:]) var stateDiffIdx = -1 for i, stemStateDiff := range statediff[1] { @@ -1034,7 +1075,7 @@ func TestProcessVerkleBalanceOpcode(t *testing.T) { gen.AddTx(tx) }) - account2BalanceTreeKey := utils.GetTreeKeyBalance(account2[:]) + account2BalanceTreeKey := utils.GetTreeKeyBasicData(account2[:]) var stateDiffIdx = -1 for i, stemStateDiff := range statediff[0] { @@ -1049,7 +1090,7 @@ func TestProcessVerkleBalanceOpcode(t *testing.T) { var zero [32]byte balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[0] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { t.Fatalf("invalid suffix diff") } if balanceStateDiff.CurrentValue == nil { @@ -1148,7 +1189,7 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { var zero [32]byte { // Check self-destructed contract in the witness - selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:]) + selfDestructContractTreeKey := utils.GetTreeKeyCodeHash(selfDestructContractAddr[:]) var stateDiffIdx = -1 for i, stemStateDiff := range statediff[1] { @@ -1162,7 +1203,7 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { } balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[1] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { t.Fatalf("balance invalid suffix") } @@ -1179,7 +1220,7 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { } } { // Check self-destructed target in the witness. - selfDestructTargetTreeKey := utils.GetTreeKeyCodeKeccak(account2[:]) + selfDestructTargetTreeKey := utils.GetTreeKeyCodeHash(account2[:]) var stateDiffIdx = -1 for i, stemStateDiff := range statediff[1] { @@ -1193,7 +1234,7 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { } balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[0] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { t.Fatalf("balance invalid suffix") } if balanceStateDiff.CurrentValue == nil { @@ -1276,7 +1317,7 @@ func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { }) { // Check self-destructed contract in the witness - selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:]) + selfDestructContractTreeKey := utils.GetTreeKeyCodeHash(selfDestructContractAddr[:]) var stateDiffIdx = -1 for i, stemStateDiff := range statediff[0] { @@ -1290,7 +1331,7 @@ func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { } balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[1] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { t.Fatalf("balance invalid suffix") } @@ -1303,7 +1344,7 @@ func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { } } { // Check self-destructed target in the witness. - selfDestructTargetTreeKey := utils.GetTreeKeyCodeKeccak(account2[:]) + selfDestructTargetTreeKey := utils.GetTreeKeyCodeHash(account2[:]) var stateDiffIdx = -1 for i, stemStateDiff := range statediff[0] { @@ -1317,7 +1358,7 @@ func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { } balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[0] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { t.Fatalf("balance invalid suffix") } if balanceStateDiff.CurrentValue == nil { @@ -1422,7 +1463,7 @@ func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T) // to the beneficiary. In this case both addresses are the same, thus this might be optimizable from a gas // perspective. But until that happens, we need to honor this "balance reading" adding it to the witness. - selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:]) + selfDestructContractTreeKey := utils.GetTreeKeyCodeHash(selfDestructContractAddr[:]) var stateDiffIdx = -1 for i, stemStateDiff := range statediff[1] { @@ -1436,7 +1477,7 @@ func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T) } balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[1] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { t.Fatalf("balance invalid suffix") } @@ -1521,7 +1562,7 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) { }) { // Check self-destructed contract in the witness - selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:]) + selfDestructContractTreeKey := utils.GetTreeKeyCodeHash(selfDestructContractAddr[:]) var stateDiffIdx = -1 for i, stemStateDiff := range statediff[0] { @@ -1535,7 +1576,7 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) { } balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[1] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { t.Fatalf("balance invalid suffix") } diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 8b55b83bb21f..d748698c6b5c 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -40,7 +40,7 @@ func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { address := stack.peek().Bytes20() - gas := evm.Accesses.TouchBalance(address[:], false) + gas := evm.Accesses.TouchBasicData(address[:], false) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 } @@ -52,8 +52,7 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil } - wgas := evm.Accesses.TouchVersion(address[:], false) - wgas += evm.Accesses.TouchCodeSize(address[:], false) + wgas := evm.Accesses.TouchBasicData(address[:], false) if wgas == 0 { wgas = params.WarmStorageReadCostEIP2929 } @@ -103,24 +102,15 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem } contractAddr := contract.Address() - statelessGas := evm.Accesses.TouchVersion(contractAddr[:], false) - statelessGas += evm.Accesses.TouchCodeSize(contractAddr[:], false) - statelessGas += evm.Accesses.TouchBalance(contractAddr[:], false) + statelessGas := evm.Accesses.TouchBasicData(contractAddr[:], false) if contractAddr != beneficiaryAddr { - statelessGas += evm.Accesses.TouchBalance(beneficiaryAddr[:], false) + statelessGas += evm.Accesses.TouchBasicData(beneficiaryAddr[:], false) } // Charge write costs if it transfers value if evm.StateDB.GetBalance(contractAddr).Sign() != 0 { - statelessGas += evm.Accesses.TouchBalance(contractAddr[:], true) + statelessGas += evm.Accesses.TouchBasicData(contractAddr[:], true) if contractAddr != beneficiaryAddr { - statelessGas += evm.Accesses.TouchBalance(beneficiaryAddr[:], true) - } - - // Case when the beneficiary does not exist: touch the account - // but leave code hash and size alone. - if evm.StateDB.Empty(beneficiaryAddr) { - statelessGas += evm.Accesses.TouchVersion(beneficiaryAddr[:], true) - statelessGas += evm.Accesses.TouchNonce(beneficiaryAddr[:], true) + statelessGas += evm.Accesses.TouchBasicData(beneficiaryAddr[:], true) } } return statelessGas, nil @@ -133,8 +123,7 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo return 0, err } addr := common.Address(stack.peek().Bytes20()) - wgas := evm.Accesses.TouchVersion(addr[:], false) - wgas += evm.Accesses.TouchCodeSize(addr[:], false) + wgas := evm.Accesses.TouchBasicData(addr[:], false) if wgas == 0 { wgas = params.WarmStorageReadCostEIP2929 } diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go index ad6184baa99a..0ddf1e2ef4c1 100644 --- a/trie/utils/verkle.go +++ b/trie/utils/verkle.go @@ -24,11 +24,13 @@ import ( ) const ( - VersionLeafKey = 0 - BalanceLeafKey = 1 - NonceLeafKey = 2 - CodeHashLeafKey = 3 - CodeSizeLeafKey = 4 + BasicDataLeafKey = 0 + CodeHashLeafKey = 1 + + BasicDataVersionOffset = 0 + BasicDataCodeSizeOffset = 5 + BasicDataNonceOffset = 8 + BasicDataBalanceOffset = 16 maxPointCacheByteSize = 100 << 20 ) @@ -70,9 +72,9 @@ func (pc *PointCache) GetTreeKeyHeader(addr []byte) *verkle.Point { return point } -func (pc *PointCache) GetTreeKeyVersionCached(addr []byte) []byte { +func (pc *PointCache) GetTreeKeyBasicDataCached(addr []byte) []byte { p := pc.GetTreeKeyHeader(addr) - v := PointToHash(p, VersionLeafKey) + v := PointToHash(p, BasicDataLeafKey) return v[:] } @@ -133,30 +135,18 @@ func GetTreeKeyAccountLeaf(address []byte, leaf byte) []byte { return GetTreeKey(address, zero, leaf) } -func GetTreeKeyVersion(address []byte) []byte { - return GetTreeKey(address, zero, VersionLeafKey) -} - -func GetTreeKeyVersionWithEvaluatedAddress(addrp *verkle.Point) []byte { - return GetTreeKeyWithEvaluatedAddess(addrp, zero, VersionLeafKey) +func GetTreeKeyBasicData(address []byte) []byte { + return GetTreeKey(address, zero, BasicDataLeafKey) } -func GetTreeKeyBalance(address []byte) []byte { - return GetTreeKey(address, zero, BalanceLeafKey) +func GetTreeKeyBasicDataEvaluatedAddress(addrp *verkle.Point) []byte { + return GetTreeKeyWithEvaluatedAddess(addrp, zero, BasicDataLeafKey) } -func GetTreeKeyNonce(address []byte) []byte { - return GetTreeKey(address, zero, NonceLeafKey) -} - -func GetTreeKeyCodeKeccak(address []byte) []byte { +func GetTreeKeyCodeHash(address []byte) []byte { return GetTreeKey(address, zero, CodeHashLeafKey) } -func GetTreeKeyCodeSize(address []byte) []byte { - return GetTreeKey(address, zero, CodeSizeLeafKey) -} - func GetTreeKeyCodeChunk(address []byte, chunk *uint256.Int) []byte { treeIndex, subIndex := GetTreeKeyCodeChunkIndices(chunk) return GetTreeKey(address, treeIndex, subIndex) diff --git a/trie/utils/verkle_test.go b/trie/utils/verkle_test.go index 2cb9d2ad8690..bce03df12b46 100644 --- a/trie/utils/verkle_test.go +++ b/trie/utils/verkle_test.go @@ -66,7 +66,7 @@ func BenchmarkPedersenHash(b *testing.B) { for i := 0; i < b.N; i++ { rand.Read(v[:]) rand.Read(addr[:]) - GetTreeKeyCodeSize(addr[:]) + GetTreeKeyBasicData(addr[:]) } } diff --git a/trie/verkle.go b/trie/verkle.go index f0568077fb35..62a271ea4ca9 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -100,7 +100,7 @@ func (trie *VerkleTrie) GetWithHashedKey(key []byte) ([]byte, error) { func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) { acc := &types.StateAccount{} - versionkey := t.pointCache.GetTreeKeyVersionCached(addr[:]) + versionkey := t.pointCache.GetTreeKeyBasicDataCached(addr[:]) var ( values [][]byte err error @@ -128,14 +128,14 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error return nil, nil } - if len(values[utils.NonceLeafKey]) > 0 { - acc.Nonce = binary.LittleEndian.Uint64(values[utils.NonceLeafKey]) + if len(values[utils.BasicDataLeafKey]) > 0 { + acc.Nonce = binary.LittleEndian.Uint64(values[utils.BasicDataLeafKey][utils.BasicDataNonceOffset:]) } + // if the account has been deleted, then values[10] will be 0 and not nil. If it has // been recreated after that, then its code keccak will NOT be 0. So return `nil` if // the nonce, and values[10], and code keccak is 0. - - if acc.Nonce == 0 && len(values) > 10 && len(values[10]) > 0 && bytes.Equal(values[utils.CodeHashLeafKey], zero[:]) { + if bytes.Equal(values[utils.BasicDataLeafKey], zero[:]) && len(values) > 10 && len(values[10]) > 0 && bytes.Equal(values[utils.CodeHashLeafKey], zero[:]) { if !t.ended { return nil, errDeletedAccount } else { @@ -143,19 +143,12 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error } } var balance [32]byte - copy(balance[:], values[utils.BalanceLeafKey]) - for i := 0; i < len(balance)/2; i++ { - balance[len(balance)-i-1], balance[i] = balance[i], balance[len(balance)-i-1] + copy(balance[16:], values[utils.BasicDataLeafKey][utils.BasicDataBalanceOffset:]) + for i := 0; i < 8; i++ { + balance[16-i-1], balance[i] = balance[i], balance[16-i-1] } - // var balance [32]byte - // if len(values[utils.BalanceLeafKey]) > 0 { - // for i := 0; i < len(balance); i++ { - // balance[len(balance)-i-1] = values[utils.BalanceLeafKey][i] - // } - // } acc.Balance = new(big.Int).SetBytes(balance[:]) acc.CodeHash = values[utils.CodeHashLeafKey] - // TODO fix the code size as well return acc, nil } @@ -164,26 +157,22 @@ var zero [32]byte func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) error { var ( - err error - nonce, balance [32]byte - values = make([][]byte, verkle.NodeWidth) - stem = t.pointCache.GetTreeKeyVersionCached(addr[:]) + err error + basicData [32]byte + values = make([][]byte, verkle.NodeWidth) + stem = t.pointCache.GetTreeKeyBasicDataCached(addr[:]) ) - // Only evaluate the polynomial once - values[utils.VersionLeafKey] = zero[:] - values[utils.NonceLeafKey] = nonce[:] - values[utils.BalanceLeafKey] = balance[:] - values[utils.CodeHashLeafKey] = acc.CodeHash[:] - - binary.LittleEndian.PutUint64(nonce[:], acc.Nonce) - bbytes := acc.Balance.Bytes() - if len(bbytes) > 0 { - for i, b := range bbytes { - balance[len(bbytes)-i-1] = b - } + binary.LittleEndian.PutUint64(basicData[utils.BasicDataNonceOffset:], acc.Nonce) + // get the lower 16 bytes of water and change its endianness + balanceBytes := acc.Balance.Bytes() + for i := 0; i < 16 && i < len(balanceBytes); i++ { + basicData[utils.BasicDataBalanceOffset+i] = balanceBytes[len(balanceBytes)-1-i] } + values[utils.BasicDataLeafKey] = basicData[:] + values[utils.CodeHashLeafKey] = acc.CodeHash[:] + switch root := t.root.(type) { case *verkle.InternalNode: err = root.InsertValuesAtStem(stem, values, t.FlatdbNodeResolver) @@ -448,6 +437,7 @@ func (t *VerkleTrie) ClearStrorageRootConversion(addr common.Address) { t.db.ClearStorageRootConversion(addr) } +// Note: the basic data leaf needs to have been previously created for this to work func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Hash, code []byte) error { var ( chunks = ChunkifyCode(code) @@ -465,9 +455,14 @@ func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Has // Reuse the calculated key to also update the code size. if i == 0 { - cs := make([]byte, 32) - binary.LittleEndian.PutUint64(cs, uint64(len(code))) - values[utils.CodeSizeLeafKey] = cs + headervals, err := t.root.(*verkle.InternalNode).GetValuesAtStem(key[:31], t.FlatdbNodeResolver) + if err != nil { + return fmt.Errorf("UpdateContractCode (addr=%x) error while getting account header: %w", addr[:], err) + } + var cs [8]byte + binary.LittleEndian.PutUint64(cs[:], uint64(len(code))) + copy(headervals[utils.BasicDataLeafKey][utils.BasicDataCodeSizeOffset:], cs[5:8]) + values[utils.BasicDataLeafKey] = headervals[utils.BasicDataLeafKey] } if groupOffset == 255 || len(chunks)-i <= 32 { diff --git a/trie/verkle_iterator.go b/trie/verkle_iterator.go index 16de8746b6d5..e24abf87abf2 100644 --- a/trie/verkle_iterator.go +++ b/trie/verkle_iterator.go @@ -35,7 +35,7 @@ type verkleNodeIterator struct { stack []verkleNodeIteratorState } -func newVerkleNodeIterator(trie *VerkleTrie, start []byte) (NodeIterator, error) { +func newVerkleNodeIterator(trie *VerkleTrie, _ []byte) (NodeIterator, error) { if trie.Hash() == zero { return new(nodeIterator), nil } diff --git a/trie/verkle_iterator_test.go b/trie/verkle_iterator_test.go index d1611feee32c..ce59a21e60e4 100644 --- a/trie/verkle_iterator_test.go +++ b/trie/verkle_iterator_test.go @@ -62,7 +62,7 @@ func TestVerkleIterator(t *testing.T) { t.Logf("\tLeaf: %x", it.LeafKey()) } } - if leafcount != 6 { + if leafcount != 2 { t.Fatalf("invalid leaf count: %d != 6", leafcount) } } diff --git a/trie/verkle_test.go b/trie/verkle_test.go index 8a4fb921bac9..5118338d9d39 100644 --- a/trie/verkle_test.go +++ b/trie/verkle_test.go @@ -372,7 +372,7 @@ func TestEmptyKeySetInProveAndSerialize(t *testing.T) { func TestGetTreeKeys(t *testing.T) { addr := common.Hex2Bytes("71562b71999873DB5b286dF957af199Ec94617f7") target := common.Hex2Bytes("1540dfad7755b40be0768c6aa0a5096fbf0215e0e8cf354dd928a17834646600") - key := utils.GetTreeKeyVersion(addr) + key := utils.GetTreeKeyBasicData(addr) t.Logf("key=%x", key) t.Logf("actualKey=%x", target) if !bytes.Equal(key, target) {