Merge branch 'jangko-implement_byzantium'
This commit is contained in:
commit
07d00b8f8d
1696
GeneralStateTests.md
1696
GeneralStateTests.md
File diff suppressed because it is too large
Load Diff
|
@ -6,12 +6,12 @@ PrecompileTests
|
|||
+ bn256mul.json OK
|
||||
+ ecrecover.json OK
|
||||
+ identity.json OK
|
||||
+ modexp.json OK
|
||||
modexp.json Skip
|
||||
+ pairing.json OK
|
||||
+ ripemd160.json OK
|
||||
+ sha256.json OK
|
||||
```
|
||||
OK: 8/8 Fail: 0/8 Skip: 0/8
|
||||
OK: 7/8 Fail: 0/8 Skip: 1/8
|
||||
|
||||
---TOTAL---
|
||||
OK: 8/8 Fail: 0/8 Skip: 0/8
|
||||
OK: 7/8 Fail: 0/8 Skip: 1/8
|
||||
|
|
|
@ -65,6 +65,9 @@ type
|
|||
NotImplementedError* = object of VMError
|
||||
## Not implemented error
|
||||
|
||||
StaticContextError* = object of VMError
|
||||
## State changes not allowed in static call context
|
||||
|
||||
proc makeVMError*: ref VMError =
|
||||
new(result)
|
||||
result.burnsGas = true
|
||||
|
|
|
@ -7,12 +7,15 @@ import options, sets,
|
|||
../vm/interpreter/vm_forks,
|
||||
./dao
|
||||
|
||||
proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMState, forkOverride=none(Fork)): GasInt =
|
||||
proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMState, fork: Fork): GasInt =
|
||||
## Process the transaction, write the results to db.
|
||||
## Returns amount of ETH to be rewarded to miner
|
||||
trace "Sender", sender
|
||||
trace "txHash", rlpHash = tx.rlpHash
|
||||
|
||||
if fork >= FkSpurious:
|
||||
vmState.touchedAccounts.incl(vmState.blockHeader.coinbase)
|
||||
|
||||
var gasUsed = tx.gasLimit
|
||||
|
||||
block:
|
||||
|
@ -31,7 +34,7 @@ proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMSta
|
|||
let recipient = tx.getRecipient()
|
||||
let isCollision = vmState.readOnlyStateDb().hasCodeOrNonce(recipient)
|
||||
|
||||
var computation = setupComputation(vmState, tx, sender, recipient, forkOverride)
|
||||
var computation = setupComputation(vmState, tx, sender, recipient, fork)
|
||||
if computation.isNil: # OOG in setupComputation
|
||||
gasUsed = 0
|
||||
break
|
||||
|
@ -41,7 +44,8 @@ proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMSta
|
|||
db.subBalance(sender, upfrontGasCost)
|
||||
|
||||
if tx.isContractCreation and isCollision: break
|
||||
if execComputation(computation):
|
||||
execComputation(computation)
|
||||
if not computation.shouldBurnGas:
|
||||
gasUsed = computation.refundGas(tx, sender)
|
||||
|
||||
if computation.isSuicided(vmState.blockHeader.coinbase):
|
||||
|
@ -85,17 +89,13 @@ proc makeReceipt(vmState: BaseVMState, fork = FkFrontier): Receipt =
|
|||
if fork < FkByzantium:
|
||||
result.stateRootOrStatus = hashOrStatus(vmState.accountDb.rootHash)
|
||||
else:
|
||||
# TODO: post byzantium fork use status instead of rootHash
|
||||
let vmStatus = true # success or failure
|
||||
result.stateRootOrStatus = hashOrStatus(vmStatus)
|
||||
result.stateRootOrStatus = hashOrStatus(vmState.status)
|
||||
|
||||
result.cumulativeGasUsed = vmState.cumulativeGasUsed
|
||||
result.logs = vmState.getAndClearLogEntries()
|
||||
result.bloom = logsBloom(result.logs).value.toByteArrayBE
|
||||
|
||||
proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, vmState: BaseVMState): ValidationResult =
|
||||
let blockReward = 5.u256 * pow(10.u256, 18) # 5 ETH
|
||||
|
||||
if chainDB.config.daoForkSupport and header.blockNumber == chainDB.config.daoForkBlock:
|
||||
vmState.mutateStateDB:
|
||||
db.applyDAOHardFork()
|
||||
|
@ -104,6 +104,8 @@ proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, v
|
|||
debug "Mismatched txRoot", blockNumber=header.blockNumber
|
||||
return ValidationResult.Error
|
||||
|
||||
let fork = vmState.blockNumber.toFork
|
||||
|
||||
if header.txRoot != BLANK_ROOT_HASH:
|
||||
if body.transactions.len == 0:
|
||||
debug "No transactions in body", blockNumber=header.blockNumber
|
||||
|
@ -116,11 +118,16 @@ proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, v
|
|||
for txIndex, tx in body.transactions:
|
||||
var sender: EthAddress
|
||||
if tx.getSender(sender):
|
||||
let gasUsed = processTransaction(tx, sender, vmState)
|
||||
let gasUsed = processTransaction(tx, sender, vmState, fork)
|
||||
else:
|
||||
debug "Could not get sender", txIndex, tx
|
||||
return ValidationResult.Error
|
||||
vmState.receipts[txIndex] = makeReceipt(vmState)
|
||||
vmState.receipts[txIndex] = makeReceipt(vmState, fork)
|
||||
|
||||
let blockReward = if fork >= FkByzantium:
|
||||
3.u256 * pow(10.u256, 18) # 3 ETH
|
||||
else:
|
||||
5.u256 * pow(10.u256, 18) # 5 ETH
|
||||
|
||||
var mainReward = blockReward
|
||||
if header.ommersHash != EMPTY_UNCLE_HASH:
|
||||
|
|
|
@ -14,7 +14,8 @@ import
|
|||
../transaction, ../config, ../vm_state, ../constants, ../vm_types,
|
||||
../vm_state_transactions, ../utils,
|
||||
../db/[db_chain, state_db, storage_types],
|
||||
rpc_types, rpc_utils, ../vm/[message, computation]
|
||||
rpc_types, rpc_utils, ../vm/[message, computation],
|
||||
../vm/interpreter/vm_forks
|
||||
|
||||
#[
|
||||
Note:
|
||||
|
@ -41,7 +42,8 @@ proc binarySearchGas(vmState: var BaseVMState, transaction: Transaction, sender:
|
|||
vmState,
|
||||
transaction,
|
||||
sender,
|
||||
recipient)
|
||||
recipient,
|
||||
vmState.blockNumber.toFork)
|
||||
|
||||
proc dummyTransaction(gasLimit, gasPrice: GasInt, destination: EthAddress, value: UInt256): Transaction =
|
||||
Transaction(
|
||||
|
@ -323,7 +325,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
|||
value = if call.value.isSome: call.value.get else: 0.u256
|
||||
comp = setupComputation(vmState, header.blockNumber, value, data, sender, destination, gasLimit, gasPrice, call.to.isNone)
|
||||
|
||||
discard comp.execComputation
|
||||
comp.execComputation
|
||||
result = ("0x" & nimcrypto.toHex(comp.output)).HexDataStr
|
||||
|
||||
rpcsrv.rpc("eth_estimateGas") do(call: EthCall, quantityTag: string) -> GasInt:
|
||||
|
|
|
@ -2,7 +2,8 @@ import
|
|||
db/[db_chain, state_db, capturedb], eth/common, utils, json,
|
||||
constants, vm_state, vm_types, transaction, p2p/executor,
|
||||
eth/trie/db, nimcrypto, strutils, ranges,
|
||||
chronicles, rpc/hexstrings, launcher
|
||||
chronicles, rpc/hexstrings, launcher,
|
||||
vm/interpreter/vm_forks
|
||||
|
||||
proc getParentHeader(self: BaseChainDB, header: BlockHeader): BlockHeader =
|
||||
self.getBlockHeader(header.parentHash)
|
||||
|
@ -90,6 +91,8 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
|
|||
stateDiff = %{"before": before, "after": after}
|
||||
beforeRoot: Hash256
|
||||
|
||||
let fork = header.blockNumber.toFork
|
||||
|
||||
for idx, tx in body.transactions:
|
||||
let sender = tx.getSender
|
||||
let recipient = tx.getRecipient
|
||||
|
@ -102,7 +105,7 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
|
|||
stateDiff["beforeRoot"] = %($stateDb.rootHash)
|
||||
beforeRoot = stateDb.rootHash
|
||||
|
||||
gasUsed = processTransaction(tx, sender, vmState)
|
||||
gasUsed = processTransaction(tx, sender, vmState, fork)
|
||||
|
||||
if idx == txIndex:
|
||||
after.captureAccount(stateDb, sender, senderName)
|
||||
|
@ -200,10 +203,11 @@ proc traceBlock*(db: BaseChainDB, header: BlockHeader, body: BlockBody, tracerFl
|
|||
doAssert(body.transactions.len != 0)
|
||||
|
||||
var gasUsed = GasInt(0)
|
||||
let fork = header.blockNumber.toFork
|
||||
|
||||
for tx in body.transactions:
|
||||
let sender = tx.getSender
|
||||
gasUsed = gasUsed + processTransaction(tx, sender, vmState)
|
||||
gasUsed = gasUsed + processTransaction(tx, sender, vmState, fork)
|
||||
|
||||
result = vmState.getTracingResult()
|
||||
result["gas"] = %gasUsed
|
||||
|
|
|
@ -103,12 +103,6 @@ proc snapshot*(comp: BaseComputation) =
|
|||
comp.dbsnapshot.intermediateRoot = comp.vmState.accountDb.rootHash
|
||||
comp.vmState.blockHeader.stateRoot = comp.vmState.accountDb.rootHash
|
||||
|
||||
proc revert*(comp: BaseComputation, burnsGas = false) =
|
||||
comp.dbsnapshot.transaction.rollback()
|
||||
comp.vmState.accountDb.rootHash = comp.dbsnapshot.intermediateRoot
|
||||
comp.vmState.blockHeader.stateRoot = comp.dbsnapshot.intermediateRoot
|
||||
comp.error = Error(info: getCurrentExceptionMsg(), burnsGas: burnsGas)
|
||||
|
||||
proc commit*(comp: BaseComputation) =
|
||||
comp.dbsnapshot.transaction.commit()
|
||||
comp.vmState.accountDb.rootHash = comp.vmState.blockHeader.stateRoot
|
||||
|
|
|
@ -640,4 +640,4 @@ const
|
|||
# The Yellow Paper is special casing the GasQuadDivisor.
|
||||
# It is defined in Appendix G with the other GasFeeKind constants
|
||||
# instead of Appendix E for precompiled contracts
|
||||
GasQuadDivisor* = 100
|
||||
GasQuadDivisor* = 20
|
||||
|
|
|
@ -31,3 +31,7 @@ proc returnGas*(gasMeter: var GasMeter; amount: GasInt) =
|
|||
proc refundGas*(gasMeter: var GasMeter; amount: GasInt) =
|
||||
gasMeter.gasRefunded += amount
|
||||
trace "GAS REFUND", consumed = gasMeter.gasRemaining - amount, amount, refunded = gasMeter.gasRefunded
|
||||
|
||||
proc resetGas*(gasMeter: var GasMeter) =
|
||||
gasMeter.gasRemaining = gasMeter.startGas
|
||||
gasMeter.gasRefunded = 0
|
||||
|
|
|
@ -197,6 +197,11 @@ op sha3, inline = true, startPos, length:
|
|||
## 0x20, Compute Keccak-256 hash.
|
||||
let (pos, len) = (startPos.toInt, length.toInt)
|
||||
|
||||
# TODO:
|
||||
# "randomStatetest14.json", # SHA3 offset
|
||||
# "sha3_deja.json", # SHA3 startPos
|
||||
# both test require Uint256 to calculate startpos/offset
|
||||
|
||||
if pos < 0 or len < 0 or pos > 2147483648:
|
||||
raise newException(OutOfBoundsRead, "Out of bounds memory access")
|
||||
|
||||
|
@ -340,9 +345,9 @@ op returnDataSize, inline = true:
|
|||
op returnDataCopy, inline = false, memStartPos, copyStartPos, size:
|
||||
## 0x3e, Copy output data from the previous call to memory.
|
||||
let (memPos, copyPos, len) = (memStartPos.cleanMemRef, copyStartPos.cleanMemRef, size.cleanMemRef)
|
||||
|
||||
let gasCost = computation.gasCosts[ReturnDataCopy].m_handler(computation.memory.len, memPos, len)
|
||||
computation.gasMeter.consumeGas(
|
||||
computation.gasCosts[ReturnDataCopy].m_handler(memPos, copyPos, len),
|
||||
gasCost,
|
||||
reason="returnDataCopy fee")
|
||||
|
||||
if copyPos + len > computation.returnData.len:
|
||||
|
@ -354,10 +359,7 @@ op returnDataCopy, inline = false, memStartPos, copyStartPos, size:
|
|||
&"for data from index {copyStartPos} to {copyStartPos + size}. Return data is {computation.returnData.len} in \n" &
|
||||
"length")
|
||||
|
||||
computation.memory.extend(memPos, len)
|
||||
|
||||
computation.memory.write(memPos):
|
||||
computation.returnData.toOpenArray(copyPos, copyPos+len)
|
||||
computation.memory.writePaddedResult(computation.returnData, memPos, copyPos, len)
|
||||
|
||||
# ##########################################
|
||||
# 40s: Block Information
|
||||
|
@ -437,6 +439,7 @@ op sload, inline = true, slot:
|
|||
|
||||
op sstore, inline = false, slot, value:
|
||||
## 0x55, Save word to storage.
|
||||
checkInStaticContext(computation)
|
||||
|
||||
let (currentValue, existing) = computation.vmState.readOnlyStateDB.getStorage(computation.msg.storageAddress, slot)
|
||||
|
||||
|
@ -589,8 +592,6 @@ proc setupCreate(computation: BaseComputation, memPos, len: int, value: Uint256)
|
|||
|
||||
op create, inline = false, value, startPosition, size:
|
||||
## 0xf0, Create a new account with associated code.
|
||||
# TODO: Forked create for Homestead
|
||||
|
||||
let (memPos, len) = (startPosition.cleanMemRef, size.cleanMemRef)
|
||||
if not computation.canTransfer(memPos, len, value):
|
||||
push: 0
|
||||
|
@ -608,6 +609,7 @@ op create, inline = false, value, startPosition, size:
|
|||
else:
|
||||
push: childComp.msg.storageAddress
|
||||
|
||||
checkInStaticContext(computation)
|
||||
childComp.applyMessage(Create)
|
||||
|
||||
proc callParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, MsgFlags) =
|
||||
|
@ -683,8 +685,8 @@ proc staticCallParams(computation: BaseComputation): (UInt256, UInt256, EthAddre
|
|||
result = (gas,
|
||||
0.u256, # value
|
||||
to,
|
||||
ZERO_ADDRESS, # sender
|
||||
ZERO_ADDRESS, # codeAddress
|
||||
computation.msg.storageAddress, # sender
|
||||
to, # codeAddress
|
||||
memoryInputStartPosition,
|
||||
memoryInputSize,
|
||||
memoryOutputStartPosition,
|
||||
|
@ -751,6 +753,9 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
|||
when opCode == CallCode:
|
||||
childMsg.storageAddress = computation.msg.storageAddress
|
||||
|
||||
when opCode == DelegateCall:
|
||||
childMsg.codeAddress = codeAddress
|
||||
|
||||
var childComp = newBaseComputation(
|
||||
computation.vmState,
|
||||
computation.vmState.blockNumber,
|
||||
|
@ -783,6 +788,10 @@ template genCall(callName: untyped, opCode: Op): untyped =
|
|||
computation.memOutPos,
|
||||
childComp.output.toOpenArray(0, actualOutputSize - 1))
|
||||
|
||||
when opCode == Call:
|
||||
if emvcStatic == computation.msg.flags and childComp.msg.value > 0.u256:
|
||||
raise newException(StaticContextError, "Cannot modify state while inside of a STATICCALL context")
|
||||
|
||||
childComp.applyMessage(opCode)
|
||||
|
||||
genCall(call, Call)
|
||||
|
@ -805,7 +814,6 @@ op returnOp, inline = false, startPos, size:
|
|||
op revert, inline = false, startPos, size:
|
||||
## 0xfd, Halt execution reverting state changes but returning data and remaining gas.
|
||||
let (pos, len) = (startPos.cleanMemRef, size.cleanMemRef)
|
||||
|
||||
computation.gasMeter.consumeGas(
|
||||
computation.gasCosts[Revert].m_handler(computation.memory.len, pos, len),
|
||||
reason = "REVERT"
|
||||
|
@ -813,6 +821,8 @@ op revert, inline = false, startPos, size:
|
|||
|
||||
computation.memory.extend(pos, len)
|
||||
computation.output = computation.memory.read(pos, len)
|
||||
# setError(msg, false) will signal cheap revert
|
||||
computation.setError("REVERT opcode executed", false)
|
||||
|
||||
proc selfDestructImpl(computation: BaseComputation, beneficiary: EthAddress) =
|
||||
## 0xff Halt execution and register account for later deletion.
|
||||
|
@ -856,6 +866,8 @@ op selfDestructEip150, inline = false:
|
|||
selfDestructImpl(computation, beneficiary)
|
||||
|
||||
op selfDestructEip161, inline = false:
|
||||
checkInStaticContext(computation)
|
||||
|
||||
let
|
||||
beneficiary = computation.stack.popAddress()
|
||||
stateDb = computation.vmState.readOnlyStateDb
|
||||
|
|
|
@ -100,8 +100,15 @@ macro genSwap*(): untyped =
|
|||
func `name`*(computation: BaseComputation) {.inline.}=
|
||||
computation.stack.swap(`pos`)
|
||||
|
||||
template checkInStaticContext*(comp: BaseComputation) =
|
||||
# TODO: if possible, this check only appear
|
||||
# when fork >= FkByzantium
|
||||
if emvcStatic == comp.msg.flags:
|
||||
raise newException(StaticContextError, "Cannot modify state while inside of a STATICCALL context")
|
||||
|
||||
proc logImpl(c: BaseComputation, opcode: Op, topicCount: int) =
|
||||
doAssert(topicCount in 0 .. 4)
|
||||
checkInStaticContext(c)
|
||||
let (memStartPosition, size) = c.stack.popInt(2)
|
||||
let (memPos, len) = (memStartPosition.cleanMemRef, size.cleanMemRef)
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ proc rangeToPadded*[T: StUint](x: openarray[byte], first, last: int): T =
|
|||
## including 0-length sequences.
|
||||
|
||||
let lo = max(0, first)
|
||||
let hi = min(x.high, last)
|
||||
let hi = min(min(x.high, last), (lo+T.bits div 8)-1)
|
||||
|
||||
if not(lo <= hi):
|
||||
return # 0
|
||||
|
|
|
@ -196,6 +196,15 @@ proc genSpuriousJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compile
|
|||
|
||||
let SpuriousOpDispatch {.compileTime.}: array[Op, NimNode] = genSpuriousJumpTable(TangerineOpDispatch)
|
||||
|
||||
proc genByzantiumJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} =
|
||||
result = ops
|
||||
result[Revert] = newIdentNode "revert"
|
||||
result[ReturnDataSize] = newIdentNode "returnDataSize"
|
||||
result[ReturnDataCopy] = newIdentNode "returnDataCopy"
|
||||
result[StaticCall] = newIdentNode"staticCall"
|
||||
|
||||
let ByzantiumOpDispatch {.compileTime.}: array[Op, NimNode] = genByzantiumJumpTable(SpuriousOpDispatch)
|
||||
|
||||
proc opTableToCaseStmt(opTable: array[Op, NimNode], computation: NimNode): NimNode =
|
||||
|
||||
let instr = quote do: `computation`.instr
|
||||
|
@ -263,6 +272,9 @@ macro genTangerineDispatch(computation: BaseComputation): untyped =
|
|||
macro genSpuriousDispatch(computation: BaseComputation): untyped =
|
||||
result = opTableToCaseStmt(SpuriousOpDispatch, computation)
|
||||
|
||||
macro genByzantiumDispatch(computation: BaseComputation): untyped =
|
||||
result = opTableToCaseStmt(ByzantiumOpDispatch, computation)
|
||||
|
||||
proc frontierVM(computation: BaseComputation) =
|
||||
genFrontierDispatch(computation)
|
||||
|
||||
|
@ -275,6 +287,9 @@ proc tangerineVM(computation: BaseComputation) =
|
|||
proc spuriousVM(computation: BaseComputation) {.gcsafe.} =
|
||||
genSpuriousDispatch(computation)
|
||||
|
||||
proc byzantiumVM(computation: BaseComputation) {.gcsafe.} =
|
||||
genByzantiumDispatch(computation)
|
||||
|
||||
proc selectVM(computation: BaseComputation, fork: Fork) {.gcsafe.} =
|
||||
# TODO: Optimise getting fork and updating opCodeExec only when necessary
|
||||
case fork
|
||||
|
@ -286,13 +301,15 @@ proc selectVM(computation: BaseComputation, fork: Fork) {.gcsafe.} =
|
|||
computation.tangerineVM()
|
||||
of FkSpurious:
|
||||
computation.spuriousVM()
|
||||
of FKByzantium:
|
||||
computation.byzantiumVM()
|
||||
else:
|
||||
raise newException(VMError, "Unknown or not implemented fork: " & $fork)
|
||||
|
||||
proc executeOpcodes(computation: BaseComputation) =
|
||||
try:
|
||||
let fork = computation.getFork
|
||||
if `computation`.execPrecompiles(fork):
|
||||
if computation.execPrecompiles(fork):
|
||||
computation.nextProc()
|
||||
return
|
||||
computation.selectVM(fork)
|
||||
|
|
|
@ -21,13 +21,18 @@ proc getSignature*(computation: BaseComputation): (array[32, byte], Signature) =
|
|||
template data: untyped = computation.msg.data
|
||||
var bytes: array[65, byte]
|
||||
let maxPos = min(data.high, 127)
|
||||
if maxPos >= 32:
|
||||
if maxPos >= 31:
|
||||
# extract message hash
|
||||
result[0][0..31] = data[0..31]
|
||||
if maxPos >= 127:
|
||||
# Copy message data to buffer
|
||||
# Note that we need to rearrange to R, S, V
|
||||
bytes[0..63] = data[64..127]
|
||||
elif maxPos >= 64:
|
||||
bytes[0..(maxPos-64)] = data[64..maxPos]
|
||||
else:
|
||||
result[0][0..maxPos] = data[0..maxPos]
|
||||
|
||||
var VOK = true
|
||||
let v = data[63]
|
||||
for x in 32..<63:
|
||||
|
@ -51,6 +56,24 @@ proc getPoint[T: G1|G2](t: typedesc[T], data: openarray[byte]): Point[T] =
|
|||
raise newException(ValidationError, "Could not get point value")
|
||||
if not py.fromBytes2(data.toOpenArray(nextOffset, nextOffset * 2 - 1)):
|
||||
raise newException(ValidationError, "Could not get point value")
|
||||
|
||||
# "ecpairing_perturb_g2_by_field_modulus_again.json",
|
||||
# "ecpairing_perturb_zeropoint_by_field_modulus.json",
|
||||
# "ecpairing_perturb_g2_by_field_modulus.json",
|
||||
# modulus comparion in FQ2.fromBytes produce different result
|
||||
const
|
||||
modulus = Uint256.fromHex("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47")
|
||||
let a = Uint256.fromBytesBE(data.toOpenArray(0, 31), false)
|
||||
let b = Uint256.fromBytesBE(data.toOpenArray(32, 63), false)
|
||||
when T is G2:
|
||||
let c = Uint256.fromBytesBE(data.toOpenArray(64, 95), false)
|
||||
let d = Uint256.fromBytesBE(data.toOpenArray(96, 127), false)
|
||||
if a >= modulus or b >= modulus or c >= modulus or d >= modulus:
|
||||
raise newException(ValidationError, "value greater than field modulus")
|
||||
else:
|
||||
if a >= modulus or b >= modulus:
|
||||
raise newException(ValidationError, "value greater than field modulus")
|
||||
|
||||
if px.isZero() and py.isZero():
|
||||
result = T.zero()
|
||||
else:
|
||||
|
@ -133,6 +156,7 @@ proc modExpInternal(computation: BaseComputation, base_len, exp_len, mod_len: in
|
|||
else: log2(exp) # highest-bit in exponent
|
||||
else:
|
||||
let first32 = rawMsg.rangeToPadded[:Uint256](96 + base_len, 95 + base_len + exp_len)
|
||||
# TODO: `modexpRandomInput.json` require Uint256 arithmetic for this code below
|
||||
if not first32.isZero:
|
||||
8 * (exp_len - 32) + first32.log2
|
||||
else:
|
||||
|
@ -160,16 +184,24 @@ proc modExpInternal(computation: BaseComputation, base_len, exp_len, mod_len: in
|
|||
result[0] = 1
|
||||
|
||||
# Start with EVM special cases
|
||||
if modulo <= 1:
|
||||
let output = if modulo <= 1:
|
||||
# If m == 0: EVM returns 0.
|
||||
# If m == 1: we can shortcut that to 0 as well
|
||||
computation.rawOutput = @(zero())
|
||||
zero()
|
||||
elif exp.isZero():
|
||||
# If 0^0: EVM returns 1
|
||||
# For all x != 0, x^0 == 1 as well
|
||||
computation.rawOutput = @(one())
|
||||
one()
|
||||
else:
|
||||
computation.rawOutput = @(powmod(base, exp, modulo).toByteArrayBE)
|
||||
powmod(base, exp, modulo).toByteArrayBE
|
||||
|
||||
# maximum output len is the same as mod_len
|
||||
# if it less than mod_len, it will be zero padded at left
|
||||
if output.len >= mod_len:
|
||||
computation.rawOutput = @(output[^mod_len..^1])
|
||||
else:
|
||||
computation.rawOutput = newSeq[byte](mod_len)
|
||||
computation.rawOutput[^output.len..^1] = output[0..^1]
|
||||
|
||||
proc modExp*(computation: BaseComputation) =
|
||||
## Modular exponentiation precompiled contract
|
||||
|
@ -201,6 +233,8 @@ proc modExp*(computation: BaseComputation) =
|
|||
raise newException(ValueError, "The Nimbus VM doesn't support modular exponentiation with numbers larger than uint8192")
|
||||
|
||||
proc bn256ecAdd*(computation: BaseComputation) =
|
||||
computation.gasMeter.consumeGas(GasECAdd, reason = "ecAdd Precompile")
|
||||
|
||||
var
|
||||
input: array[128, byte]
|
||||
output: array[64, byte]
|
||||
|
@ -216,11 +250,11 @@ proc bn256ecAdd*(computation: BaseComputation) =
|
|||
# we can discard here because we supply proper buffer
|
||||
discard apo.get().toBytes(output)
|
||||
|
||||
# TODO: gas computation
|
||||
# computation.gasMeter.consumeGas(gasFee, reason = "ecAdd Precompile")
|
||||
computation.rawOutput = @output
|
||||
|
||||
proc bn256ecMul*(computation: BaseComputation) =
|
||||
computation.gasMeter.consumeGas(GasECMul, reason="ecMul Precompile")
|
||||
|
||||
var
|
||||
input: array[96, byte]
|
||||
output: array[64, byte]
|
||||
|
@ -238,17 +272,18 @@ proc bn256ecMul*(computation: BaseComputation) =
|
|||
# we can discard here because we supply buffer of proper size
|
||||
discard apo.get().toBytes(output)
|
||||
|
||||
# TODO: gas computation
|
||||
# computation.gasMeter.consumeGas(gasFee, reason="ecMul Precompile")
|
||||
computation.rawOutput = @output
|
||||
|
||||
proc bn256ecPairing*(computation: BaseComputation) =
|
||||
var output: array[32, byte]
|
||||
|
||||
let msglen = len(computation.msg.data)
|
||||
if msglen mod 192 != 0:
|
||||
raise newException(ValidationError, "Invalid input length")
|
||||
|
||||
let numPoints = msglen div 192
|
||||
let gasFee = GasECPairingBase + numPoints * GasECPairingPerPoint
|
||||
computation.gasMeter.consumeGas(gasFee, reason="ecPairing Precompile")
|
||||
|
||||
var output: array[32, byte]
|
||||
if msglen == 0:
|
||||
# we can discard here because we supply buffer of proper size
|
||||
discard BNU256.one().toBytes(output)
|
||||
|
@ -271,8 +306,6 @@ proc bn256ecPairing*(computation: BaseComputation) =
|
|||
# we can discard here because we supply buffer of proper size
|
||||
discard BNU256.one().toBytes(output)
|
||||
|
||||
# TODO: gas computation
|
||||
# computation.gasMeter.consumeGas(gasFee, reason="ecPairing Precompile")
|
||||
computation.rawOutput = @output
|
||||
|
||||
proc execPrecompiles*(computation: BaseComputation, fork: Fork): bool {.inline.} =
|
||||
|
@ -295,8 +328,14 @@ proc execPrecompiles*(computation: BaseComputation, fork: Fork): bool {.inline.}
|
|||
of paEcAdd: bn256ecAdd(computation)
|
||||
of paEcMul: bn256ecMul(computation)
|
||||
of paPairing: bn256ecPairing(computation)
|
||||
except ValidationError:
|
||||
# swallow any precompiles errors
|
||||
debug "execPrecompiles validation error", msg=getCurrentExceptionMsg()
|
||||
except ValueError:
|
||||
debug "execPrecompiles value error", msg=getCurrentExceptionMsg()
|
||||
except OutOfGas:
|
||||
let msg = getCurrentExceptionMsg()
|
||||
# cannot use setError here, cyclic dependency
|
||||
computation.error = Error(info: msg, burnsGas: true)
|
||||
except:
|
||||
let msg = getCurrentExceptionMsg()
|
||||
if fork >= FKByzantium and precompile > paIdentity:
|
||||
computation.error = Error(info: msg, burnsGas: true)
|
||||
else:
|
||||
# swallow any other precompiles errors
|
||||
debug "execPrecompiles validation error", msg=msg
|
||||
|
|
|
@ -26,16 +26,7 @@ proc validateTransaction*(vmState: BaseVMState, transaction: Transaction, sender
|
|||
transaction.accountNonce == account.nonce and
|
||||
account.balance >= gasCost
|
||||
|
||||
proc setupComputation*(vmState: BaseVMState, tx: Transaction, sender, recipient: EthAddress, forkOverride=none(Fork)) : BaseComputation =
|
||||
let fork =
|
||||
if forkOverride.isSome:
|
||||
forkOverride.get
|
||||
else:
|
||||
vmState.blockNumber.toFork
|
||||
|
||||
if fork >= FkSpurious:
|
||||
vmState.touchedAccounts.incl(vmState.blockHeader.coinbase)
|
||||
|
||||
proc setupComputation*(vmState: BaseVMState, tx: Transaction, sender, recipient: EthAddress, fork: Fork) : BaseComputation =
|
||||
var gas = tx.gasLimit - tx.intrinsicGas
|
||||
|
||||
# TODO: refactor message to use byterange
|
||||
|
@ -66,10 +57,10 @@ proc setupComputation*(vmState: BaseVMState, tx: Transaction, sender, recipient:
|
|||
options = newMessageOptions(origin = sender,
|
||||
createAddress = recipient))
|
||||
|
||||
result = newBaseComputation(vmState, vmState.blockNumber, msg, forkOverride)
|
||||
result = newBaseComputation(vmState, vmState.blockNumber, msg, some(fork))
|
||||
doAssert result.isOriginComputation
|
||||
|
||||
proc execComputation*(computation: var BaseComputation): bool =
|
||||
proc execComputation*(computation: var BaseComputation) =
|
||||
if computation.msg.isCreate:
|
||||
computation.applyMessage(Create)
|
||||
else:
|
||||
|
@ -88,8 +79,8 @@ proc execComputation*(computation: var BaseComputation): bool =
|
|||
if computation.getFork >= FkSpurious:
|
||||
computation.collectTouchedAccounts(computation.vmState.touchedAccounts)
|
||||
|
||||
result = computation.isSuccess
|
||||
if result:
|
||||
computation.vmstate.status = computation.isSuccess
|
||||
if computation.isSuccess:
|
||||
computation.vmState.addLogs(computation.logEntries)
|
||||
|
||||
proc refundGas*(computation: BaseComputation, tx: Transaction, sender: EthAddress): GasInt =
|
||||
|
|
|
@ -26,6 +26,7 @@ type
|
|||
accountDb* : AccountStateDB
|
||||
cumulativeGasUsed*: GasInt
|
||||
touchedAccounts*: HashSet[EthAddress]
|
||||
status* : bool
|
||||
|
||||
AccessLogs* = ref object
|
||||
reads*: Table[string, string]
|
||||
|
|
|
@ -233,10 +233,10 @@ proc runVM*(blockNumber: Uint256, chainDB: BaseChainDB, boa: Assembler): bool =
|
|||
var computation = initComputation(blockNumber, chainDB, boa.code, boa.data)
|
||||
|
||||
let gas = computation.gasMeter.gasRemaining
|
||||
let computationResult = execComputation(computation)
|
||||
execComputation(computation)
|
||||
let gasUsed = gas - computation.gasMeter.gasRemaining
|
||||
|
||||
if computationResult:
|
||||
if computation.isSuccess:
|
||||
if boa.success == false:
|
||||
error "different success value", expected=boa.success, actual=true
|
||||
return false
|
||||
|
|
|
@ -13,11 +13,49 @@
|
|||
# being mostly used for short-term regression prevention.
|
||||
func allowedFailingGeneralStateTest*(folder, name: string): bool =
|
||||
let allowedFailingGeneralStateTests = @[
|
||||
# a family of UInt256 truncated to int problems
|
||||
"randomStatetest14.json", # SHA3 offset
|
||||
"randomStatetest85.json", # CALL* memoffset
|
||||
"sha3_deja.json", # SHA3 startPos
|
||||
# modexp exp_len & friends truncated to int
|
||||
# and not OOG where it should OOG
|
||||
"modexpRandomInput.json",
|
||||
|
||||
"CreateOOGafterInitCodeReturndataSize.json",
|
||||
"RevertInCreateInInit.json",
|
||||
"modexp.json",
|
||||
|
||||
# see precompiles getPoint[G2]
|
||||
"ecpairing_perturb_g2_by_field_modulus_again.json",
|
||||
"ecpairing_perturb_zeropoint_by_field_modulus.json",
|
||||
"ecpairing_perturb_g2_by_field_modulus.json",
|
||||
|
||||
# all these tests below actually pass
|
||||
# but they are very slow
|
||||
# byzantium slow
|
||||
"LoopCallsDepthThenRevert3.json",
|
||||
"LoopCallsDepthThenRevert2.json",
|
||||
"LoopCallsDepthThenRevert.json",
|
||||
"static_Call50000.json",
|
||||
"static_Call50000_ecrec.json",
|
||||
"static_Call50000_identity.json",
|
||||
"static_Call50000_identity2.json",
|
||||
"static_Call50000_rip160.json",
|
||||
"static_Call50000_sha256.json",
|
||||
"LoopCallsThenRevert.json",
|
||||
"LoopDelegateCallsDepthThenRevert.json",
|
||||
"recursiveCreateReturnValue.json",
|
||||
"static_Call1024PreCalls2.json",
|
||||
"Callcode1024BalanceTooLow.json",
|
||||
"static_Call1024BalanceTooLow.json",
|
||||
"static_Call1024BalanceTooLow2.json",
|
||||
"static_Call1024OOG.json",
|
||||
"static_Call1024PreCalls3.json",
|
||||
"static_Call1024PreCalls.json",
|
||||
"static_Call1MB1024Calldepth.json",
|
||||
|
||||
# Homestead recursives
|
||||
#["ContractCreationSpam.json",
|
||||
"ContractCreationSpam.json",
|
||||
"Call1024OOG.json",
|
||||
"Call1024PreCalls.json",
|
||||
"CallRecursiveBombPreCall.json",
|
||||
|
@ -42,6 +80,6 @@ func allowedFailingGeneralStateTest*(folder, name: string): bool =
|
|||
"callcodecallcallcode_ABCB_RECURSIVE.json",
|
||||
"callcodecallcodecall_ABCB_RECURSIVE.json",
|
||||
"callcodecallcodecallcode_ABCB_RECURSIVE.json",
|
||||
"callcallcallcode_ABCB_RECURSIVE.json",]#
|
||||
"callcallcallcode_ABCB_RECURSIVE.json"
|
||||
]
|
||||
result = name in allowedFailingGeneralStateTests
|
||||
|
|
|
@ -114,7 +114,7 @@ proc testFixtureIndexes(tester: Tester, testStatusIMPL: var TestStatus) =
|
|||
if tester.fork >= FkSpurious:
|
||||
let recipient = tester.tx.getRecipient()
|
||||
let miner = tester.header.coinbase
|
||||
let touchedAccounts = [sender, miner, recipient]
|
||||
let touchedAccounts = [miner] # [sender, miner, recipient]
|
||||
for account in touchedAccounts:
|
||||
debug "state clearing", account
|
||||
if db.accountExists(account) and db.isEmptyAccount(account):
|
||||
|
@ -122,7 +122,7 @@ proc testFixtureIndexes(tester: Tester, testStatusIMPL: var TestStatus) =
|
|||
|
||||
return
|
||||
|
||||
gasUsed = tester.tx.processTransaction(sender, vmState, some(tester.fork))
|
||||
gasUsed = tester.tx.processTransaction(sender, vmState, tester.fork)
|
||||
|
||||
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus,
|
||||
debugMode = false, supportedForks: set[Fork] = supportedForks) =
|
||||
|
|
|
@ -24,7 +24,7 @@ const
|
|||
FkByzantium: "Byzantium",
|
||||
}.toTable
|
||||
|
||||
supportedForks* = {FkFrontier, FkHomestead, FkTangerine, FkSpurious}
|
||||
supportedForks* = {FkFrontier, FkHomestead, FkTangerine, FkSpurious, FkByzantium}
|
||||
|
||||
type
|
||||
Status* {.pure.} = enum OK, Fail, Skip
|
||||
|
|
Loading…
Reference in New Issue