From 99bf4e2bb288b1bb5e6d51813cfc282a9cad8761 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Tue, 30 Nov 2021 04:11:42 +0000 Subject: [PATCH 1/5] wip access witness gas charging for writes --- consensus/ethash/consensus.go | 2 +- core/state_transition.go | 10 ++-- core/types/access_witness.go | 105 +++++++++++++++++++++++++++++----- core/vm/evm.go | 37 ++++++++++-- params/protocol_params.go | 10 +++- 5 files changed, 136 insertions(+), 28 deletions(-) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 8dd95be64c89..f1c5998b3777 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -669,5 +669,5 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header } state.AddBalance(header.Coinbase, reward) coinbase := utils.GetTreeKeyBalance(header.Coinbase.Bytes()) - state.Witness().TouchAddress(coinbase, state.GetBalance(header.Coinbase).Bytes()) + state.Witness().TouchAddress(coinbase, state.GetBalance(header.Coinbase).Bytes(), true) } diff --git a/core/state_transition.go b/core/state_transition.go index 393e620ad860..772790c56a8c 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -308,22 +308,24 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if msg.To() != nil { toBalance := trieUtils.GetTreeKeyBalance(msg.To().Bytes()) pre := st.state.GetBalance(*msg.To()) - gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(toBalance, pre.Bytes()) + if !msg.Value().Cmp(big.NewInt(0)) != 0 { + gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(toBalance, pre.Bytes(), true) + } // NOTE: Nonce also needs to be charged, because it is needed for execution // on the statless side. var preTN [8]byte fromNonce := trieUtils.GetTreeKeyNonce(msg.To().Bytes()) binary.BigEndian.PutUint64(preTN[:], st.state.GetNonce(*msg.To())) - gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromNonce, preTN[:]) + gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromNonce, preTN[:], true) } fromBalance := trieUtils.GetTreeKeyBalance(msg.From().Bytes()) preFB := st.state.GetBalance(msg.From()).Bytes() fromNonce := trieUtils.GetTreeKeyNonce(msg.From().Bytes()) var preFN [8]byte binary.BigEndian.PutUint64(preFN[:], st.state.GetNonce(msg.From())) - gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromNonce, preFN[:]) - gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromBalance, preFB[:]) + gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromNonce, preFN[:], true) + gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromBalance, preFB[:], true) } st.gas -= gas diff --git a/core/types/access_witness.go b/core/types/access_witness.go index 3fe6b78dc555..c80cc73d5f74 100644 --- a/core/types/access_witness.go +++ b/core/types/access_witness.go @@ -26,7 +26,12 @@ import ( // TODO(@gballet) this doesn't fully support deletions type AccessWitness struct { // Branches flags if a given branch has been loaded - Branches map[[31]byte]struct{} + // for the byte value: + // the first bit is set if the branch has been edited + // the second bit is set if the branch has been read + Branches map[[31]byte]byte + + LeavesAccesses map[common.Hash]byte // Chunks contains the initial value of each address Chunks map[common.Hash][]byte @@ -37,6 +42,7 @@ type AccessWitness struct { Undefined map[common.Hash]struct{} } + func NewAccessWitness() *AccessWitness { return &AccessWitness{ Branches: make(map[[31]byte]struct{}), @@ -45,23 +51,61 @@ func NewAccessWitness() *AccessWitness { } } +const ( + AccessWitnessReadFlag = 1 + AccessWitnessWriteFlag = 2 +) + +func (aw *AccessWitness) touchAddressOnWrite(addr, value []byte) (bool, bool) { + var stem [31]byte + copy(stem[:], addr[:31]) + + var stemWrite, chunkWrite, chunkFill bool + + // NOTE: stem, selector access flags already exist in their + // respective maps because this function is called at the end of + // processing a read access event + + if !(aw.Branches[stem] & AccessWitnessWriteFlag) { + stemWrite = true + aw.Branches[stem] |= AccessWitnessWriteFlag + } + + if aw.LeafAccesses[common.BytesToHash(addr)] & AccessWitnessWriteFlag { + chunkWrite = true + aw.LeafAccesses[common.BytesToHash(addr)] |= AccessWitnessWriteFlag + } + + // TODO charge chunk filling costs if the leaf was previously empty in the state + /* + if chunkWrite { + if _, err := verkleDb.TryGet(addr); err != nil { + chunkFill = true + } + } + */ + + return stemWrite, chunkWrite, chunkFill +} + // TouchAddress adds any missing addr to the witness and returns respectively // true if the stem or the stub weren't arleady present. -func (aw *AccessWitness) TouchAddress(addr, value []byte) (bool, bool) { +func (aw *AccessWitness) TouchAddress(addr, value []byte, isWrite bool) (bool, bool, bool, bool, bool) { var ( stem [31]byte - newStem bool - newSelector bool + stemRead bool + selectorRead bool ) copy(stem[:], addr[:31]) // Check for the presence of the stem - if _, newStem := aw.Branches[stem]; !newStem { - aw.Branches[stem] = struct{}{} + if _, hasStem := aw.Branches[stem]; !hasStem { + stemRead = true + aw.Branches[stem] = AccessWitnessReadFlag } // Check for the presence of the selector - if _, newSelector := aw.Chunks[common.BytesToHash(addr)]; !newSelector { + if _, hasSelector := aw.Chunks[common.BytesToHash(addr)]; !hasSelector { if value == nil { aw.Undefined[common.BytesToHash(addr)] = struct{}{} } else { @@ -70,25 +114,50 @@ func (aw *AccessWitness) TouchAddress(addr, value []byte) (bool, bool) { } aw.Chunks[common.BytesToHash(addr)] = value } + + if accessFlags, hasAccessFlags = :aw.LeafAccesses; hasAccessFlags { + if !(aw.LeafAccesses[common.BytesToHash(addr)] & AccessWitnessRead) + aw.LeafAccesses[common.BytesToHash(addr)] |= AccessWitnessRead + } + } else { + aw.LeafAccesses[common.BytesToHash(addr)] = AccessWitnessRead + + } + } + + var stemWrite, chunkWrite, chunkFill bool + + if isWrite { + stemWrite, selectorWrite, chunkFill := aw.touchAddressOnWrite(addr, value) } - return newStem, newSelector + return stemRead, selectorRead, stemWrite, selectorWrite, chunkFill } // TouchAddressAndChargeGas checks if a location has already been touched in // the current witness, and charge extra gas if that isn't the case. This is // meant to only be called on a tx-context access witness (i.e. before it is // merged), not a block-context witness: witness costs are charged per tx. -func (aw *AccessWitness) TouchAddressAndChargeGas(addr, value []byte) uint64 { +func (aw *AccessWitness) TouchAddressAndChargeGas(addr, value []byte, isWrite bool) uint64 { var gas uint64 - nstem, nsel := aw.TouchAddress(addr, value) - if nstem { - gas += params.WitnessBranchCost + stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := aw.TouchAddress(addr, value, isWrite) + if stemRead { + gas += params.WitnessBranchReadCost + } + if selectorRead { + gas += params.WitnessChunkReadCost + } + if stemWrite { + gas += params.WitnessBranchWriteCost + } + if selectorWrite { + gas += params.WitnessChunkWriteCost } - if nsel { - gas += params.WitnessChunkCost + if selectorFill { + gas += params.WitnessChunkFillCost } + return gas } @@ -104,7 +173,7 @@ func (aw *AccessWitness) Merge(other *AccessWitness) { for k := range other.Branches { if _, ok := aw.Branches[k]; !ok { - aw.Branches[k] = struct{}{} + aw.Branches[k] = other.Branches[k] } } @@ -113,6 +182,12 @@ func (aw *AccessWitness) Merge(other *AccessWitness) { aw.Chunks[k] = chunk } } + + for k, leafAccessFlags := range other.LeafAccesses { + if _, ok := aw.LeafAccesses[k]; !ok { + aw.LeafAccesses[k] = leafAccessFlags + } + } } // Key returns, predictably, the list of keys that were touched during the diff --git a/core/vm/evm.go b/core/vm/evm.go index f5bdff8303f2..18c2ea34538f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -170,6 +170,18 @@ func (evm *EVM) Interpreter() *EVMInterpreter { return evm.interpreter } +// try to consume an amount of gas, if the amount consumed +// is greater than the amount left, set amount left to 0 and return false +func tryConsumeGas(gasLeft *uint64, gasConsumed uint64) bool { + if gasConsumed > *gasLeft { + *gas = 0 + return false + } + + *gasLeft -= gasConsumed + return true +} + // Call executes the contract associated with the addr with the given input as // parameters. It also handles any necessary value transfer required and takes // the necessary steps to create accounts and reverses the state in case of an @@ -234,13 +246,28 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } else { // Touch the account data var data [32]byte - evm.Accesses.TouchAddress(utils.GetTreeKeyVersion(addr.Bytes()), data[:]) + if !tryConsumeGas(evm.Accesses.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr.Bytes()), data[:]), false) { + evm.StateDB.RevertToSnapshot(snapshot) + return []byte{}, ErrOutOfGas, gas + } binary.BigEndian.PutUint64(data[:], evm.StateDB.GetNonce(addr)) - evm.Accesses.TouchAddress(utils.GetTreeKeyNonce(addr[:]), data[:]) - evm.Accesses.TouchAddress(utils.GetTreeKeyBalance(addr[:]), evm.StateDB.GetBalance(addr).Bytes()) + if !tryConsumeGas(&gas, evm.Accesses.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(addr[:]), data[:], false)) { + evm.StateDB.RevertToSnapshot(snapshot) + return []byte{}, ErrOutOfGas, gas + } + if !tryConsumeGas(evm.Accesses.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(addr[:]), evm.StateDB.GetBalance(addr).Bytes()), false) { + evm.StateDB.RevertToSnapshot(snapshot) + return []byte{}, ErrOutOfGas, gas + } binary.BigEndian.PutUint64(data[:], uint64(len(code))) - evm.Accesses.TouchAddress(utils.GetTreeKeyCodeSize(addr[:]), data[:]) - evm.Accesses.TouchAddress(utils.GetTreeKeyCodeKeccak(addr[:]), evm.StateDB.GetCodeHash(addr).Bytes()) + if !tryConsumeGas(evm.Accesses.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(addr[:]), data[:]), false) { + evm.StateDB.RevertToSnapshot(snapshot) + return []byte{}, ErrOutOfGas, gas + } + if !tryConsumeGas(evm.Accesses.TouchAddress(utils.GetTreeKeyCodeKeccak(addr[:]), evm.StateDB.GetCodeHash(addr).Bytes())) { + evm.StateDB.RevertToSnapshot(snapshot) + return []byte{}, ErrOutOfGas, gas + } addrCopy := addr // If the account has no code, we can abort here diff --git a/params/protocol_params.go b/params/protocol_params.go index fc0e83a18a01..0f7930037831 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -157,9 +157,13 @@ const ( RefundQuotient uint64 = 2 RefundQuotientEIP3529 uint64 = 5 - // Verkle tree EIP: costs associated to witness accesses - WitnessBranchCost = uint64(1900) - WitnessChunkCost = uint64(200) + // Verkle tree EIP: costs associated to witness accesses + WitnessBranchReadCost = uint64(1900) + WitnessChunkReadCost = uint64(200) + WitnessBranchWriteCost = uint64(3000) + WitnessChunkWriteCost = uint64(500) + WitnessChunkFillCost = uint64(6200) + ) // Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations From 0ede6e2a2e0ce11da9257358ce655e29c6e608b6 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Tue, 30 Nov 2021 07:09:34 +0000 Subject: [PATCH 2/5] wip --- core/types/access_witness.go | 86 +++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/core/types/access_witness.go b/core/types/access_witness.go index c80cc73d5f74..7db1371ecd03 100644 --- a/core/types/access_witness.go +++ b/core/types/access_witness.go @@ -21,6 +21,13 @@ import ( "github.com/ethereum/go-ethereum/params" ) +type VerkleStem [31]byte + +type ChunkValue struct { + mode byte + value []byte +} + // AccessWitness lists the locations of the state that are being accessed // during the production of a block. // TODO(@gballet) this doesn't fully support deletions @@ -29,25 +36,16 @@ type AccessWitness struct { // for the byte value: // the first bit is set if the branch has been edited // the second bit is set if the branch has been read - Branches map[[31]byte]byte - - LeavesAccesses map[common.Hash]byte + Branches map[VerkleStem]byte // Chunks contains the initial value of each address - Chunks map[common.Hash][]byte - - // The initial value isn't always available at the time an - // address is touched, this map references addresses that - // were touched but can not yet be put in Chunks. - Undefined map[common.Hash]struct{} + Chunks map[common.Hash]ChunkValue } - func NewAccessWitness() *AccessWitness { return &AccessWitness{ - Branches: make(map[[31]byte]struct{}), - Chunks: make(map[common.Hash][]byte), - Undefined: make(map[common.Hash]struct{}), + Branches: make(map[VerkleStem]struct{}), + Chunks: make(map[common.Hash]ChunkValue), } } @@ -56,11 +54,22 @@ const ( AccessWitnessWriteFlag = 2 ) -func (aw *AccessWitness) touchAddressOnWrite(addr, value []byte) (bool, bool) { - var stem [31]byte - copy(stem[:], addr[:31]) +// because of the way Geth's EVM is implemented, the gas cost of an operation +// may be needed before the value of the leaf-key can be retrieved. Hence, we +// break witness access (for the purpose of gas accounting), and filling witness +// values into two methods +func (aw *AccessWitness) SetLeafValue(addr, value []byte) { + if chunk, exists := aw.Chunks[addr]; exists { + chunk.value = value + } else { + panic(fmt.Sprintf("address not in access witness: %x", addr)) + } +} +func (aw *AccessWitness) touchAddressOnWrite(addr, value []byte) (bool, bool, bool) { + var stem VerkleStem var stemWrite, chunkWrite, chunkFill bool + copy(stem[:], addr[:31]) // NOTE: stem, selector access flags already exist in their // respective maps because this function is called at the end of @@ -71,9 +80,9 @@ func (aw *AccessWitness) touchAddressOnWrite(addr, value []byte) (bool, bool) { aw.Branches[stem] |= AccessWitnessWriteFlag } - if aw.LeafAccesses[common.BytesToHash(addr)] & AccessWitnessWriteFlag { + if aw.Chunks[common.BytesToHash(addr)] & AccessWitnessWriteFlag { chunkWrite = true - aw.LeafAccesses[common.BytesToHash(addr)] |= AccessWitnessWriteFlag + aw.Chunks[common.BytesToHash(addr)].mode |= AccessWitnessWriteFlag } // TODO charge chunk filling costs if the leaf was previously empty in the state @@ -90,7 +99,7 @@ func (aw *AccessWitness) touchAddressOnWrite(addr, value []byte) (bool, bool) { // TouchAddress adds any missing addr to the witness and returns respectively // true if the stem or the stub weren't arleady present. -func (aw *AccessWitness) TouchAddress(addr, value []byte, isWrite bool) (bool, bool, bool, bool, bool) { +func (aw *AccessWitness) touchAddress(addr []byte, isWrite bool) (bool, bool, bool, bool, bool) { var ( stem [31]byte stemRead bool @@ -104,24 +113,15 @@ func (aw *AccessWitness) TouchAddress(addr, value []byte, isWrite bool) (bool, b aw.Branches[stem] = AccessWitnessReadFlag } - // Check for the presence of the selector - if _, hasSelector := aw.Chunks[common.BytesToHash(addr)]; !hasSelector { - if value == nil { - aw.Undefined[common.BytesToHash(addr)] = struct{}{} - } else { - if _, ok := aw.Undefined[common.BytesToHash(addr)]; !ok { - delete(aw.Undefined, common.BytesToHash(addr)) - } - aw.Chunks[common.BytesToHash(addr)] = value - } - - if accessFlags, hasAccessFlags = :aw.LeafAccesses; hasAccessFlags { - if !(aw.LeafAccesses[common.BytesToHash(addr)] & AccessWitnessRead) - aw.LeafAccesses[common.BytesToHash(addr)] |= AccessWitnessRead - } - } else { - aw.LeafAccesses[common.BytesToHash(addr)] = AccessWitnessRead + // always charge read cost whether the access event is read/write + // literal interpretation of the spec + selectorRead = true + // Check for the presence of the leaf selector + if _, hasSelector := aw.Chunks[common.BytesToHash(addr)]; !hasSelector { + aw.Chunks[common.BytesToHash(addr)] = ChunkValue{ + AccessWitnessReadFlag, + nil, } } @@ -134,11 +134,7 @@ func (aw *AccessWitness) TouchAddress(addr, value []byte, isWrite bool) (bool, b return stemRead, selectorRead, stemWrite, selectorWrite, chunkFill } -// TouchAddressAndChargeGas checks if a location has already been touched in -// the current witness, and charge extra gas if that isn't the case. This is -// meant to only be called on a tx-context access witness (i.e. before it is -// merged), not a block-context witness: witness costs are charged per tx. -func (aw *AccessWitness) TouchAddressAndChargeGas(addr, value []byte, isWrite bool) uint64 { +func (aw *AccessWitness) touchAddressAndChargeGas(addr, value []byte, isWrite bool) uint64 { var gas uint64 stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := aw.TouchAddress(addr, value, isWrite) @@ -161,6 +157,14 @@ func (aw *AccessWitness) TouchAddressAndChargeGas(addr, value []byte, isWrite bo return gas } +func (aw *AccessWitness) TouchAddressOnWriteAndChargeGas(addr []byte) uint64 { + touchAddressAndChargeGas(addr, true) +} + +func (aw *AccessWitness) TouchAddressOnReadAndChargeGas(addr []byte) uint64 { + touchAddressAndChargeGas(addr, false) +} + // Merge is used to merge the witness that got generated during the execution // of a tx, with the accumulation of witnesses that were generated during the // execution of all the txs preceding this one in a given block. From 2c421d2ac79cff11101a0b966e34a390a227f679 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Tue, 30 Nov 2021 08:25:18 +0000 Subject: [PATCH 3/5] wip --- consensus/ethash/consensus.go | 3 ++- core/state_transition.go | 12 ++++++++---- core/vm/evm.go | 23 +++++++++++++++-------- core/vm/gas_table.go | 10 +++++----- core/vm/instructions.go | 15 +++++++++------ core/vm/interpreter.go | 3 ++- 6 files changed, 41 insertions(+), 25 deletions(-) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index f1c5998b3777..33797305d548 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -669,5 +669,6 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header } state.AddBalance(header.Coinbase, reward) coinbase := utils.GetTreeKeyBalance(header.Coinbase.Bytes()) - state.Witness().TouchAddress(coinbase, state.GetBalance(header.Coinbase).Bytes(), true) + state.Witness().TouchAddressOnReadAndChargeGas(coinbase) + state.Witness().SetLeafValue(coinbase, state.GetBalance(header.Coinbase).Bytes()) } diff --git a/core/state_transition.go b/core/state_transition.go index 772790c56a8c..6939016879a9 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -309,7 +309,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { toBalance := trieUtils.GetTreeKeyBalance(msg.To().Bytes()) pre := st.state.GetBalance(*msg.To()) if !msg.Value().Cmp(big.NewInt(0)) != 0 { - gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(toBalance, pre.Bytes(), true) + gas += st.evm.TxContext.Accesses.TouchAddressOnWriteAndChargeGas(toBalance) + st.evmTxContext.Accesses.SetLeafValue(toBalance, pre.Bytes()) } // NOTE: Nonce also needs to be charged, because it is needed for execution @@ -317,15 +318,18 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { var preTN [8]byte fromNonce := trieUtils.GetTreeKeyNonce(msg.To().Bytes()) binary.BigEndian.PutUint64(preTN[:], st.state.GetNonce(*msg.To())) - gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromNonce, preTN[:], true) + gas += st.evm.TxContext.Accesses.TouchAddressOnWriteAndChargeGas(fromNonce) + st.evm.TxContext.Accesses.SetLeafValue(fromNonce, preTN[:]) } fromBalance := trieUtils.GetTreeKeyBalance(msg.From().Bytes()) preFB := st.state.GetBalance(msg.From()).Bytes() fromNonce := trieUtils.GetTreeKeyNonce(msg.From().Bytes()) var preFN [8]byte binary.BigEndian.PutUint64(preFN[:], st.state.GetNonce(msg.From())) - gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromNonce, preFN[:], true) - gas += st.evm.TxContext.Accesses.TouchAddressAndChargeGas(fromBalance, preFB[:], true) + gas += st.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(fromNonce) + st.evm.TxContext.SetLeafValue(fromNonce, preFN[:]) + gas += st.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(fromBalance) + st.evm.TxContext.SetLeafValue(fromBalance, preFB[:]) } st.gas -= gas diff --git a/core/vm/evm.go b/core/vm/evm.go index 18c2ea34538f..7bf6e883d68c 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -170,14 +170,16 @@ func (evm *EVM) Interpreter() *EVMInterpreter { return evm.interpreter } -// try to consume an amount of gas, if the amount consumed -// is greater than the amount left, set amount left to 0 and return false -func tryConsumeGas(gasLeft *uint64, gasConsumed uint64) bool { +func (evm *EVM) tryTouchWitnessAndConsumeGas(key, value []byte, gasLeft *uint64) bool { + gasConsumed := evm.Accesses.TouchAddressOnReadAndChargeGas(key) + if gasConsumed > *gasLeft { *gas = 0 return false } + evm.Accesses.SetLeafValue(key, value) + *gasLeft -= gasConsumed return true } @@ -246,25 +248,30 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } else { // Touch the account data var data [32]byte - if !tryConsumeGas(evm.Accesses.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr.Bytes()), data[:]), false) { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyVersion(addr.Bytes()), data[:], gas) + { evm.StateDB.RevertToSnapshot(snapshot) return []byte{}, ErrOutOfGas, gas } binary.BigEndian.PutUint64(data[:], evm.StateDB.GetNonce(addr)) - if !tryConsumeGas(&gas, evm.Accesses.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(addr[:]), data[:], false)) { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyNonce(addr.Bytes()), data[:], gas) + { evm.StateDB.RevertToSnapshot(snapshot) return []byte{}, ErrOutOfGas, gas } - if !tryConsumeGas(evm.Accesses.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(addr[:]), evm.StateDB.GetBalance(addr).Bytes()), false) { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyBalance(addr.Bytes()), data[:], gas) + { evm.StateDB.RevertToSnapshot(snapshot) return []byte{}, ErrOutOfGas, gas } binary.BigEndian.PutUint64(data[:], uint64(len(code))) - if !tryConsumeGas(evm.Accesses.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(addr[:]), data[:]), false) { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyCodeSize(addr.Bytes()), data[:], gas) + { evm.StateDB.RevertToSnapshot(snapshot) return []byte{}, ErrOutOfGas, gas } - if !tryConsumeGas(evm.Accesses.TouchAddress(utils.GetTreeKeyCodeKeccak(addr[:]), evm.StateDB.GetCodeHash(addr).Bytes())) { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyCodeKeccak(addr.Bytes()), data[:], gas) + { evm.StateDB.RevertToSnapshot(snapshot) return []byte{}, ErrOutOfGas, gas } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index c42f0e61152d..306b2bf621b1 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -93,7 +93,7 @@ func gasExtCodeSize(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem slot := stack.Back(0) if evm.accesses != nil { index := trieUtils.GetTreeKeyCodeSize(slot.Bytes()) - usedGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil) + usedGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) } return usedGas, nil @@ -129,7 +129,7 @@ func gasCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory // TODO make a version of GetTreeKeyCodeChunk without the bigint index := trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(chunk)) - statelessGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil) + statelessGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) } } @@ -160,7 +160,7 @@ func gasExtCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem for ; chunk < endChunk; chunk++ { // TODO(@gballet) make a version of GetTreeKeyCodeChunk without the bigint index := trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(chunk)) - statelessGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil) + statelessGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) } } @@ -175,7 +175,7 @@ func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySiz where := stack.Back(0) addr := contract.Address() index := trieUtils.GetTreeKeyStorageSlot(addr[:], where) - usedGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil) + usedGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) } return usedGas, nil @@ -426,7 +426,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize // Charge witness costs for i := trieUtils.VersionLeafKey; i <= trieUtils.CodeSizeLeafKey; i++ { index := trieUtils.GetTreeKeyAccountLeaf(address[:], byte(i)) - gas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil) + gas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index fdc6e3a37c27..77a9a024d6e1 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -345,7 +345,7 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) cs := uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())) if interpreter.evm.accesses != nil { index := trieUtils.GetTreeKeyCodeSize(slot.Bytes()) - interpreter.evm.TxContext.Accesses.TouchAddress(index, uint256.NewInt(cs).Bytes()) + interpreter.evm.TxContext.Accesses.SetLeafValue(indx), uint256.NewInt(cs).Bytes()) } slot.SetUint64(cs) return nil, nil @@ -400,7 +400,7 @@ func touchEachChunks(start, end uint64, code []byte, contract *Contract, evm *EV end = uint64(len(code)) } copy(value[1:], code[chunk*31:end]) - evm.Accesses.TouchAddress(index, value[:]) + evm.Accesses.SetLeafValues(index, value[:]) } } @@ -582,7 +582,7 @@ func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by // Get the initial value as it might not be present index := trieUtils.GetTreeKeyStorageSlot(scope.Contract.Address().Bytes(), loc) - interpreter.evm.TxContext.Accesses.TouchAddress(index, val.Bytes()) + interpreter.evm.TxContext.Accesses.SetLeafValue(index, val.Bytes()) return nil, nil } @@ -924,7 +924,8 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by } copy(value[1:], scope.Contract.Code[chunk*31:endMin]) index := trieUtils.GetTreeKeyCodeChunk(scope.Contract.Address().Bytes(), uint256.NewInt(chunk)) - interpreter.evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil) + interpreter.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) + interpreter.evm.TxContext.Accesses.SetLeafValue(index, value) } } else { scope.Stack.push(integer.Clear()) @@ -961,7 +962,8 @@ func makePush(size uint64, pushByteSize int) executionFunc { value[0] = byte(count) copy(value[1:], scope.Contract.Code[chunk*31:endMin]) index := trieUtils.GetTreeKeyCodeChunk(scope.Contract.Address().Bytes(), uint256.NewInt(chunk)) - interpreter.evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil) + interpreter.evm.TxContext.Accesses.TouchAddressAndChargeGas(index) + interpreter.evm.TxContext.Accesses.SetLeafValue(index, value) // in the case of PUSH32, the end data might be two chunks away, // so also get the middle chunk. There is a boundary condition @@ -980,7 +982,8 @@ func makePush(size uint64, pushByteSize int) executionFunc { } copy(value[1:], scope.Contract.Code[chunk*31:end]) index := trieUtils.GetTreeKeyCodeChunk(scope.Contract.Address().Bytes(), uint256.NewInt(chunk)) - interpreter.evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil) + interpreter.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) + interpreter.evm.TxContext.Accesses.SetLeafValue(index, value) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 68a1f33794e6..6acb97f9d663 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -227,7 +227,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( value[0] = byte(count) copy(value[1:], contract.Code[chunk*31:end]) } - contract.Gas -= in.evm.TxContext.Accesses.TouchAddressAndChargeGas(index, value[:]) + contract.Gas -= in.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index, value[:]) + in.evm.TxContext.SetLeafValue(index, value[:]) } if inWitness { From 500a8c2b83de7aba5fe014114fc9b4c84aab0899 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Tue, 30 Nov 2021 22:00:36 +0000 Subject: [PATCH 4/5] builds --- core/state_transition.go | 8 +++--- core/types/access_witness.go | 56 +++++++++++++++++------------------- core/vm/evm.go | 27 +++++++---------- core/vm/gas_table.go | 10 +++---- core/vm/instructions.go | 12 ++++---- core/vm/interpreter.go | 4 +-- 6 files changed, 55 insertions(+), 62 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 6939016879a9..8cc9b36b001c 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -308,9 +308,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if msg.To() != nil { toBalance := trieUtils.GetTreeKeyBalance(msg.To().Bytes()) pre := st.state.GetBalance(*msg.To()) - if !msg.Value().Cmp(big.NewInt(0)) != 0 { + if msg.Value().Cmp(big.NewInt(0)) != 0 { gas += st.evm.TxContext.Accesses.TouchAddressOnWriteAndChargeGas(toBalance) - st.evmTxContext.Accesses.SetLeafValue(toBalance, pre.Bytes()) + st.evm.TxContext.Accesses.SetLeafValue(toBalance, pre.Bytes()) } // NOTE: Nonce also needs to be charged, because it is needed for execution @@ -327,9 +327,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { var preFN [8]byte binary.BigEndian.PutUint64(preFN[:], st.state.GetNonce(msg.From())) gas += st.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(fromNonce) - st.evm.TxContext.SetLeafValue(fromNonce, preFN[:]) + st.evm.TxContext.Accesses.SetLeafValue(fromNonce, preFN[:]) gas += st.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(fromBalance) - st.evm.TxContext.SetLeafValue(fromBalance, preFB[:]) + st.evm.TxContext.Accesses.SetLeafValue(fromBalance, preFB[:]) } st.gas -= gas diff --git a/core/types/access_witness.go b/core/types/access_witness.go index 7db1371ecd03..8ef82990a19e 100644 --- a/core/types/access_witness.go +++ b/core/types/access_witness.go @@ -17,6 +17,8 @@ package types import ( + "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" ) @@ -44,7 +46,7 @@ type AccessWitness struct { func NewAccessWitness() *AccessWitness { return &AccessWitness{ - Branches: make(map[VerkleStem]struct{}), + Branches: make(map[VerkleStem]byte), Chunks: make(map[common.Hash]ChunkValue), } } @@ -58,15 +60,18 @@ const ( // may be needed before the value of the leaf-key can be retrieved. Hence, we // break witness access (for the purpose of gas accounting), and filling witness // values into two methods -func (aw *AccessWitness) SetLeafValue(addr, value []byte) { - if chunk, exists := aw.Chunks[addr]; exists { +func (aw *AccessWitness) SetLeafValue(addr []byte, value []byte) { + var stem [31]byte + copy(stem[:], addr[:31]) + + if chunk, exists := aw.Chunks[common.BytesToHash(addr)]; exists { chunk.value = value } else { panic(fmt.Sprintf("address not in access witness: %x", addr)) } } -func (aw *AccessWitness) touchAddressOnWrite(addr, value []byte) (bool, bool, bool) { +func (aw *AccessWitness) touchAddressOnWrite(addr []byte) (bool, bool, bool) { var stem VerkleStem var stemWrite, chunkWrite, chunkFill bool copy(stem[:], addr[:31]) @@ -75,14 +80,16 @@ func (aw *AccessWitness) touchAddressOnWrite(addr, value []byte) (bool, bool, bo // respective maps because this function is called at the end of // processing a read access event - if !(aw.Branches[stem] & AccessWitnessWriteFlag) { + if (aw.Branches[stem] & AccessWitnessWriteFlag) == 0 { stemWrite = true aw.Branches[stem] |= AccessWitnessWriteFlag } - if aw.Chunks[common.BytesToHash(addr)] & AccessWitnessWriteFlag { + chunkValue := aw.Chunks[common.BytesToHash(addr)] + if chunkValue.mode & AccessWitnessWriteFlag != 0 { chunkWrite = true - aw.Chunks[common.BytesToHash(addr)].mode |= AccessWitnessWriteFlag + chunkValue.mode |= AccessWitnessWriteFlag + aw.Chunks[common.BytesToHash(addr)] = chunkValue } // TODO charge chunk filling costs if the leaf was previously empty in the state @@ -125,19 +132,19 @@ func (aw *AccessWitness) touchAddress(addr []byte, isWrite bool) (bool, bool, bo } } - var stemWrite, chunkWrite, chunkFill bool + var stemWrite, selectorWrite, chunkFill bool if isWrite { - stemWrite, selectorWrite, chunkFill := aw.touchAddressOnWrite(addr, value) + stemWrite, selectorWrite, chunkFill = aw.touchAddressOnWrite(addr) } return stemRead, selectorRead, stemWrite, selectorWrite, chunkFill } -func (aw *AccessWitness) touchAddressAndChargeGas(addr, value []byte, isWrite bool) uint64 { +func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, isWrite bool) uint64 { var gas uint64 - stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := aw.TouchAddress(addr, value, isWrite) + stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := aw.touchAddress(addr, isWrite) if stemRead { gas += params.WitnessBranchReadCost } @@ -158,23 +165,17 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr, value []byte, isWrite bo } func (aw *AccessWitness) TouchAddressOnWriteAndChargeGas(addr []byte) uint64 { - touchAddressAndChargeGas(addr, true) + return aw.touchAddressAndChargeGas(addr, true) } func (aw *AccessWitness) TouchAddressOnReadAndChargeGas(addr []byte) uint64 { - touchAddressAndChargeGas(addr, false) + return aw.touchAddressAndChargeGas(addr, false) } // Merge is used to merge the witness that got generated during the execution // of a tx, with the accumulation of witnesses that were generated during the // execution of all the txs preceding this one in a given block. func (aw *AccessWitness) Merge(other *AccessWitness) { - for k := range other.Undefined { - if _, ok := aw.Undefined[k]; !ok { - aw.Undefined[k] = struct{}{} - } - } - for k := range other.Branches { if _, ok := aw.Branches[k]; !ok { aw.Branches[k] = other.Branches[k] @@ -186,12 +187,6 @@ func (aw *AccessWitness) Merge(other *AccessWitness) { aw.Chunks[k] = chunk } } - - for k, leafAccessFlags := range other.LeafAccesses { - if _, ok := aw.LeafAccesses[k]; !ok { - aw.LeafAccesses[k] = leafAccessFlags - } - } } // Key returns, predictably, the list of keys that were touched during the @@ -207,14 +202,17 @@ func (aw *AccessWitness) Keys() [][]byte { } func (aw *AccessWitness) KeyVals() map[common.Hash][]byte { - return aw.Chunks + result := make(map[common.Hash][]byte) + for k, v := range aw.Chunks { + result[k] = v.value + } + return result } func (aw *AccessWitness) Copy() *AccessWitness { naw := &AccessWitness{ - Branches: make(map[[31]byte]struct{}), - Chunks: make(map[common.Hash][]byte), - Undefined: make(map[common.Hash]struct{}), + Branches: make(map[VerkleStem]byte), + Chunks: make(map[common.Hash]ChunkValue), } naw.Merge(aw) diff --git a/core/vm/evm.go b/core/vm/evm.go index 7bf6e883d68c..7fcf0bee0b34 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -174,7 +174,7 @@ func (evm *EVM) tryTouchWitnessAndConsumeGas(key, value []byte, gasLeft *uint64) gasConsumed := evm.Accesses.TouchAddressOnReadAndChargeGas(key) if gasConsumed > *gasLeft { - *gas = 0 + *gasLeft = 0 return false } @@ -248,32 +248,27 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } else { // Touch the account data var data [32]byte - if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyVersion(addr.Bytes()), data[:], gas) - { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyVersion(addr.Bytes()), data[:], &gas) { evm.StateDB.RevertToSnapshot(snapshot) - return []byte{}, ErrOutOfGas, gas + return []byte{}, gas, ErrOutOfGas } binary.BigEndian.PutUint64(data[:], evm.StateDB.GetNonce(addr)) - if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyNonce(addr.Bytes()), data[:], gas) - { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyNonce(addr.Bytes()), data[:], &gas) { evm.StateDB.RevertToSnapshot(snapshot) - return []byte{}, ErrOutOfGas, gas + return []byte{}, gas, ErrOutOfGas } - if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyBalance(addr.Bytes()), data[:], gas) - { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyBalance(addr.Bytes()), data[:], &gas) { evm.StateDB.RevertToSnapshot(snapshot) - return []byte{}, ErrOutOfGas, gas + return []byte{}, gas, ErrOutOfGas } binary.BigEndian.PutUint64(data[:], uint64(len(code))) - if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyCodeSize(addr.Bytes()), data[:], gas) - { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyCodeSize(addr.Bytes()), data[:], &gas) { evm.StateDB.RevertToSnapshot(snapshot) - return []byte{}, ErrOutOfGas, gas + return []byte{}, gas, ErrOutOfGas } - if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyCodeKeccak(addr.Bytes()), data[:], gas) - { + if !evm.tryTouchWitnessAndConsumeGas(utils.GetTreeKeyCodeKeccak(addr.Bytes()), data[:], &gas) { evm.StateDB.RevertToSnapshot(snapshot) - return []byte{}, ErrOutOfGas, gas + return []byte{}, gas, ErrOutOfGas } addrCopy := addr diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 306b2bf621b1..018d6950fdea 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -93,7 +93,7 @@ func gasExtCodeSize(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem slot := stack.Back(0) if evm.accesses != nil { index := trieUtils.GetTreeKeyCodeSize(slot.Bytes()) - usedGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) + usedGas += evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) } return usedGas, nil @@ -129,7 +129,7 @@ func gasCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory // TODO make a version of GetTreeKeyCodeChunk without the bigint index := trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(chunk)) - statelessGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) + statelessGas += evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) } } @@ -160,7 +160,7 @@ func gasExtCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem for ; chunk < endChunk; chunk++ { // TODO(@gballet) make a version of GetTreeKeyCodeChunk without the bigint index := trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(chunk)) - statelessGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) + statelessGas += evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) } } @@ -175,7 +175,7 @@ func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySiz where := stack.Back(0) addr := contract.Address() index := trieUtils.GetTreeKeyStorageSlot(addr[:], where) - usedGas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) + usedGas += evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) } return usedGas, nil @@ -426,7 +426,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize // Charge witness costs for i := trieUtils.VersionLeafKey; i <= trieUtils.CodeSizeLeafKey; i++ { index := trieUtils.GetTreeKeyAccountLeaf(address[:], byte(i)) - gas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index) + gas += evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 77a9a024d6e1..3d3e4cb517bb 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -345,7 +345,7 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) cs := uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())) if interpreter.evm.accesses != nil { index := trieUtils.GetTreeKeyCodeSize(slot.Bytes()) - interpreter.evm.TxContext.Accesses.SetLeafValue(indx), uint256.NewInt(cs).Bytes()) + interpreter.evm.TxContext.Accesses.SetLeafValue(index, uint256.NewInt(cs).Bytes()) } slot.SetUint64(cs) return nil, nil @@ -400,7 +400,7 @@ func touchEachChunks(start, end uint64, code []byte, contract *Contract, evm *EV end = uint64(len(code)) } copy(value[1:], code[chunk*31:end]) - evm.Accesses.SetLeafValues(index, value[:]) + evm.Accesses.SetLeafValue(index, value[:]) } } @@ -925,7 +925,7 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by copy(value[1:], scope.Contract.Code[chunk*31:endMin]) index := trieUtils.GetTreeKeyCodeChunk(scope.Contract.Address().Bytes(), uint256.NewInt(chunk)) interpreter.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) - interpreter.evm.TxContext.Accesses.SetLeafValue(index, value) + interpreter.evm.TxContext.Accesses.SetLeafValue(index, value[:]) } } else { scope.Stack.push(integer.Clear()) @@ -962,8 +962,8 @@ func makePush(size uint64, pushByteSize int) executionFunc { value[0] = byte(count) copy(value[1:], scope.Contract.Code[chunk*31:endMin]) index := trieUtils.GetTreeKeyCodeChunk(scope.Contract.Address().Bytes(), uint256.NewInt(chunk)) - interpreter.evm.TxContext.Accesses.TouchAddressAndChargeGas(index) - interpreter.evm.TxContext.Accesses.SetLeafValue(index, value) + interpreter.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) + interpreter.evm.TxContext.Accesses.SetLeafValue(index, value[:]) // in the case of PUSH32, the end data might be two chunks away, // so also get the middle chunk. There is a boundary condition @@ -983,7 +983,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { copy(value[1:], scope.Contract.Code[chunk*31:end]) index := trieUtils.GetTreeKeyCodeChunk(scope.Contract.Address().Bytes(), uint256.NewInt(chunk)) interpreter.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) - interpreter.evm.TxContext.Accesses.SetLeafValue(index, value) + interpreter.evm.TxContext.Accesses.SetLeafValue(index, value[:]) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 6acb97f9d663..f2fe21291d67 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -227,8 +227,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( value[0] = byte(count) copy(value[1:], contract.Code[chunk*31:end]) } - contract.Gas -= in.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index, value[:]) - in.evm.TxContext.SetLeafValue(index, value[:]) + contract.Gas -= in.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(index) + in.evm.TxContext.Accesses.SetLeafValue(index, value[:]) } if inWitness { From acff77df2783f315ea0fe882936e8041078cf632 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Tue, 30 Nov 2021 22:18:01 +0000 Subject: [PATCH 5/5] make sure intrinsic gas check occurs after witness costs are factored in. Adjust TestProcessStateless to account for increased intrinsic gas cost --- core/state_processor_test.go | 6 +++--- core/state_transition.go | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 93a41f2ecba9..8cebf925422d 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -378,11 +378,11 @@ func TestProcessStateless(t *testing.T) { blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) defer blockchain.Stop() chain, _ := GenerateVerkleChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(_ int, gen *BlockGen) { - tx, _ := types.SignTx(types.NewTransaction(0, common.Address{1, 2, 3}, big.NewInt(999), params.TxGas, big.NewInt(875000000), nil), signer, testKey) + tx, _ := types.SignTx(types.NewTransaction(0, common.Address{1, 2, 3}, big.NewInt(999), 28600, big.NewInt(875000000), nil), signer, testKey) gen.AddTx(tx) - tx, _ = types.SignTx(types.NewTransaction(1, common.Address{}, big.NewInt(999), params.TxGas, big.NewInt(875000000), nil), signer, testKey) + tx, _ = types.SignTx(types.NewTransaction(1, common.Address{}, big.NewInt(999), 28600, big.NewInt(875000000), nil), signer, testKey) gen.AddTx(tx) - tx, _ = types.SignTx(types.NewTransaction(2, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil), signer, testKey) + tx, _ = types.SignTx(types.NewTransaction(2, common.Address{}, big.NewInt(0), 28600, big.NewInt(875000000), nil), signer, testKey) gen.AddTx(tx) }) diff --git a/core/state_transition.go b/core/state_transition.go index 8cc9b36b001c..7bc444566bec 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -301,9 +301,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if err != nil { return nil, err } - if st.gas < gas { - return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas) - } + if st.evm.TxContext.Accesses != nil { if msg.To() != nil { toBalance := trieUtils.GetTreeKeyBalance(msg.To().Bytes()) @@ -331,6 +329,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { gas += st.evm.TxContext.Accesses.TouchAddressOnReadAndChargeGas(fromBalance) st.evm.TxContext.Accesses.SetLeafValue(fromBalance, preFB[:]) } + if st.gas < gas { + return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas) + } st.gas -= gas // Check clause 6