odds and ends (#2481)

small cleanups to reduce memory allocations
This commit is contained in:
Jacek Sieka 2024-07-13 20:42:49 +02:00 committed by GitHub
parent f08178c592
commit 72947b3647
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 177 additions and 164 deletions

View File

@ -157,9 +157,9 @@ proc processBeaconBlockRoot*(vmState: BaseVMState, beaconRoot: Hash256):
)
# runComputation a.k.a syscall/evm.call
let res = call.runComputation()
if res.isError:
return err("processBeaconBlockRoot: " & res.error)
let res = call.runComputation(string)
if res.len > 0:
return err("processBeaconBlockRoot: " & res)
statedb.persist(clearEmptyAccount = true)
ok()

View File

@ -46,9 +46,9 @@ type
StoData ## Slot storage data
PayloadRef* = ref object of RootRef
## The payload type depends on the sub-tree used. The `VertesID(1)` rooted
## sub-tree only has `AccountData` type payload, while all other sub-trees
## have `RawData` payload.
## The payload type depends on the sub-tree used. The `VertexID(1)` rooted
## sub-tree only has `AccountData` type payload, stoID-based have StoData
## while generic have RawData
case pType*: PayloadType
of RawData:
rawBlob*: Blob ## Opaque data, default value

View File

@ -31,15 +31,15 @@ import
# ------------------------------------------------------------------------------
when false:
func toError(e: AristoError; s: string; error = Unspecified): CoreDbErrorRef =
CoreDbErrorRef(
func toError(e: AristoError; s: string; error = Unspecified): CoreDbError =
CoreDbError(
error: error,
ctx: s,
isAristo: true,
aErr: e)
func toError(e: KvtError; s: string; error = Unspecified): CoreDbErrorRef =
CoreDbErrorRef(
func toError(e: KvtError; s: string; error = Unspecified): CoreDbError =
CoreDbError(
error: error,
ctx: s,
isAristo: false,

View File

@ -25,7 +25,7 @@ export
#CoreDbCaptRef,
CoreDbCtxRef,
CoreDbErrorCode,
CoreDbErrorRef,
CoreDbError,
CoreDbKvtRef,
CoreDbMptRef,
CoreDbPersistentTypes,
@ -120,11 +120,11 @@ proc finish*(db: CoreDbRef; eradicate = false) =
CoreDbAccRef(db.ctx).call(finish, db.ctx.mpt, eradicate)
db.ifTrackNewApi: debug logTxt, api, elapsed
proc `$$`*(e: CoreDbErrorRef): string =
proc `$$`*(e: CoreDbError): string =
## Pretty print error symbol, note that this directive may have side effects
## as it calls a backend function.
##
if e.isNil: "" else: e.toStr()
e.toStr()
proc persistent*(
db: CoreDbRef;

View File

@ -89,7 +89,7 @@ type
TxRollbackFn = "rollback"
TxSaveDisposeFn = "safeDispose"
func toStr*(e: CoreDbErrorRef): string {.gcsafe.}
func toStr*(e: CoreDbError): string {.gcsafe.}
# ------------------------------------------------------------------------------
# Private helpers
@ -148,7 +148,7 @@ func toStr(rc: CoreDbRc[CoreDbAccRef]): string = rc.toStr "acc"
# Public API logging helpers
# ------------------------------------------------------------------------------
func toStr*(e: CoreDbErrorRef): string =
func toStr*(e: CoreDbError): string =
result = $e.error & "("
result &= (if e.isAristo: "Aristo" else: "Kvt")
result &= ", ctx=" & $e.ctx & ", error="

View File

@ -36,7 +36,7 @@ type
CoreDbProfData* = AristoDbProfData
## Borrowed from `aristo_profile`, only used in profiling mode
CoreDbRc*[T] = Result[T,CoreDbErrorRef]
CoreDbRc*[T] = Result[T,CoreDbError]
CoreDbAccount* = AristoAccount
## Generic account record representation. The data fields
@ -93,14 +93,14 @@ type
CoreDbMptRef* = distinct CoreDbCtxRef
## Generic MPT
CoreDbTxRef* = ref object
## Transaction descriptor
ctx*: CoreDbCtxRef ## Context (also contains `Aristo` descriptor)
aTx*: AristoTxRef ## `Aristo` transaction (if any)
kTx*: KvtTxRef ## `KVT` transaction (if any)
CoreDbErrorRef* = ref object
CoreDbError* = object
## Generic error object
error*: CoreDbErrorCode
ctx*: string ## Context where the exception or error occured

View File

@ -69,8 +69,8 @@ template call*(kvt: CoreDbKvtRef; fn: untyped; args: varArgs[untyped]): untyped
# ---------------
func toError*(e: KvtError; s: string; error = Unspecified): CoreDbErrorRef =
CoreDbErrorRef(
func toError*(e: KvtError; s: string; error = Unspecified): CoreDbError =
CoreDbError(
error: error,
ctx: s,
isAristo: false,
@ -106,8 +106,8 @@ template call*(
# ---------------
func toError*(e: AristoError; s: string; error = Unspecified): CoreDbErrorRef =
CoreDbErrorRef(
func toError*(e: AristoError; s: string; error = Unspecified): CoreDbError =
CoreDbError(
error: error,
ctx: s,
isAristo: true,

View File

@ -623,12 +623,11 @@ proc selfDestructLen*(ac: AccountsLedgerRef): int =
proc addLogEntry*(ac: AccountsLedgerRef, log: Log) =
ac.savePoint.logEntries.add log
proc logEntries*(ac: AccountsLedgerRef): seq[Log] =
proc logEntries*(ac: AccountsLedgerRef): lent seq[Log] =
ac.savePoint.logEntries
proc getAndClearLogEntries*(ac: AccountsLedgerRef): seq[Log] =
result = ac.savePoint.logEntries
ac.savePoint.logEntries.setLen(0)
swap(result, ac.savePoint.logEntries)
proc ripemdSpecial*(ac: AccountsLedgerRef) =
ac.ripemdSpecial = true

View File

@ -140,7 +140,7 @@ template getBlobBaseFee*(c: Computation): UInt256 =
else:
c.vmState.txCtx.blobBaseFee
proc getBlockHash*(c: Computation, number: BlockNumber): Hash256 {.gcsafe, raises:[].} =
proc getBlockHash*(c: Computation, number: BlockNumber): Hash256 =
when evmc_enabled:
let
blockNumber = BlockNumber c.host.getTxContext().block_number
@ -229,8 +229,8 @@ proc newComputation*(vmState: BaseVMState, sysCall: bool, message: Message,
new result
result.vmState = vmState
result.msg = message
result.memory = EvmMemoryRef.new()
result.stack = EvmStackRef.new()
result.memory = EvmMemory.init()
result.stack = EvmStack.init()
result.returnStack = @[]
result.gasMeter.init(message.gas)
result.sysCall = sysCall
@ -248,8 +248,8 @@ func newComputation*(vmState: BaseVMState, sysCall: bool,
new result
result.vmState = vmState
result.msg = message
result.memory = EvmMemoryRef.new()
result.stack = EvmStackRef.new()
result.memory = EvmMemory.init()
result.stack = EvmStack.init()
result.returnStack = @[]
result.gasMeter.init(message.gas)
result.code = CodeStream.init(code)
@ -418,7 +418,7 @@ proc refundSelfDestruct*(c: Computation) =
func tracingEnabled*(c: Computation): bool =
c.vmState.tracingEnabled
func traceOpCodeStarted*(c: Computation, op: Op): int {.raises: [].} =
func traceOpCodeStarted*(c: Computation, op: Op): int =
c.vmState.captureOpStart(
c,
c.code.pc - 1,
@ -426,7 +426,7 @@ func traceOpCodeStarted*(c: Computation, op: Op): int {.raises: [].} =
c.gasMeter.gasRemaining,
c.msg.depth + 1)
func traceOpCodeEnded*(c: Computation, op: Op, opIndex: int) {.raises: [].} =
func traceOpCodeEnded*(c: Computation, op: Op, opIndex: int) =
c.vmState.captureOpEnd(
c,
c.code.pc - 1,
@ -437,7 +437,7 @@ func traceOpCodeEnded*(c: Computation, op: Op, opIndex: int) {.raises: [].} =
c.msg.depth + 1,
opIndex)
func traceError*(c: Computation) {.raises: [].} =
func traceError*(c: Computation) =
c.vmState.captureFault(
c,
c.code.pc - 1,
@ -451,15 +451,15 @@ func traceError*(c: Computation) {.raises: [].} =
func prepareTracer*(c: Computation) =
c.vmState.capturePrepare(c, c.msg.depth)
func opcodeGastCost*(
c: Computation, op: Op, gasCost: GasInt, reason: string): EvmResultVoid
{.raises: [].} =
c.vmState.captureGasCost(
c,
op,
gasCost,
c.gasMeter.gasRemaining,
c.msg.depth + 1)
func opcodeGasCost*(
c: Computation, op: Op, gasCost: GasInt, reason: static string): EvmResultVoid =
if c.vmState.tracingEnabled:
c.vmState.captureGasCost(
c,
op,
gasCost,
c.gasMeter.gasRemaining,
c.msg.depth + 1)
c.gasMeter.consumeGas(gasCost, reason)
# ------------------------------------------------------------------------------

View File

@ -19,9 +19,12 @@ func init*(m: var GasMeter, startGas: GasInt) =
m.gasRemaining = startGas
m.gasRefunded = 0
func consumeGas*(gasMeter: var GasMeter; amount: GasInt; reason: string): EvmResultVoid =
func consumeGas*(
gasMeter: var GasMeter; amount: GasInt; reason: static string): EvmResultVoid =
# TODO report reason - consumeGas is a hotspot in EVM execution so it has to
# be done carefully
if amount > gasMeter.gasRemaining:
return err(memErr(OutOfGas))
return err(gasErr(OutOfGas))
gasMeter.gasRemaining -= amount
ok()

View File

@ -48,7 +48,7 @@ template handleFixedGasCostsDirective(fork: EVMFork; op: Op; k: var VmCtx) =
if k.cpt.tracingEnabled:
k.cpt.opIndex = k.cpt.traceOpCodeStarted(op)
? k.cpt.opcodeGastCost(op, k.cpt.gasCosts[op].cost, reason = $op)
? k.cpt.opcodeGasCost(op, k.cpt.gasCosts[op].cost, reason = $op)
? vmOpHandlers[fork][op].run(k)
# If continuation is not nil, traceOpCodeEnded will be called in executeOpcodes.

View File

@ -139,7 +139,7 @@ proc expOp(k: var VmCtx): EvmResultVoid =
## 0x0A, Exponentiation
let (base, exponent) = ? k.cpt.stack.popInt(2)
? k.cpt.opcodeGastCost(Exp,
? k.cpt.opcodeGasCost(Exp,
k.cpt.gasCosts[Exp].d_handler(exponent),
reason = "EXP: exponent bytes")

View File

@ -225,7 +225,7 @@ proc callOp(k: var VmCtx): EvmResultVoid =
memOffset: p.memOffset,
memLength: p.memLength))
? cpt.opcodeGastCost(Call, gasCost, reason = $Call)
? cpt.opcodeGasCost(Call, gasCost, reason = $Call)
cpt.returnData.setLen(0)
@ -297,7 +297,7 @@ proc callCodeOp(k: var VmCtx): EvmResultVoid =
memOffset: p.memOffset,
memLength: p.memLength))
? cpt.opcodeGastCost(CallCode, gasCost, reason = $CallCode)
? cpt.opcodeGasCost(CallCode, gasCost, reason = $CallCode)
cpt.returnData.setLen(0)
@ -370,7 +370,7 @@ proc delegateCallOp(k: var VmCtx): EvmResultVoid =
memOffset: p.memOffset,
memLength: p.memLength))
? cpt.opcodeGastCost(DelegateCall, gasCost, reason = $DelegateCall)
? cpt.opcodeGasCost(DelegateCall, gasCost, reason = $DelegateCall)
cpt.returnData.setLen(0)
if cpt.msg.depth >= MaxCallDepth:
@ -437,7 +437,7 @@ proc staticCallOp(k: var VmCtx): EvmResultVoid =
memOffset: p.memOffset,
memLength: p.memLength))
? cpt.opcodeGastCost(StaticCall, gasCost, reason = $StaticCall)
? cpt.opcodeGasCost(StaticCall, gasCost, reason = $StaticCall)
cpt.returnData.setLen(0)

View File

@ -110,7 +110,7 @@ proc createOp(k: var VmCtx): EvmResultVoid =
memLength: memLen)
gasCost = cpt.gasCosts[Create].cr_handler(1.u256, gasParams)
? cpt.opcodeGastCost(Create,
? cpt.opcodeGasCost(Create,
gasCost, reason = "CREATE: GasCreate + memLen * memory expansion")
cpt.memory.extend(memPos, memLen)
cpt.returnData.setLen(0)
@ -192,7 +192,7 @@ proc create2Op(k: var VmCtx): EvmResultVoid =
var gasCost = cpt.gasCosts[Create].cr_handler(1.u256, gasParams)
gasCost = gasCost + cpt.gasCosts[Create2].m_handler(0, 0, memLen)
? cpt.opcodeGastCost(Create2,
? cpt.opcodeGasCost(Create2,
gasCost, reason = "CREATE2: GasCreate + memLen * memory expansion")
cpt.memory.extend(memPos, memLen)
cpt.returnData.setLen(0)

View File

@ -55,7 +55,7 @@ proc balanceEIP2929Op (k: var VmCtx): EvmResultVoid =
address = ? cpt.stack.popAddress()
gasCost = cpt.gasEip2929AccountCheck(address)
? cpt.opcodeGastCost(Balance, gasCost, reason = "Balance EIP2929")
? cpt.opcodeGasCost(Balance, gasCost, reason = "Balance EIP2929")
cpt.stack.push cpt.getBalance(address)
# ------------------
@ -106,7 +106,7 @@ proc callDataCopyOp (k: var VmCtx): EvmResultVoid =
let (memPos, copyPos, len) =
(memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
? k.cpt.opcodeGastCost(CallDataCopy,
? k.cpt.opcodeGasCost(CallDataCopy,
k.cpt.gasCosts[CallDataCopy].m_handler(k.cpt.memory.len, memPos, len),
reason = "CallDataCopy fee")
@ -130,7 +130,7 @@ proc codeCopyOp (k: var VmCtx): EvmResultVoid =
let (memPos, copyPos, len) =
(memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
? cpt.opcodeGastCost(CodeCopy,
? cpt.opcodeGasCost(CodeCopy,
cpt.gasCosts[CodeCopy].m_handler(cpt.memory.len, memPos, len),
reason = "CodeCopy fee")
@ -158,7 +158,7 @@ proc extCodeSizeEIP2929Op (k: var VmCtx): EvmResultVoid =
address = ? cpt.stack.popAddress()
gasCost = cpt.gasEip2929AccountCheck(address)
? cpt.opcodeGastCost(ExtCodeSize, gasCost, reason = "ExtCodeSize EIP2929")
? cpt.opcodeGasCost(ExtCodeSize, gasCost, reason = "ExtCodeSize EIP2929")
cpt.stack.push cpt.getCodeSize(address)
# -----------
@ -172,7 +172,7 @@ proc extCodeCopyOp (k: var VmCtx): EvmResultVoid =
(memPos, codePos, len) =
(memStartPos.cleanMemRef, codeStartPos.cleanMemRef, size.cleanMemRef)
? cpt.opcodeGastCost(ExtCodeCopy,
? cpt.opcodeGasCost(ExtCodeCopy,
cpt.gasCosts[ExtCodeCopy].m_handler(cpt.memory.len, memPos, len),
reason = "ExtCodeCopy fee")
@ -192,7 +192,7 @@ proc extCodeCopyEIP2929Op (k: var VmCtx): EvmResultVoid =
gasCost = cpt.gasCosts[ExtCodeCopy].m_handler(cpt.memory.len, memPos, len) +
cpt.gasEip2929AccountCheck(address)
? cpt.opcodeGastCost(ExtCodeCopy, gasCost, reason = "ExtCodeCopy EIP2929")
? cpt.opcodeGasCost(ExtCodeCopy, gasCost, reason = "ExtCodeCopy EIP2929")
let code = cpt.getCode(address)
cpt.memory.writePadded(code.bytes(), memPos, codePos, len)
@ -215,7 +215,7 @@ proc returnDataCopyOp (k: var VmCtx): EvmResultVoid =
gasCost = k.cpt.gasCosts[ReturnDataCopy].m_handler(
k.cpt.memory.len, memPos, len)
? k.cpt.opcodeGastCost(ReturnDataCopy, gasCost, reason = "returnDataCopy fee")
? k.cpt.opcodeGasCost(ReturnDataCopy, gasCost, reason = "returnDataCopy fee")
if copyPos + len > k.cpt.returnData.len:
return err(opErr(OutOfBounds))
@ -239,7 +239,7 @@ proc extCodeHashEIP2929Op (k: var VmCtx): EvmResultVoid =
address = ? k.cpt.stack.popAddress()
gasCost = cpt.gasEip2929AccountCheck(address)
? cpt.opcodeGastCost(ExtCodeHash, gasCost, reason = "ExtCodeHash EIP2929")
? cpt.opcodeGasCost(ExtCodeHash, gasCost, reason = "ExtCodeHash EIP2929")
cpt.stack.push cpt.getCodeHash(address)
# ------------------------------------------------------------------------------

View File

@ -39,7 +39,7 @@ proc sha3Op(k: var VmCtx): EvmResultVoid =
if pos < 0 or len < 0 or pos > 2147483648'i64:
return err(opErr(OutOfBounds))
? k.cpt.opcodeGastCost(Op.Sha3,
? k.cpt.opcodeGasCost(Op.Sha3,
k.cpt.gasCosts[Op.Sha3].m_handler(k.cpt.memory.len, pos, len),
reason = "SHA3: word gas cost")

View File

@ -58,7 +58,7 @@ proc logImpl(c: Computation, opcode: Op, topicCount: static int): EvmResultVoid
if memPos < 0 or len < 0:
return err(opErr(OutOfBounds))
? c.opcodeGastCost(opcode,
? c.opcodeGasCost(opcode,
c.gasCosts[opcode].m_handler(c.memory.len, memPos, len),
reason = "Memory expansion, Log topic and data gas cost")
c.memory.extend(memPos, len)

View File

@ -49,7 +49,7 @@ when evmc_enabled:
res = ForkToSstoreCost[c.fork][status]
gasCost = res.gasCost.GasInt + coldAccess
? c.opcodeGastCost(Sstore, gasCost, "SSTORE")
? c.opcodeGasCost(Sstore, gasCost, "SSTORE")
c.gasMeter.refundGas(res.gasRefund)
ok()
@ -61,7 +61,7 @@ else:
currentValue: currentValue)
res = c.gasCosts[Sstore].ss_handler(newValue, gasParam)
? c.opcodeGastCost(Sstore, res.gasCost, "SSTORE")
? c.opcodeGasCost(Sstore, res.gasCost, "SSTORE")
c.gasMeter.refundGas(res.gasRefund)
c.vmState.mutateStateDB:
@ -80,7 +80,7 @@ else:
res = c.gasCosts[Sstore].ss_handler(newValue, gasParam)
? c.opcodeGastCost(Sstore, res.gasCost + coldAccess, "SSTORE")
? c.opcodeGasCost(Sstore, res.gasCost + coldAccess, "SSTORE")
c.gasMeter.refundGas(res.gasRefund)
@ -132,7 +132,7 @@ proc mloadOp (k: var VmCtx): EvmResultVoid =
let memStartPos = ? k.cpt.stack.popInt()
let memPos = memStartPos.cleanMemRef
? k.cpt.opcodeGastCost(Mload,
? k.cpt.opcodeGasCost(Mload,
k.cpt.gasCosts[Mload].m_handler(k.cpt.memory.len, memPos, 32),
reason = "MLOAD: GasVeryLow + memory expansion")
@ -145,7 +145,7 @@ proc mstoreOp (k: var VmCtx): EvmResultVoid =
let (memStartPos, value) = ? k.cpt.stack.popInt(2)
let memPos = memStartPos.cleanMemRef
? k.cpt.opcodeGastCost(Mstore,
? k.cpt.opcodeGasCost(Mstore,
k.cpt.gasCosts[Mstore].m_handler(k.cpt.memory.len, memPos, 32),
reason = "MSTORE: GasVeryLow + memory expansion")
@ -158,7 +158,7 @@ proc mstore8Op (k: var VmCtx): EvmResultVoid =
let (memStartPos, value) = ? k.cpt.stack.popInt(2)
let memPos = memStartPos.cleanMemRef
? k.cpt.opcodeGastCost(Mstore8,
? k.cpt.opcodeGasCost(Mstore8,
k.cpt.gasCosts[Mstore8].m_handler(k.cpt.memory.len, memPos, 1),
reason = "MSTORE8: GasVeryLow + memory expansion")
@ -181,7 +181,7 @@ proc sloadEIP2929Op (k: var VmCtx): EvmResultVoid =
cpt = k.cpt
slot = ? cpt.stack.popInt()
gasCost = cpt.gasEip2929AccountCheck(cpt.msg.contractAddress, slot)
? cpt.opcodeGastCost(Sload, gasCost, reason = "sloadEIP2929")
? cpt.opcodeGasCost(Sload, gasCost, reason = "sloadEIP2929")
cpt.stack.push cpt.getStorage(slot)
# -------
@ -305,7 +305,7 @@ proc mCopyOp (k: var VmCtx): EvmResultVoid =
let (dstPos, srcPos, len) =
(dst.cleanMemRef, src.cleanMemRef, size.cleanMemRef)
? k.cpt.opcodeGastCost(Mcopy,
? k.cpt.opcodeGasCost(Mcopy,
k.cpt.gasCosts[Mcopy].m_handler(k.cpt.memory.len, max(dstPos, srcPos), len),
reason = "Mcopy fee")

View File

@ -43,7 +43,7 @@ proc returnOp(k: var VmCtx): EvmResultVoid =
let (startPos, size) = ? k.cpt.stack.popInt(2)
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
? k.cpt.opcodeGastCost(Return,
? k.cpt.opcodeGasCost(Return,
k.cpt.gasCosts[Return].m_handler(k.cpt.memory.len, pos, len),
reason = "RETURN")
k.cpt.memory.extend(pos, len)
@ -57,7 +57,7 @@ proc revertOp(k: var VmCtx): EvmResultVoid =
let (startPos, size) = ? k.cpt.stack.popInt(2)
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
? k.cpt.opcodeGastCost(Revert,
? k.cpt.opcodeGasCost(Revert,
k.cpt.gasCosts[Revert].m_handler(k.cpt.memory.len, pos, len),
reason = "REVERT")
@ -92,7 +92,7 @@ proc selfDestructEIP150Op(k: var VmCtx): EvmResultVoid =
condition = not cpt.accountExists(beneficiary)
gasCost = cpt.gasCosts[SelfDestruct].sc_handler(condition)
? cpt.opcodeGastCost(SelfDestruct,
? cpt.opcodeGasCost(SelfDestruct,
gasCost, reason = "SELFDESTRUCT EIP150")
cpt.selfDestruct(beneficiary)
ok()
@ -109,7 +109,7 @@ proc selfDestructEIP161Op(k: var VmCtx): EvmResultVoid =
condition = isDead and not balance.isZero
gasCost = cpt.gasCosts[SelfDestruct].sc_handler(condition)
? cpt.opcodeGastCost(SelfDestruct,
? cpt.opcodeGasCost(SelfDestruct,
gasCost, reason = "SELFDESTRUCT EIP161")
cpt.selfDestruct(beneficiary)
ok()
@ -137,7 +137,7 @@ proc selfDestructEIP2929Op(k: var VmCtx): EvmResultVoid =
db.accessList(beneficiary)
gasCost = gasCost + ColdAccountAccessCost
? cpt.opcodeGastCost(SelfDestruct,
? cpt.opcodeGasCost(SelfDestruct,
gasCost, reason = "SELFDESTRUCT EIP2929")
cpt.selfDestruct(beneficiary)
ok()

View File

@ -16,16 +16,18 @@ import
./interpreter/utils/utils_numeric
type
EvmMemoryRef* = ref object
EvmMemory* = object
bytes*: seq[byte]
func new*(_: type EvmMemoryRef): EvmMemoryRef =
new(result)
func init*(_: type EvmMemory): EvmMemory =
EvmMemory(
bytes: newSeqOfCap[byte](1024)
)
func len*(memory: EvmMemoryRef): int =
func len*(memory: EvmMemory): int =
memory.bytes.len
func extend*(memory: EvmMemoryRef; startPos, size: int) =
func extend*(memory: var EvmMemory; startPos, size: int) =
if size <= 0:
return
let newSize = ceil32(startPos + size)
@ -33,22 +35,22 @@ func extend*(memory: EvmMemoryRef; startPos, size: int) =
return
memory.bytes.setLen(newSize)
func new*(_: type EvmMemoryRef, size: Natural): EvmMemoryRef =
result = EvmMemoryRef.new()
func init*(_: type EvmMemory, size: Natural): EvmMemory =
result = EvmMemory.init()
result.extend(0, size)
template read*(memory: EvmMemoryRef, startPos, size: int): openArray[byte] =
template read*(memory: EvmMemory, startPos, size: int): openArray[byte] =
memory.bytes.toOpenArray(startPos, startPos + size - 1)
template read32Bytes*(memory: EvmMemoryRef, startPos: int): openArray[byte] =
template read32Bytes*(memory: EvmMemory, startPos: int): openArray[byte] =
memory.bytes.toOpenArray(startPos, startPos + 31)
when defined(evmc_enabled):
func readPtr*(memory: EvmMemoryRef, startPos: Natural): ptr byte =
func readPtr*(memory: EvmMemory, startPos: Natural): ptr byte =
if memory.bytes.len == 0 or startPos >= memory.bytes.len: return
result = memory.bytes[startPos].addr
func write*(memory: EvmMemoryRef, startPos: Natural, value: openArray[byte]): EvmResultVoid =
func write*(memory: var EvmMemory, startPos: Natural, value: openArray[byte]): EvmResultVoid =
let size = value.len
if size == 0:
return
@ -58,13 +60,13 @@ func write*(memory: EvmMemoryRef, startPos: Natural, value: openArray[byte]): Ev
assign(memory.bytes.toOpenArray(startPos, int(startPos + size) - 1), value)
ok()
func write*(memory: EvmMemoryRef, startPos: Natural, value: byte): EvmResultVoid =
func write*(memory: var EvmMemory, startPos: Natural, value: byte): EvmResultVoid =
if startPos + 1 > memory.len:
return err(memErr(MemoryFull))
memory.bytes[startPos] = value
ok()
func copy*(memory: EvmMemoryRef, dst, src, len: Natural) =
func copy*(memory: var EvmMemory, dst, src, len: Natural) =
if len <= 0: return
memory.extend(max(dst, src), len)
if dst == src:
@ -73,7 +75,7 @@ func copy*(memory: EvmMemoryRef, dst, src, len: Natural) =
memory.bytes.toOpenArray(dst, dst + len - 1),
memory.bytes.toOpenArray(src, src + len - 1))
func writePadded*(memory: EvmMemoryRef, data: openArray[byte],
func writePadded*(memory: var EvmMemory, data: openArray[byte],
memPos, dataPos, len: Natural) =
memory.extend(memPos, len)

View File

@ -12,19 +12,20 @@
import
std/[macros],
stew/assign2,
eth/common,
./evm_errors,
./interpreter/utils/utils_numeric
type
EvmStackRef* = ref object
EvmStack* = ref object
values: seq[EvmStackElement]
EvmStackElement = UInt256
EvmStackInts = uint64 | uint | int | GasInt
EvmStackBytes32 = array[32, byte]
func len*(stack: EvmStackRef): int {.inline.} =
func len*(stack: EvmStack): int {.inline.} =
len(stack.values)
# ------------------------------------------------------------------------------
@ -51,7 +52,7 @@ template fromStackElem(elem: EvmStackElement, _: type UInt256): UInt256 =
elem
func fromStackElem(elem: EvmStackElement, _: type EthAddress): EthAddress =
result[0 .. ^1] = elem.toBytesBE().toOpenArray(12, 31)
assign(result, elem.toBytesBE().toOpenArray(12, 31))
template fromStackElem(elem: EvmStackElement, _: type Hash256): Hash256 =
Hash256(data: elem.toBytesBE())
@ -59,24 +60,24 @@ template fromStackElem(elem: EvmStackElement, _: type Hash256): Hash256 =
template fromStackElem(elem: EvmStackElement, _: type EvmStackBytes32): EvmStackBytes32 =
elem.toBytesBE()
func pushAux[T](stack: EvmStackRef, value: T): EvmResultVoid =
func pushAux[T](stack: var EvmStack, value: T): EvmResultVoid =
if len(stack.values) > 1023:
return err(stackErr(StackFull))
stack.values.setLen(stack.values.len + 1)
toStackElem(value, stack.values[^1])
ok()
func ensurePop(stack: EvmStackRef, expected: int): EvmResultVoid =
func ensurePop(stack: EvmStack, expected: int): EvmResultVoid =
if stack.values.len < expected:
return err(stackErr(StackInsufficient))
ok()
func popAux(stack: EvmStackRef, T: type): EvmResult[T] =
func popAux(stack: var EvmStack, T: type): EvmResult[T] =
? ensurePop(stack, 1)
result = ok(fromStackElem(stack.values[^1], T))
stack.values.setLen(stack.values.len - 1)
func internalPopTuple(stack: EvmStackRef, T: type, tupleLen: static[int]): EvmResult[T] =
func internalPopTuple(stack: var EvmStack, T: type, tupleLen: static[int]): EvmResult[T] =
? ensurePop(stack, tupleLen)
var
i = 0
@ -96,41 +97,42 @@ macro genTupleType(len: static[int], elemType: untyped): untyped =
# Public functions
# ------------------------------------------------------------------------------
func push*(stack: EvmStackRef,
func push*(stack: var EvmStack,
value: EvmStackInts | UInt256 | EthAddress | Hash256): EvmResultVoid =
pushAux(stack, value)
func push*(stack: EvmStackRef, value: openArray[byte]): EvmResultVoid =
func push*(stack: var EvmStack, value: openArray[byte]): EvmResultVoid =
pushAux(stack, value)
func popInt*(stack: EvmStackRef): EvmResult[UInt256] =
func popInt*(stack: var EvmStack): EvmResult[UInt256] =
popAux(stack, UInt256)
func popSafeInt*(stack: EvmStackRef): EvmResult[int] =
func popSafeInt*(stack: var EvmStack): EvmResult[int] =
? ensurePop(stack, 1)
result = ok(fromStackElem(stack.values[^1], UInt256).safeInt)
stack.values.setLen(stack.values.len - 1)
func popMemRef*(stack: EvmStackRef): EvmResult[int] =
func popMemRef*(stack: var EvmStack): EvmResult[int] =
? ensurePop(stack, 1)
result = ok(fromStackElem(stack.values[^1], UInt256).cleanMemRef)
stack.values.setLen(stack.values.len - 1)
func popInt*(stack: EvmStackRef, numItems: static[int]): auto =
func popInt*(stack: var EvmStack, numItems: static[int]): auto =
type T = genTupleType(numItems, UInt256)
stack.internalPopTuple(T, numItems)
func popAddress*(stack: EvmStackRef): EvmResult[EthAddress] =
func popAddress*(stack: var EvmStack): EvmResult[EthAddress] =
popAux(stack, EthAddress)
func popTopic*(stack: EvmStackRef): EvmResult[EvmStackBytes32] =
func popTopic*(stack: var EvmStack): EvmResult[EvmStackBytes32] =
popAux(stack, EvmStackBytes32)
func new*(_: type EvmStackRef): EvmStackRef =
new(result)
result.values = @[]
func init*(_: type EvmStack): EvmStack =
EvmStack(
values: newSeqOfCap[EvmStackElement](128)
)
func swap*(stack: EvmStackRef, position: int): EvmResultVoid =
func swap*(stack: var EvmStack, position: int): EvmResultVoid =
## Perform a SWAP operation on the stack
let idx = position + 1
if idx < stack.values.len + 1:
@ -139,46 +141,46 @@ func swap*(stack: EvmStackRef, position: int): EvmResultVoid =
else:
err(stackErr(StackInsufficient))
func dup*(stack: EvmStackRef, position: int): EvmResultVoid =
func dup*(stack: var EvmStack, position: int): EvmResultVoid =
## Perform a DUP operation on the stack
if position in 1 .. stack.len:
stack.push(stack.values[^position])
else:
err(stackErr(StackInsufficient))
func peek*(stack: EvmStackRef): EvmResult[UInt256] =
func peek*(stack: EvmStack): EvmResult[UInt256] =
if stack.values.len == 0:
return err(stackErr(StackInsufficient))
ok(fromStackElem(stack.values[^1], UInt256))
func peekSafeInt*(stack: EvmStackRef): EvmResult[int] =
func peekSafeInt*(stack: EvmStack): EvmResult[int] =
if stack.values.len == 0:
return err(stackErr(StackInsufficient))
ok(fromStackElem(stack.values[^1], UInt256).safeInt)
func `[]`*(stack: EvmStackRef, i: BackwardsIndex, T: typedesc): EvmResult[T] =
func `[]`*(stack: EvmStack, i: BackwardsIndex, T: typedesc): EvmResult[T] =
? ensurePop(stack, int(i))
ok(fromStackElem(stack.values[i], T))
func peekInt*(stack: EvmStackRef): EvmResult[UInt256] =
func peekInt*(stack: EvmStack): EvmResult[UInt256] =
? ensurePop(stack, 1)
ok(fromStackElem(stack.values[^1], Uint256))
func peekAddress*(stack: EvmStackRef): EvmResult[EthAddress] =
func peekAddress*(stack: EvmStack): EvmResult[EthAddress] =
? ensurePop(stack, 1)
ok(fromStackElem(stack.values[^1], EthAddress))
func top*(stack: EvmStackRef,
func top*(stack: EvmStack,
value: EvmStackInts | UInt256 | EthAddress | Hash256): EvmResultVoid =
if stack.values.len == 0:
return err(stackErr(StackInsufficient))
toStackElem(value, stack.values[^1])
ok()
iterator items*(stack: EvmStackRef): UInt256 =
iterator items*(stack: EvmStack): UInt256 =
for v in stack.values:
yield v
iterator pairs*(stack: EvmStackRef): (int, UInt256) =
iterator pairs*(stack: EvmStack): (int, UInt256) =
for i, v in stack.values:
yield (i, v)

View File

@ -326,9 +326,8 @@ proc captureGasCost*(vmState: BaseVMState,
comp: Computation,
op: Op, gasCost: GasInt, gasRemaining: GasInt,
depth: int) =
if vmState.tracingEnabled:
let fixed = vmState.gasCosts[op].kind == GckFixed
vmState.tracer.captureGasCost(comp, fixed, op, gasCost, gasRemaining, depth)
let fixed = vmState.gasCosts[op].kind == GckFixed
vmState.tracer.captureGasCost(comp, fixed, op, gasCost, gasRemaining, depth)
proc captureOpEnd*(vmState: BaseVMState, comp: Computation, pc: int,
op: Op, gas: GasInt, refund: int64,

View File

@ -72,8 +72,8 @@ type
# The execution computation
vmState*: BaseVMState
msg*: Message
memory*: EvmMemoryRef
stack*: EvmStackRef
memory*: EvmMemory
stack*: EvmStack
returnStack*: seq[int]
gasMeter*: GasMeter
code*: CodeStream
@ -96,7 +96,7 @@ type
evmcStatus*: evmc_status_code
info* : string
burnsGas* : bool
GasMeter* = object
gasRefunded*: int64
gasRemaining*: GasInt
@ -124,7 +124,7 @@ type
DisableStateDiff
EnableAccount
DisableReturnData
TracerRef* = ref object of RootObj
flags*: set[TracerFlags]

View File

@ -57,9 +57,8 @@ type
gasUsed*: GasInt # Gas used by the call.
contractAddress*: EthAddress # Created account (when `isCreate`).
output*: seq[byte] # Output data.
logEntries*: seq[Log] # Output logs.
stack*: EvmStackRef # EVM stack on return (for test only).
memory*: EvmMemoryRef # EVM memory on return (for test only).
stack*: EvmStack # EVM stack on return (for test only).
memory*: EvmMemory # EVM memory on return (for test only).
func isError*(cr: CallResult): bool =
cr.error.len > 0
@ -276,7 +275,8 @@ proc calculateAndPossiblyRefundGas(host: TransactionHost, call: CallParams): Gas
host.vmState.mutateStateDB:
db.addBalance(call.sender, result.u256 * call.gasPrice.u256)
proc finishRunningComputation(host: TransactionHost, call: CallParams): CallResult =
proc finishRunningComputation(
host: TransactionHost, call: CallParams, T: type): T =
let c = host.computation
let gasRemaining = calculateAndPossiblyRefundGas(host, call)
@ -284,17 +284,26 @@ proc finishRunningComputation(host: TransactionHost, call: CallParams): CallResu
let gasUsed = host.msg.gas.GasInt - gasRemaining
host.vmState.captureEnd(c, c.output, gasUsed, c.errorOpt)
if c.isError:
result.error = c.error.info
result.gasUsed = call.gasLimit - gasRemaining
result.output = system.move(c.output)
result.contractAddress = if call.isCreate: c.msg.contractAddress
else: default(HostAddress)
result.logEntries = host.vmState.stateDB.logEntries()
result.stack = c.stack
result.memory = c.memory
when T is CallResult:
# Collecting the result can be unnecessarily expensive when (re)-processing
# transactions
if c.isError:
result.error = c.error.info
result.gasUsed = call.gasLimit - gasRemaining
result.output = system.move(c.output)
result.contractAddress = if call.isCreate: c.msg.contractAddress
else: default(HostAddress)
result.stack = move(c.stack)
result.memory = move(c.memory)
elif T is GasInt:
result = call.gasLimit - gasRemaining
elif T is string:
if c.isError:
result = c.error.info
else:
{.error: "Unknown computation output".}
proc runComputation*(call: CallParams): CallResult =
proc runComputation*(call: CallParams, T: type): T =
let host = setupHost(call)
prepareToRunComputation(host, call)
@ -306,4 +315,4 @@ proc runComputation*(call: CallParams): CallResult =
else:
execComputation(host.computation)
finishRunningComputation(host, call)
finishRunningComputation(host, call, T)

View File

@ -38,7 +38,7 @@ proc rpcCallEvm*(args: TransactionArgs,
var dbTx = com.db.ctx.newTransaction()
defer: dbTx.dispose() # always dispose state changes
ok(runComputation(params))
ok(runComputation(params, CallResult))
proc rpcCallEvm*(args: TransactionArgs,
header: common.BlockHeader,
@ -50,7 +50,7 @@ proc rpcCallEvm*(args: TransactionArgs,
var dbTx = com.db.ctx.newTransaction()
defer: dbTx.dispose() # always dispose state changes
ok(runComputation(params))
ok(runComputation(params, CallResult))
proc rpcEstimateGas*(args: TransactionArgs,
header: common.BlockHeader,
@ -125,8 +125,8 @@ proc rpcEstimateGas*(args: TransactionArgs,
params.gasLimit = gasLimit
# TODO: bail out on consensus error similar to validateTransaction
let res = runComputation(params)
ok(res.isError)
let res = runComputation(params, string)
ok(res.len > 0)
# Execute the binary search and hone in on an executable gas limit
while lo+1 < hi:
@ -191,11 +191,10 @@ proc txCallEvm*(tx: Transaction,
vmState: BaseVMState): GasInt =
let
call = callParamsForTx(tx, sender, vmState)
res = runComputation(call)
res.gasUsed
runComputation(call, GasInt)
proc testCallEvm*(tx: Transaction,
sender: EthAddress,
vmState: BaseVMState): CallResult =
let call = callParamsForTest(tx, sender, vmState)
runComputation(call)
runComputation(call, CallResult)

View File

@ -25,8 +25,8 @@ import
../nimbus/transaction/call_evm
template testPush(value: untyped, expected: untyped): untyped =
privateAccess(EvmStackRef)
var stack = EvmStackRef.new()
privateAccess(EvmStack)
var stack = EvmStack.init()
check stack.push(value).isOk
check(stack.values == @[expected])
@ -44,14 +44,14 @@ proc runStackTests() =
testPush("ves".toBytes, "ves".toBytes.bigEndianToInt)
test "push does not allow stack to exceed 1024":
var stack = EvmStackRef.new()
var stack = EvmStack.init()
for z in 0 ..< 1024:
check stack.push(z.uint).isOk
check(stack.len == 1024)
check stack.push(1025).error.code == EvmErrorCode.StackFull
test "dup does not allow stack to exceed 1024":
var stack = EvmStackRef.new()
var stack = EvmStack.init()
check stack.push(1.u256).isOk
for z in 0 ..< 1023:
check stack.dup(1).isOk
@ -59,14 +59,14 @@ proc runStackTests() =
check stack.dup(1).error.code == EvmErrorCode.StackFull
test "pop returns latest stack item":
var stack = EvmStackRef.new()
var stack = EvmStack.init()
for element in @[1'u, 2'u, 3'u]:
check stack.push(element).isOk
check(stack.popInt.get == 3.u256)
test "swap correct":
privateAccess(EvmStackRef)
var stack = EvmStackRef.new()
privateAccess(EvmStack)
var stack = EvmStack.init()
for z in 0 ..< 5:
check stack.push(z.uint).isOk
check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256])
@ -76,8 +76,8 @@ proc runStackTests() =
check(stack.values == @[0.u256, 4.u256, 2.u256, 1.u256, 3.u256])
test "dup correct":
privateAccess(EvmStackRef)
var stack = EvmStackRef.new()
privateAccess(EvmStack)
var stack = EvmStack.init()
for z in 0 ..< 5:
check stack.push(z.uint).isOk
check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256])
@ -87,30 +87,30 @@ proc runStackTests() =
check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256, 4.u256, 1.u256])
test "pop raises InsufficientStack appropriately":
var stack = EvmStackRef.new()
var stack = EvmStack.init()
check stack.popInt().error.code == EvmErrorCode.StackInsufficient
test "swap raises InsufficientStack appropriately":
var stack = EvmStackRef.new()
var stack = EvmStack.init()
check stack.swap(0).error.code == EvmErrorCode.StackInsufficient
test "dup raises InsufficientStack appropriately":
var stack = EvmStackRef.new()
var stack = EvmStack.init()
check stack.dup(0).error.code == EvmErrorCode.StackInsufficient
test "binary operations raises InsufficientStack appropriately":
# https://github.com/status-im/nimbus/issues/31
# ./tests/fixtures/VMTests/vmArithmeticTest/mulUnderFlow.json
var stack = EvmStackRef.new()
var stack = EvmStack.init()
check stack.push(123).isOk
check stack.popInt(2).error.code == EvmErrorCode.StackInsufficient
proc memory32: EvmMemoryRef =
result = EvmMemoryRef.new(32)
proc memory32: EvmMemory =
result = EvmMemory.init(32)
proc memory128: EvmMemoryRef =
result = EvmMemoryRef.new(123)
proc memory128: EvmMemory =
result = EvmMemory.init(123)
proc runMemoryTests() =
suite "Memory tests":
@ -126,7 +126,7 @@ proc runMemoryTests() =
check mem.write(startPos = 128, value = 1.byte).error.code == EvmErrorCode.MemoryFull
test "extends appropriately extends memory":
var mem = EvmMemoryRef.new()
var mem = EvmMemory.init()
# Test extends to 32 byte array: 0 < (start_position + size) <= 32
mem.extend(startPos = 0, size = 10)
check(mem.bytes == repeat(0.byte, 32))