mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-12 13:24:21 +00:00
Fix EVM tracer producing wrong order of CALL family
Also fix t8n tool when given json txs with no v,r,s fields. v,r,s field can be subtituted by "secretKey" field.
This commit is contained in:
parent
124ac064c6
commit
849c4bc785
@ -395,6 +395,9 @@ proc traceOpCodeStarted*(c: Computation, op: Op): int {.gcsafe, raises: [].} =
|
||||
c.gasMeter.gasRemaining,
|
||||
c.msg.depth + 1)
|
||||
|
||||
proc traceCallFamilyGas*(c: Computation, op: Op, gas: GasInt) {.gcsafe, raises: [].} =
|
||||
c.vmState.callFamilyGas(op, gas, c.msg.depth + 1)
|
||||
|
||||
proc traceOpCodeEnded*(c: Computation, op: Op, opIndex: int) {.gcsafe, raises: [].} =
|
||||
c.vmState.captureOpEnd(
|
||||
c.code.pc - 1,
|
||||
|
@ -203,8 +203,11 @@ const
|
||||
raise newException(
|
||||
StaticContextError,
|
||||
"Cannot modify state while inside of a STATICCALL context")
|
||||
|
||||
let p = cpt.callParams
|
||||
|
||||
let
|
||||
gasAtStart = cpt.gasMeter.gasRemaining
|
||||
p = cpt.callParams
|
||||
|
||||
cpt.asyncChainTo(ifNecessaryGetAccounts(cpt.vmState, @[p.sender])):
|
||||
cpt.asyncChainTo(ifNecessaryGetCodeForAccounts(cpt.vmState, @[p.contractAddress, p.codeAddress])):
|
||||
var (gasCost, childGasLimit) = cpt.gasCosts[Call].c_handler(
|
||||
@ -238,6 +241,9 @@ const
|
||||
raise newException(
|
||||
OutOfGas, "Gas not enough to perform calculation (call)")
|
||||
|
||||
gasCost = gasAtStart - cpt.gasMeter.gasRemaining
|
||||
cpt.traceCallFamilyGas(Call, gasCost)
|
||||
|
||||
cpt.memory.extend(p.memInPos, p.memInLen)
|
||||
cpt.memory.extend(p.memOutPos, p.memOutLen)
|
||||
|
||||
@ -287,6 +293,7 @@ const
|
||||
## 0xf2, Message-call into this account with an alternative account's code.
|
||||
let
|
||||
cpt = k.cpt
|
||||
gasAtStart = cpt.gasMeter.gasRemaining
|
||||
p = cpt.callCodeParams
|
||||
|
||||
cpt.asyncChainTo(ifNecessaryGetAccounts(cpt.vmState, @[p.sender])):
|
||||
@ -325,6 +332,9 @@ const
|
||||
raise newException(
|
||||
OutOfGas, "Gas not enough to perform calculation (callCode)")
|
||||
|
||||
gasCost = gasAtStart - cpt.gasMeter.gasRemaining
|
||||
cpt.traceCallFamilyGas(CallCode, gasCost)
|
||||
|
||||
cpt.memory.extend(p.memInPos, p.memInLen)
|
||||
cpt.memory.extend(p.memOutPos, p.memOutLen)
|
||||
|
||||
@ -375,6 +385,7 @@ const
|
||||
## code, but persisting the current values for sender and value.
|
||||
let
|
||||
cpt = k.cpt
|
||||
gasAtStart = cpt.gasMeter.gasRemaining
|
||||
p = cpt.delegateCallParams
|
||||
|
||||
cpt.asyncChainTo(ifNecessaryGetAccounts(cpt.vmState, @[p.sender])):
|
||||
@ -409,6 +420,9 @@ const
|
||||
raise newException(
|
||||
OutOfGas, "Gas not enough to perform calculation (delegateCall)")
|
||||
|
||||
gasCost = gasAtStart - cpt.gasMeter.gasRemaining
|
||||
cpt.traceCallFamilyGas(DelegateCall, gasCost)
|
||||
|
||||
cpt.memory.extend(p.memInPos, p.memInLen)
|
||||
cpt.memory.extend(p.memOutPos, p.memOutLen)
|
||||
|
||||
@ -451,6 +465,7 @@ const
|
||||
|
||||
let
|
||||
cpt = k.cpt
|
||||
gasAtStart = cpt.gasMeter.gasRemaining
|
||||
p = cpt.staticCallParams
|
||||
|
||||
cpt.asyncChainTo(ifNecessaryGetAccounts(cpt.vmState, @[p.sender])):
|
||||
@ -490,6 +505,9 @@ const
|
||||
raise newException(
|
||||
OutOfGas, "Gas not enough to perform calculation (staticCall)")
|
||||
|
||||
gasCost = gasAtStart - cpt.gasMeter.gasRemaining
|
||||
cpt.traceCallFamilyGas(StaticCall, gasCost)
|
||||
|
||||
cpt.memory.extend(p.memInPos, p.memInLen)
|
||||
cpt.memory.extend(p.memOutPos, p.memOutLen)
|
||||
|
||||
|
@ -99,11 +99,13 @@ const
|
||||
raise newException(InitcodeError,
|
||||
&"CREATE: have {memLen}, max {EIP3860_MAX_INITCODE_SIZE}")
|
||||
|
||||
let gasParams = GasParams(
|
||||
kind: Create,
|
||||
cr_currentMemSize: k.cpt.memory.len,
|
||||
cr_memOffset: memPos,
|
||||
cr_memLength: memLen)
|
||||
let
|
||||
gasAtStart = k.cpt.gasMeter.gasRemaining
|
||||
gasParams = GasParams(
|
||||
kind: Create,
|
||||
cr_currentMemSize: k.cpt.memory.len,
|
||||
cr_memOffset: memPos,
|
||||
cr_memLength: memLen)
|
||||
|
||||
var gasCost = k.cpt.gasCosts[Create].c_handler(1.u256, gasParams).gasCost
|
||||
k.cpt.gasMeter.consumeGas(
|
||||
@ -132,6 +134,9 @@ const
|
||||
createMsgGas -= createMsgGas div 64
|
||||
k.cpt.gasMeter.consumeGas(createMsgGas, reason = "CREATE")
|
||||
|
||||
gasCost = gasAtStart - k.cpt.gasMeter.gasRemaining
|
||||
k.cpt.traceCallFamilyGas(Create, gasCost)
|
||||
|
||||
when evmc_enabled:
|
||||
let
|
||||
msg = new(nimbus_message)
|
||||
@ -177,11 +182,13 @@ const
|
||||
raise newException(InitcodeError,
|
||||
&"CREATE2: have {memLen}, max {EIP3860_MAX_INITCODE_SIZE}")
|
||||
|
||||
let gasParams = GasParams(
|
||||
kind: Create,
|
||||
cr_currentMemSize: k.cpt.memory.len,
|
||||
cr_memOffset: memPos,
|
||||
cr_memLength: memLen)
|
||||
let
|
||||
gasAtStart = k.cpt.gasMeter.gasRemaining
|
||||
gasParams = GasParams(
|
||||
kind: Create,
|
||||
cr_currentMemSize: k.cpt.memory.len,
|
||||
cr_memOffset: memPos,
|
||||
cr_memLength: memLen)
|
||||
|
||||
var gasCost = k.cpt.gasCosts[Create].c_handler(1.u256, gasParams).gasCost
|
||||
gasCost = gasCost + k.cpt.gasCosts[Create2].m_handler(0, 0, memLen)
|
||||
@ -212,6 +219,9 @@ const
|
||||
createMsgGas -= createMsgGas div 64
|
||||
k.cpt.gasMeter.consumeGas(createMsgGas, reason = "CREATE2")
|
||||
|
||||
gasCost = gasAtStart - k.cpt.gasMeter.gasRemaining
|
||||
k.cpt.traceCallFamilyGas(Create2, gasCost)
|
||||
|
||||
when evmc_enabled:
|
||||
let
|
||||
msg = new(nimbus_message)
|
||||
|
@ -257,8 +257,8 @@ proc executeOpcodes*(c: Computation, shouldPrepareTracer: bool = true)
|
||||
except CatchableError as e:
|
||||
let
|
||||
msg = e.msg
|
||||
depth = $c.msg.depth
|
||||
c.setError("Opcode Dispatch Error msg=" & msg & ", depth=" & depth, true)
|
||||
depth = $(c.msg.depth + 1) # plus one to match tracer depth, and avoid confusion
|
||||
c.setError("Opcode Dispatch Error: " & msg & ", depth=" & depth, true)
|
||||
|
||||
if c.isError() and c.continuation.isNil:
|
||||
if c.tracingEnabled: c.traceError()
|
||||
|
@ -58,7 +58,7 @@ proc ensurePop(elements: Stack, a: int) =
|
||||
let expected = a
|
||||
if num < expected:
|
||||
raise newException(InsufficientStack,
|
||||
&"Stack underflow: expected {expected} elements, got {num} instead.")
|
||||
&"Stack underflow, expect {expected}, got {num}")
|
||||
|
||||
proc popAux[T](stack: var Stack, value: var T) =
|
||||
ensurePop(stack, 1)
|
||||
@ -103,7 +103,7 @@ proc swap*(stack: var Stack, position: int) =
|
||||
(stack.values[^1], stack.values[^idx]) = (stack.values[^idx], stack.values[^1])
|
||||
else:
|
||||
raise newException(InsufficientStack,
|
||||
&"Insufficient stack items for SWAP{position}")
|
||||
"Stack underflow for SWAP" & $position)
|
||||
|
||||
template getInt(x: int): int = x
|
||||
|
||||
@ -114,7 +114,7 @@ proc dup*(stack: var Stack, position: int | UInt256) =
|
||||
stack.push(stack.values[^position])
|
||||
else:
|
||||
raise newException(InsufficientStack,
|
||||
&"Insufficient stack items for DUP{position}")
|
||||
"Stack underflow for DUP" & $position)
|
||||
|
||||
proc peek*(stack: Stack): UInt256 =
|
||||
# This should be used only for testing purposes!
|
||||
|
@ -393,6 +393,12 @@ proc captureOpStart*(vmState: BaseVMState, pc: int,
|
||||
if vmState.tracingEnabled:
|
||||
result = vmState.tracer.captureOpStart(pc, op, gas, depth)
|
||||
|
||||
proc callFamilyGas*(vmState: BaseVMState,
|
||||
op: Op, gas: GasInt,
|
||||
depth: int) =
|
||||
if vmState.tracingEnabled:
|
||||
vmState.tracer.callFamilyGas(op, gas, depth)
|
||||
|
||||
proc captureOpEnd*(vmState: BaseVMState, pc: int,
|
||||
op: Op, gas: GasInt, refund: GasInt,
|
||||
rData: openArray[byte],
|
||||
|
@ -29,6 +29,17 @@ type
|
||||
stack: JsonNode
|
||||
storageKeys: seq[HashSet[UInt256]]
|
||||
index: int
|
||||
callFamilyNode: JsonNode
|
||||
|
||||
const
|
||||
callFamily = [
|
||||
Create,
|
||||
Create2,
|
||||
Call,
|
||||
CallCode,
|
||||
DelegateCall,
|
||||
StaticCall,
|
||||
]
|
||||
|
||||
template stripLeadingZeros(value: string): string =
|
||||
var cidx = 0
|
||||
@ -41,7 +52,10 @@ proc encodeHexInt(x: SomeInteger): JsonNode =
|
||||
%("0x" & x.toHex.stripLeadingZeros.toLowerAscii)
|
||||
|
||||
proc `%`(x: openArray[byte]): JsonNode =
|
||||
%("0x" & x.toHex)
|
||||
if x.len == 0:
|
||||
%("")
|
||||
else:
|
||||
%("0x" & x.toHex)
|
||||
|
||||
proc writeJson(ctx: JsonTracer, res: JsonNode) =
|
||||
try:
|
||||
@ -113,7 +127,10 @@ proc captureOpImpl(ctx: JsonTracer, pc: int,
|
||||
if error.isSome:
|
||||
res["error"] = %(error.get)
|
||||
|
||||
ctx.writeJson(res)
|
||||
if op in callFamily:
|
||||
ctx.callFamilyNode = res
|
||||
else:
|
||||
ctx.writeJson(res)
|
||||
|
||||
proc newJsonTracer*(stream: Stream, flags: set[TracerFlags], pretty: bool): JsonTracer =
|
||||
JsonTracer(
|
||||
@ -171,13 +188,34 @@ method captureOpStart*(ctx: JsonTracer, pc: int,
|
||||
except ValueError as ex:
|
||||
error "JsonTracer captureOpStart", msg=ex.msg
|
||||
|
||||
if op in callFamily:
|
||||
try:
|
||||
ctx.captureOpImpl(pc, op, 0, 0, [], depth, none(string))
|
||||
except RlpError as ex:
|
||||
error "JsonTracer captureOpEnd", msg=ex.msg
|
||||
|
||||
# make sure captureOpEnd get the right opIndex
|
||||
result = ctx.index
|
||||
inc ctx.index
|
||||
|
||||
method callFamilyGas*(ctx: JsonTracer,
|
||||
op: Op, gas: GasInt,
|
||||
depth: int) {.gcsafe.} =
|
||||
doAssert(op in callFamily)
|
||||
doAssert(ctx.callFamilyNode.isNil.not)
|
||||
let res = ctx.callFamilyNode
|
||||
res["gasCost"] = encodeHexInt(gas)
|
||||
ctx.writeJson(res)
|
||||
|
||||
method captureOpEnd*(ctx: JsonTracer, pc: int,
|
||||
op: Op, gas: GasInt, refund: GasInt,
|
||||
rData: openArray[byte],
|
||||
depth: int, opIndex: int) {.gcsafe.} =
|
||||
|
||||
if op in callFamily:
|
||||
# call family opcode is processed in captureOpStart
|
||||
return
|
||||
|
||||
try:
|
||||
ctx.captureOpImpl(pc, op, gas, refund, rData, depth, none(string))
|
||||
except RlpError as ex:
|
||||
|
@ -173,6 +173,11 @@ method captureOpStart*(ctx: TracerRef, pc: int,
|
||||
depth: int): int {.base, gcsafe.} =
|
||||
discard
|
||||
|
||||
method callFamilyGas*(ctx: TracerRef,
|
||||
op: Op, gas: GasInt,
|
||||
depth: int) {.base, gcsafe.} =
|
||||
discard
|
||||
|
||||
method captureOpEnd*(ctx: TracerRef, pc: int,
|
||||
op: Op, gas: GasInt, refund: GasInt,
|
||||
rData: openArray[byte],
|
||||
@ -188,4 +193,3 @@ method captureFault*(ctx: TracerRef, pc: int,
|
||||
|
||||
method capturePrepare*(ctx: TracerRef, depth: int) {.base, gcsafe.} =
|
||||
discard
|
||||
|
||||
|
@ -43,6 +43,7 @@ export
|
||||
vms.captureEnter,
|
||||
vms.captureExit,
|
||||
vms.captureOpStart,
|
||||
vms.callFamilyGas,
|
||||
vms.captureOpEnd,
|
||||
vms.captureFault,
|
||||
vms.capturePrepare
|
||||
|
4
tests/fixtures/TracerTests/block46402.json
vendored
4
tests/fixtures/TracerTests/block46402.json
vendored
@ -283,7 +283,7 @@
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000060"
|
||||
],
|
||||
"error": "Opcode Dispatch Error msg=Out of gas: Needed 20000 - Remaining 412 - Reason: SSTORE, depth=0",
|
||||
"error": "Opcode Dispatch Error: Out of gas: Needed 20000 - Remaining 412 - Reason: SSTORE, depth=1",
|
||||
"gasCost": 0
|
||||
}
|
||||
],
|
||||
@ -705,7 +705,7 @@
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000000000000000000000000000000000000000000000000060"
|
||||
],
|
||||
"error": "Opcode Dispatch Error msg=Out of gas: Needed 20000 - Remaining 412 - Reason: SSTORE, depth=0",
|
||||
"error": "Opcode Dispatch Error: Out of gas: Needed 20000 - Remaining 412 - Reason: SSTORE, depth=1",
|
||||
"gasCost": 0
|
||||
}
|
||||
]
|
||||
|
@ -212,9 +212,6 @@ proc parseTx(n: JsonNode, chainId: ChainID): Transaction =
|
||||
required(tx, GasInt, gas)
|
||||
required(tx, UInt256, value)
|
||||
required(tx, Blob, input)
|
||||
required(tx, int64, v)
|
||||
required(tx, UInt256, r)
|
||||
required(tx, UInt256, s)
|
||||
|
||||
if n.hasKey("to"):
|
||||
tx.to = some(EthAddress.fromJson(n, "to"))
|
||||
@ -248,6 +245,9 @@ proc parseTx(n: JsonNode, chainId: ChainID): Transaction =
|
||||
let secretKey = PrivateKey.fromRaw(data).tryGet
|
||||
signTransaction(tx, secretKey, chainId, eip155)
|
||||
else:
|
||||
required(tx, int64, v)
|
||||
required(tx, UInt256, r)
|
||||
required(tx, UInt256, s)
|
||||
tx
|
||||
|
||||
proc parseTxLegacy(item: var Rlp): Result[Transaction, string] =
|
||||
|
@ -498,7 +498,7 @@ const
|
||||
expOut: "exp.json",
|
||||
),
|
||||
TestSpec(
|
||||
name : "EVM tracer crash bug",
|
||||
name : "EVM tracer nil stack crash bug",
|
||||
base : "testdata/00-519",
|
||||
input : t8nInput(
|
||||
"alloc.json", "txs.json", "env.json", "Shanghai", "0",
|
||||
@ -506,6 +506,15 @@ const
|
||||
output: T8nOutput(trace: true),
|
||||
expOut: "exp.txt",
|
||||
),
|
||||
TestSpec(
|
||||
name : "EVM tracer wrong order for CALL family opcodes",
|
||||
base : "testdata/00-520",
|
||||
input : t8nInput(
|
||||
"alloc.json", "txs.json", "env.json", "Merge", "0",
|
||||
),
|
||||
output: T8nOutput(trace: true, result: true),
|
||||
expOut: "exp.txt",
|
||||
),
|
||||
]
|
||||
|
||||
proc main() =
|
||||
|
2
tools/t8n/testdata/00-519/exp.txt
vendored
2
tools/t8n/testdata/00-519/exp.txt
vendored
@ -1,2 +1,2 @@
|
||||
{"pc":0,"op":0,"gas":"0x0","gasCost":"0xfffffffffffecb68","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP","error":"Blake2b F function invalid input"}
|
||||
{"output":"0x","gasUsed":"0x13498","error":"Blake2b F function invalid input"}
|
||||
{"output":"","gasUsed":"0x13498","error":"Blake2b F function invalid input"}
|
||||
|
237
tools/t8n/testdata/00-520/alloc.json
vendored
Normal file
237
tools/t8n/testdata/00-520/alloc.json
vendored
Normal file
File diff suppressed because one or more lines are too long
9
tools/t8n/testdata/00-520/env.json
vendored
Normal file
9
tools/t8n/testdata/00-520/env.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"currentBaseFee": "0x10",
|
||||
"currentCoinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||
"currentGasLimit": "0x26e1f476fe1e22",
|
||||
"currentNumber": "0x1",
|
||||
"currentTimestamp": "0x3e8",
|
||||
"currentRandom": "0x0000000000000000000000000000000000000000000000000000000000020000",
|
||||
"currentDifficulty": "0x0",
|
||||
}
|
12
tools/t8n/testdata/00-520/exp.txt
vendored
Normal file
12
tools/t8n/testdata/00-520/exp.txt
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{"pc":0,"op":96,"gas":"0x79bf88","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||
{"pc":2,"op":96,"gas":"0x79bf85","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||
{"pc":4,"op":96,"gas":"0x79bf82","gasCost":"0x3","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||
{"pc":6,"op":96,"gas":"0x79bf7f","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||
{"pc":8,"op":96,"gas":"0x79bf7c","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||
{"pc":10,"op":96,"gas":"0x79bf79","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||
{"pc":12,"op":90,"gas":"0x79bf76","gasCost":"0x2","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0xf3"],"depth":1,"refund":0,"opName":"GAS"}
|
||||
{"pc":13,"op":241,"gas":"0x79bf74","gasCost":"0x77d89f","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0xf3","0x79bf74"],"depth":1,"refund":0,"opName":"CALL"}
|
||||
{"pc":0,"op":11,"gas":"0x77ce77","gasCost":"0x5","memSize":0,"stack":["0x0"],"depth":2,"refund":0,"opName":"SIGNEXTEND","error":"Opcode Dispatch Error: Stack underflow, expect 2, got 0, depth=2"}
|
||||
{"pc":14,"op":80,"gas":"0x1e6d5","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"POP"}
|
||||
{"pc":15,"op":152,"gas":"0x1e6d3","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"SWAP9","error":"Opcode Dispatch Error: Stack underflow for SWAP9, depth=1"}
|
||||
{"output":"","gasUsed":"0x79bf88","error":"Opcode Dispatch Error: Stack underflow for SWAP9, depth=1"}
|
14
tools/t8n/testdata/00-520/txs.json
vendored
Normal file
14
tools/t8n/testdata/00-520/txs.json
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"type": "0x0",
|
||||
"chainId": "0x1",
|
||||
"nonce": "0x0",
|
||||
"gasPrice": "0x10",
|
||||
"gas": "0x7a1200",
|
||||
"to": "0x00000000000000000000000000000000000000f1",
|
||||
"value": "0x54afed",
|
||||
"input": "0xf9ddd3baf78a80",
|
||||
"secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||
"sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||
}
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user