Skip to content

Commit 4ec344b

Browse files
Update callcode test (#3214)
* Update callcode test with clearer results * lint * Address feedback * evm: squash call(code) test --------- Co-authored-by: Jochem Brouwer <[email protected]>
1 parent 9046347 commit 4ec344b

File tree

1 file changed

+58
-28
lines changed

1 file changed

+58
-28
lines changed

packages/evm/test/runCall.spec.ts

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common'
22
import {
33
Account,
44
Address,
5-
BIGINT_100,
65
MAX_UINT64,
76
bytesToBigInt,
87
bytesToHex,
98
concatBytes,
109
hexToBytes,
1110
padToEven,
1211
unpadBytes,
12+
zeros,
1313
} from '@ethereumjs/util'
1414
import { keccak256 } from 'ethereum-cryptography/keccak.js'
1515
import { assert, describe, it } from 'vitest'
@@ -724,38 +724,68 @@ describe('RunCall tests', () => {
724724
const evm = new EVM({
725725
common,
726726
})
727-
for (const opcode of ['f1', 'f2']) {
728-
// Code to either CALL or CALLCODE into the own address, with value 1
729-
// JUMPDEST added at the start to track how much CALL(CODE)s are made
730-
const contractCode = hexToBytes(`0x5B6000808080600130611a90${opcode}`)
731-
const contractAddress = Address.fromString('0x000000000000000000000000636F6E7472616374')
732-
await evm.stateManager.putContractCode(contractAddress, contractCode)
733727

734-
const account = await evm.stateManager.getAccount(contractAddress)
735-
account!.balance = BIGINT_100
736-
737-
await evm.stateManager.putAccount(contractAddress, account)
728+
for (const [opcode, gas, expectedOutput] of [
729+
['f1', 36600, '0x'], // 36600 is CALL fee
730+
['f2', 11600, '0x'], // 11600 is CALLCODE fee
731+
['f1', 36600 + 7 * 3, '0x01'], // 36600 is CALL fee + 7 * 3 gas for 7 PUSH opcodes
732+
['f2', 11600 + 7 * 3, '0x01'], // 11600 is CALLCODE fee + 7 * 3 gas for 7 PUSH opcodes
733+
]) {
734+
// Code to either CALL or CALLCODE into AACC empty contract, with value 1
735+
// If enough gas is provided, then since nonzero value is sent, the gas limit
736+
// in the call(coded) contract will get the "bonus gas" stipend of 2300
737+
// Previously, we added this gas stipend to the current gas available (which is wrong)
738+
739+
/***
740+
* Bytecode for AAAA contract (used to check CALL/CALLCODE execution when gas is less than required)
741+
* PUSH1 0x00
742+
* PUSH1 0x00
743+
* PUSH1 0x00
744+
* PUSH1 0x00
745+
* PUSH1 0x01
746+
* PUSH2 0xAACC
747+
* PUSH2 0x1a90 // Note: this is the gas available in the new call(code) frame, this value does not matter
748+
* CALLCODE/CALL
749+
*/
750+
const callCodeAddress = Address.fromString('0x000000000000000000000000000000000000aaaa')
751+
const callCode = hexToBytes(`0x6000600060006000600161AACC611a90${opcode}`)
752+
753+
const gasLimit = gas.toString(16).padStart(4, '0')
754+
755+
/***
756+
* Bytecode for AAAB contract (used to call contract AAAA and stores result of call execution)
757+
* PUSH1 0x00
758+
* DUP1
759+
* DUP1
760+
* DUP1
761+
* DUP1
762+
* PUSH2 0xAAAA
763+
* PUSH2 0x{gas} <- This is the gas limit set for the CALL/CODE execution
764+
* CALL
765+
* PUSH1 0x00
766+
* SSTORE
767+
*/
768+
const callerAddress = Address.fromString('0x000000000000000000000000000000000000aaab')
769+
const callerCode = hexToBytes(`0x60008080808061AAAA61${gasLimit}f1600055`)
770+
771+
await evm.stateManager.putAccount(callCodeAddress, new Account())
772+
await evm.stateManager.putContractCode(callCodeAddress, callCode)
773+
774+
await evm.stateManager.putAccount(callerAddress, new Account(undefined, BigInt(1)))
775+
await evm.stateManager.putContractCode(callerAddress, callerCode)
738776

739777
const runCallArgs = {
740-
gasLimit: BigInt(50000 - 21000),
741-
to: contractAddress,
778+
to: callerAddress,
779+
gasLimit: 0xfffffffn,
742780
}
743-
744-
let jumpdestCalls = 0
745-
746-
evm.events.on('step', (e) => {
747-
if (e.opcode.name === 'JUMPDEST') {
748-
jumpdestCalls++
749-
}
750-
})
751-
752781
await evm.runCall(runCallArgs)
753-
// JUMPDEST should be called twice:
754-
// 1: call into the contract (externally, via tx)
755-
// 2: call(code) into the contract (internally)
756-
// If jumpdest would run >=3 times, it means the (2) has enough gas to pay for CALL(CODE)
757-
// This is not correct
758-
assert.ok(jumpdestCalls === 2, 'called JUMPDEST twice')
782+
783+
const callResult = bytesToHex(
784+
await evm.stateManager.getContractStorage(callerAddress, zeros(32))
785+
)
786+
// Expect slot to have value of either: 0 since CALLCODE and CODE did not have enough gas to execute
787+
// Or 1, if CALL(CODE) has enough gas to enter the new call frame
788+
assert.equal(callResult, expectedOutput, `should have result ${expectedOutput}`)
759789
}
760790
})
761791
})

0 commit comments

Comments
 (0)