diff --git a/.github/workflows/spec-tests.yml b/.github/workflows/spec-tests.yml index 2de4a05fb50c..14b98cc39de4 100644 --- a/.github/workflows/spec-tests.yml +++ b/.github/workflows/spec-tests.yml @@ -8,8 +8,8 @@ on: workflow_dispatch: env: - EEST_USER: "ethereum" - EEST_BRANCH: "verkle/main" + EEST_USER: "jsign" + EEST_BRANCH: "jsign/fix-test" jobs: setup: diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 5a8723761f87..76caca0fbc7b 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -526,7 +526,7 @@ func VerkleKey(ctx *cli.Context) error { } fmt.Printf("%#x\n", utils.GetTreeKeyStorageSlotWithEvaluatedAddress(ap, slot)) } else { - fmt.Printf("%#x\n", utils.GetTreeKeyVersionWithEvaluatedAddress(ap)) + fmt.Printf("%#x\n", utils.GetTreeKeyBasicDataEvaluatedAddress(ap)) } return nil } @@ -616,7 +616,7 @@ func genVktFromAlloc(alloc core.GenesisAlloc) (*trie.VerkleTrie, error) { CodeHash: crypto.Keccak256Hash(acc.Code).Bytes(), Root: common.Hash{}, } - err := vkt.UpdateAccount(addr, account) + err := vkt.UpdateAccount(addr, account, len(acc.Code)) if err != nil { return nil, fmt.Errorf("error inserting account: %w", err) } 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..d98c081eec76 100644 --- a/core/overlay/conversion.go +++ b/core/overlay/conversion.go @@ -95,29 +95,24 @@ 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 + binary.BigEndian.PutUint64(basicData[utils.BasicDataNonceOffset:], acc.Nonce) - var balance [verkle.LeafValueSize]byte - for i, b := range acc.Balance.Bytes() { - balance[len(acc.Balance.Bytes())-1-i] = b - } - leafNodeData.Values[utils.BalanceLeafKey] = balance[:] - - var nonce [verkle.LeafValueSize]byte - binary.LittleEndian.PutUint64(nonce[:8], acc.Nonce) - leafNodeData.Values[utils.NonceLeafKey] = nonce[:] + // get the lower 16 bytes of water and change its endianness + balanceBytes := acc.Balance.Bytes() + copy(basicData[32-len(balanceBytes):], balanceBytes[:]) + leafNodeData.Values[utils.BasicDataLeafKey] = basicData[:] leafNodeData.Values[utils.CodeHashLeafKey] = acc.CodeHash[:] } +// addAccountCode needs to be called AFTER addAccount, as it will reuse the leaf 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[:] + binary.BigEndian.PutUint32(leafNodeData.Values[utils.BasicDataLeafKey][utils.BasicDataCodeSizeOffset-1:utils.BasicDataNonceOffset], uint32(codeSize)) // 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 fa10ca839c3b..72fb15888ff8 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -147,7 +147,7 @@ type Trie interface { // UpdateAccount abstracts an account write to the trie. It encodes the // provided account object with associated algorithm and then updates it // in the trie with provided address. - UpdateAccount(address common.Address, account *types.StateAccount) error + UpdateAccount(address common.Address, account *types.StateAccount, codeLen int) error // UpdateContractCode abstracts code write to the trie. It is expected // to be moved to the stateWriter interface when the latter is ready. @@ -340,7 +340,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/statedb.go b/core/state/statedb.go index 4f64d19f4a3e..bbc7b649365c 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -576,7 +576,7 @@ func (s *StateDB) updateStateObject(obj *stateObject) { } // Encode the account and update the account trie addr := obj.Address() - if err := s.trie.UpdateAccount(addr, &obj.data); err != nil { + if err := s.trie.UpdateAccount(addr, &obj.data, len(obj.code)); err != nil { s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err)) } if obj.dirtyCode { diff --git a/core/state_processor_test.go b/core/state_processor_test.go index a29252598f2b..2cb9266b7a00 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 + + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */ + 739 /* execution costs */ + codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + + 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, @@ -734,8 +775,8 @@ func TestProcessVerkleInvalidContractCreation(t *testing.T) { if stemStateDiff.SuffixDiffs[0].NewValue == nil { t.Fatalf("missing post state value for BLOCKHASH contract at block #2") } - if *stemStateDiff.SuffixDiffs[0].NewValue != common.HexToHash("0a130e6478e47593861d8c3feb65045497327d89619dd12ae12d70e73a0191dd") { - t.Fatalf("invalid post state value for BLOCKHASH contract at block #2: 0a130e6478e47593861d8c3feb65045497327d89619dd12ae12d70e73a0191dd != %x", (*stemStateDiff.SuffixDiffs[0].NewValue)[:]) + if *stemStateDiff.SuffixDiffs[0].NewValue != common.HexToHash("ac9ab8a7d88cfee11ebcda5f47232c07fcb393c8916e37fa67eb5e315b1f8ef6") { + t.Fatalf("invalid post state value for BLOCKHASH contract at block #2: ac9ab8a7d88cfee11ebcda5f47232c07fcb393c8916e37fa67eb5e315b1f8ef6 != %x", (*stemStateDiff.SuffixDiffs[0].NewValue)[:]) } } else if suffixDiff.Suffix > 4 { t.Fatalf("invalid suffix diff found for %x in block #2: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) @@ -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] { @@ -1161,25 +1202,25 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { t.Fatalf("no state diff found for stem") } - balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[1] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { t.Fatalf("balance invalid suffix") } // The original balance was 42. - var fourtyTwo [32]byte - fourtyTwo[0] = 42 - if *balanceStateDiff.CurrentValue != fourtyTwo { - t.Fatalf("the pre-state balance before self-destruct must be 42") + var fourtyTwo [16]byte + fourtyTwo[15] = 42 + if !bytes.Equal((*balanceStateDiff.CurrentValue)[utils.BasicDataBalanceOffset:], fourtyTwo[:]) { + t.Fatalf("the pre-state balance before self-destruct must be %x, got %x", fourtyTwo, *balanceStateDiff.CurrentValue) } // The new balance must be 0. - if *balanceStateDiff.NewValue != zero { + if !bytes.Equal((*balanceStateDiff.NewValue)[utils.BasicDataBalanceOffset:], zero[utils.BasicDataBalanceOffset:]) { t.Fatalf("the post-state balance after self-destruct must be 0") } } { // 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 { @@ -1202,10 +1243,10 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { if balanceStateDiff.NewValue == nil { t.Fatalf("codeHash.NewValue must not be empty") } - preStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.CurrentValue[:]) - postStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.NewValue[:]) + preStateBalance := binary.BigEndian.Uint64(balanceStateDiff.CurrentValue[utils.BasicDataBalanceOffset+8:]) + postStateBalance := binary.BigEndian.Uint64(balanceStateDiff.NewValue[utils.BasicDataBalanceOffset+8:]) if postStateBalance-preStateBalance != 42 { - t.Fatalf("the post-state balance after self-destruct must be 42") + t.Fatalf("the post-state balance after self-destruct must be 42, got %d-%d=%d", postStateBalance, preStateBalance, postStateBalance-preStateBalance) } } } @@ -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] { @@ -1289,8 +1330,8 @@ func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { t.Fatalf("no state diff found for stem") } - balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[1] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[0] + 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 { @@ -1326,10 +1367,10 @@ func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { if balanceStateDiff.NewValue == nil { t.Fatalf("codeHash.NewValue must not be empty") } - preStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.CurrentValue[:]) - postStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.NewValue[:]) + preStateBalance := binary.BigEndian.Uint64(balanceStateDiff.CurrentValue[utils.BasicDataBalanceOffset+8:]) + postStateBalance := binary.BigEndian.Uint64(balanceStateDiff.NewValue[utils.BasicDataBalanceOffset+8:]) if postStateBalance-preStateBalance != 42 { - t.Fatalf("the post-state balance after self-destruct must be 42") + t.Fatalf("the post-state balance after self-destruct must be 42. got %d", postStateBalance) } } } @@ -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] { @@ -1435,15 +1476,15 @@ func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T) t.Fatalf("no state diff found for stem") } - balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[1] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { t.Fatalf("balance invalid suffix") } // The original balance was 42. - var fourtyTwo [32]byte - fourtyTwo[0] = 42 - if *balanceStateDiff.CurrentValue != fourtyTwo { + var fourtyTwo [16]byte + fourtyTwo[15] = 42 + if !bytes.Equal((*balanceStateDiff.CurrentValue)[utils.BasicDataBalanceOffset:], fourtyTwo[:]) { t.Fatalf("the pre-state balance before self-destruct must be 42") } @@ -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] { @@ -1534,8 +1575,8 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) { t.Fatalf("no state diff found for stem") } - balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[1] - if balanceStateDiff.Suffix != utils.BalanceLeafKey { + balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[0] + 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/light/trie.go b/light/trie.go index 2f5999230896..fd7e4e472f21 100644 --- a/light/trie.go +++ b/light/trie.go @@ -223,7 +223,7 @@ func (t *odrTrie) GetAccount(address common.Address) (*types.StateAccount, error return acct, nil } -func (t *odrTrie) UpdateAccount(address common.Address, acc *types.StateAccount) error { +func (t *odrTrie) UpdateAccount(address common.Address, acc *types.StateAccount, _ int) error { key := crypto.Keccak256(address.Bytes()) value, err := rlp.EncodeToBytes(acc) if err != nil { diff --git a/trie/secure_trie.go b/trie/secure_trie.go index f4a999c2f68f..9198f595796e 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -163,7 +163,7 @@ func (t *StateTrie) UpdateStorage(_ common.Address, key, value []byte) error { } // UpdateAccount will abstract the write of an account to the secure trie. -func (t *StateTrie) UpdateAccount(address common.Address, acc *types.StateAccount) error { +func (t *StateTrie) UpdateAccount(address common.Address, acc *types.StateAccount, _ int) error { hk := t.hashKey(address.Bytes()) data, err := rlp.EncodeToBytes(acc) if err != nil { diff --git a/trie/transition.go b/trie/transition.go index 0fe197336524..61e61d2d8d56 100644 --- a/trie/transition.go +++ b/trie/transition.go @@ -113,11 +113,11 @@ func (t *TransitionTrie) UpdateStorage(address common.Address, key []byte, value } // UpdateAccount abstract an account write to the trie. -func (t *TransitionTrie) UpdateAccount(addr common.Address, account *types.StateAccount) error { +func (t *TransitionTrie) UpdateAccount(addr common.Address, account *types.StateAccount, codeLen int) error { if account.Root != (common.Hash{}) && account.Root != types.EmptyRootHash { t.overlay.db.SetStorageRootConversion(addr, account.Root) } - return t.overlay.UpdateAccount(addr, account) + return t.overlay.UpdateAccount(addr, account, codeLen) } // Delete removes any existing value for key from the trie. If a node was not 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..ed34deebc3e2 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,62 +128,45 @@ 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.BigEndian.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 { return nil, nil } } - 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] - } - // 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] - // } - // } + var balance [16]byte + copy(balance[:], values[utils.BasicDataLeafKey][utils.BasicDataBalanceOffset:]) acc.Balance = new(big.Int).SetBytes(balance[:]) acc.CodeHash = values[utils.CodeHashLeafKey] - // TODO fix the code size as well return acc, nil } var zero [32]byte -func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) error { +func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount, codeLen int) 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[:] + binary.BigEndian.PutUint32(basicData[utils.BasicDataCodeSizeOffset-1:], uint32(codeLen)) + binary.BigEndian.PutUint64(basicData[utils.BasicDataNonceOffset:], acc.Nonce) + balanceBytes := acc.Balance.Bytes() + copy(basicData[32-len(balanceBytes):], balanceBytes[:]) + values[utils.BasicDataLeafKey] = basicData[:] 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 - } - } - switch root := t.root.(type) { case *verkle.InternalNode: err = root.InsertValuesAtStem(stem, values, t.FlatdbNodeResolver) @@ -193,7 +176,6 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) if err != nil { return fmt.Errorf("UpdateAccount (%x) error: %v", addr, err) } - // TODO figure out if the code size needs to be updated, too return nil } @@ -448,6 +430,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) @@ -463,13 +446,6 @@ func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Has } values[groupOffset] = chunks[i : i+32] - // 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 - } - if groupOffset == 255 || len(chunks)-i <= 32 { err = t.UpdateStem(key[:31], values) 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..42f8f9fd3406 100644 --- a/trie/verkle_iterator_test.go +++ b/trie/verkle_iterator_test.go @@ -37,7 +37,7 @@ func TestVerkleIterator(t *testing.T) { } // NOTE: the code size isn't written to the trie via TryUpdateAccount // so it will be missing from the test nodes. - trie.UpdateAccount(common.Address{}, account0) + trie.UpdateAccount(common.Address{}, account0, 0) account1 := &types.StateAccount{ Nonce: 1337, Balance: big.NewInt(2000), @@ -46,7 +46,7 @@ func TestVerkleIterator(t *testing.T) { } // This address is meant to hash to a value that has the same first byte as 0xbf var clash = common.HexToAddress("69fd8034cdb20934dedffa7dccb4fb3b8062a8be") - trie.UpdateAccount(clash, account1) + trie.UpdateAccount(clash, account1, 0) // Manually go over every node to check that we get all // the correct nodes. @@ -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) {