eip2718: test_blockchain_json pass test
This commit is contained in:
parent
2a9c3982d9
commit
79044f1e92
|
@ -17,11 +17,6 @@ import
|
|||
json_serialization/lexer
|
||||
|
||||
type
|
||||
# beware that although in some cases
|
||||
# chainId have identical value to networkId
|
||||
# they are separate entity
|
||||
ChainId* = distinct uint
|
||||
|
||||
ChainOptions = object
|
||||
chainId : ChainId
|
||||
homesteadBlock : Option[BlockNumber]
|
||||
|
|
|
@ -40,9 +40,9 @@ proc validateTransaction*(vmState: BaseVMState, tx: Transaction,
|
|||
require=tx.intrinsicGas(fork)
|
||||
return
|
||||
|
||||
if tx.accountNonce != nonce:
|
||||
if tx.nonce != nonce:
|
||||
debug "invalid tx: account nonce mismatch",
|
||||
txNonce=tx.accountnonce,
|
||||
txNonce=tx.nonce,
|
||||
accountNonce=nonce
|
||||
return
|
||||
|
||||
|
@ -96,19 +96,30 @@ func logsBloom(logs: openArray[Log]): LogsBloom =
|
|||
|
||||
func createBloom*(receipts: openArray[Receipt]): Bloom =
|
||||
var bloom: LogsBloom
|
||||
for receipt in receipts:
|
||||
bloom.value = bloom.value or logsBloom(receipt.logs).value
|
||||
for rec in receipts:
|
||||
bloom.value = bloom.value or logsBloom(rec.logs).value
|
||||
result = bloom.value.toByteArrayBE
|
||||
|
||||
proc makeReceipt*(vmState: BaseVMState, fork = FkFrontier): Receipt =
|
||||
if fork < FkByzantium:
|
||||
result.stateRootOrStatus = hashOrStatus(vmState.accountDb.rootHash)
|
||||
else:
|
||||
result.stateRootOrStatus = hashOrStatus(vmState.status)
|
||||
proc makeReceipt*(vmState: BaseVMState, fork: Fork, txType: TxType): Receipt =
|
||||
if txType == AccessListTxType:
|
||||
var rec = AccessListReceipt(
|
||||
status: vmState.status,
|
||||
cumulativeGasUsed: vmState.cumulativeGasUsed,
|
||||
logs: vmState.getAndClearLogEntries()
|
||||
)
|
||||
rec.bloom = logsBloom(rec.logs).value.toByteArrayBE
|
||||
return Receipt(receiptType: AccessListReceiptType, accessListReceipt: rec)
|
||||
|
||||
result.cumulativeGasUsed = vmState.cumulativeGasUsed
|
||||
result.logs = vmState.getAndClearLogEntries()
|
||||
result.bloom = logsBloom(result.logs).value.toByteArrayBE
|
||||
var rec: LegacyReceipt
|
||||
if fork < FkByzantium:
|
||||
rec.stateRootOrStatus = hashOrStatus(vmState.accountDb.rootHash)
|
||||
else:
|
||||
rec.stateRootOrStatus = hashOrStatus(vmState.status)
|
||||
|
||||
rec.cumulativeGasUsed = vmState.cumulativeGasUsed
|
||||
rec.logs = vmState.getAndClearLogEntries()
|
||||
rec.bloom = logsBloom(rec.logs).value.toByteArrayBE
|
||||
Receipt(receiptType: LegacyReceiptType, legacyReceipt: rec)
|
||||
|
||||
func eth(n: int): Uint256 {.compileTime.} =
|
||||
n.u256 * pow(10.u256, 18)
|
||||
|
@ -178,7 +189,7 @@ proc processBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, v
|
|||
else:
|
||||
debug "Could not get sender", txIndex, tx
|
||||
return ValidationResult.Error
|
||||
vmState.receipts[txIndex] = makeReceipt(vmState, fork)
|
||||
vmState.receipts[txIndex] = makeReceipt(vmState, fork, tx.txType)
|
||||
|
||||
if header.ommersHash != EMPTY_UNCLE_HASH:
|
||||
let h = chainDB.persistUncles(body.uncles)
|
||||
|
|
|
@ -7,11 +7,30 @@
|
|||
|
||||
import
|
||||
./constants, ./errors, eth/[common, keys], ./utils,
|
||||
stew/shims/macros,
|
||||
./vm_types2, ./vm_gas_costs
|
||||
|
||||
import eth/common/transaction as common_transaction
|
||||
export common_transaction
|
||||
|
||||
template txc(fn: untyped, params: varargs[untyped]): untyped =
|
||||
if tx.txType == LegacyTxType:
|
||||
unpackArgs(fn, [tx.legacyTx, params])
|
||||
else:
|
||||
unpackArgs(fn, [tx.accessListTx, params])
|
||||
|
||||
template txField(field: untyped): untyped =
|
||||
if tx.txType == LegacyTxType:
|
||||
tx.legacyTx.field
|
||||
else:
|
||||
tx.accessListTx.field
|
||||
|
||||
template txFieldAsgn(field, data: untyped) =
|
||||
if tx.txType == LegacyTxType:
|
||||
tx.legacyTx.field = data
|
||||
else:
|
||||
tx.accessListTx.field = data
|
||||
|
||||
func intrinsicGas*(data: openarray[byte], fork: Fork): GasInt =
|
||||
result = gasFees[fork][GasTransaction]
|
||||
for i in data:
|
||||
|
@ -20,7 +39,7 @@ func intrinsicGas*(data: openarray[byte], fork: Fork): GasInt =
|
|||
else:
|
||||
result += gasFees[fork][GasTXDataNonZero]
|
||||
|
||||
proc intrinsicGas*(tx: Transaction, fork: Fork): GasInt =
|
||||
proc intrinsicGas*(tx: TxTypes, fork: Fork): GasInt =
|
||||
# Compute the baseline gas cost for this transaction. This is the amount
|
||||
# of gas needed to send this transaction (but that is not actually used
|
||||
# for computation)
|
||||
|
@ -29,13 +48,16 @@ proc intrinsicGas*(tx: Transaction, fork: Fork): GasInt =
|
|||
if tx.isContractCreation:
|
||||
result = result + gasFees[fork][GasTXCreate]
|
||||
|
||||
proc getSignature*(transaction: Transaction, output: var Signature): bool =
|
||||
proc intrinsicGas*(tx: Transaction, fork: Fork): GasInt =
|
||||
txc(intrinsicGas, fork)
|
||||
|
||||
proc getSignature*(tx: LegacyTx, output: var Signature): bool =
|
||||
var bytes: array[65, byte]
|
||||
bytes[0..31] = transaction.R.toByteArrayBE()
|
||||
bytes[32..63] = transaction.S.toByteArrayBE()
|
||||
bytes[0..31] = tx.R.toByteArrayBE()
|
||||
bytes[32..63] = tx.S.toByteArrayBE()
|
||||
|
||||
# TODO: V will become a byte or range soon.
|
||||
var v = transaction.V.int
|
||||
var v = tx.V
|
||||
if v >= EIP155_CHAIN_ID_OFFSET:
|
||||
v = 28 - (v and 0x01)
|
||||
elif v == 27 or v == 28:
|
||||
|
@ -50,32 +72,76 @@ proc getSignature*(transaction: Transaction, output: var Signature): bool =
|
|||
return true
|
||||
return false
|
||||
|
||||
proc toSignature*(transaction: Transaction): Signature =
|
||||
if not getSignature(transaction, result):
|
||||
proc getSignature*(tx: AccessListTx, output: var Signature): bool =
|
||||
var bytes: array[65, byte]
|
||||
bytes[0..31] = tx.R.toByteArrayBE()
|
||||
bytes[32..63] = tx.S.toByteArrayBE()
|
||||
bytes[64] = tx.V.byte
|
||||
let sig = Signature.fromRaw(bytes)
|
||||
if sig.isOk:
|
||||
output = sig[]
|
||||
return true
|
||||
return false
|
||||
|
||||
proc getSignature*(tx: Transaction, output: var Signature): bool =
|
||||
txc(getSignature, output)
|
||||
|
||||
proc toSignature*(tx: Transaction): Signature =
|
||||
if not getSignature(tx, result):
|
||||
raise newException(Exception, "Invalid signature")
|
||||
|
||||
proc getSender*(transaction: Transaction, output: var EthAddress): bool =
|
||||
proc getSender*(tx: LegacyTx | AccessListTx | Transaction, output: var EthAddress): bool =
|
||||
## Find the address the transaction was sent from.
|
||||
var sig: Signature
|
||||
if transaction.getSignature(sig):
|
||||
var txHash = transaction.txHashNoSignature
|
||||
if tx.getSignature(sig):
|
||||
var txHash = tx.txHashNoSignature
|
||||
let pubkey = recover(sig, SkMessage(txHash.data))
|
||||
if pubkey.isOk:
|
||||
output = pubkey[].toCanonicalAddress()
|
||||
result = true
|
||||
|
||||
proc getSender*(transaction: Transaction): EthAddress =
|
||||
proc getSender*(tx: LegacyTx | AccessListTx | Transaction): EthAddress =
|
||||
## Raises error on failure to recover public key
|
||||
if not transaction.getSender(result):
|
||||
if not tx.getSender(result):
|
||||
raise newException(ValidationError, "Could not derive sender address from transaction")
|
||||
|
||||
proc getRecipient*(tx: Transaction, sender: EthAddress): EthAddress =
|
||||
proc getRecipient*(tx: LegacyTx | AccessListTx, sender: EthAddress): EthAddress =
|
||||
if tx.isContractCreation:
|
||||
result = generateAddress(sender, tx.accountNonce)
|
||||
result = generateAddress(sender, tx.nonce)
|
||||
else:
|
||||
result = tx.to
|
||||
|
||||
proc validate*(tx: Transaction, fork: Fork) =
|
||||
proc getRecipient*(tx: Transaction, sender: EthAddress): EthAddress =
|
||||
txc(getRecipient, sender)
|
||||
|
||||
proc gasLimit*(tx: Transaction): GasInt =
|
||||
txField(gasLimit)
|
||||
|
||||
proc gasPrice*(tx: Transaction): GasInt =
|
||||
txField(gasPrice)
|
||||
|
||||
proc value*(tx: Transaction): UInt256 =
|
||||
txField(value)
|
||||
|
||||
proc isContractCreation*(tx: Transaction): bool =
|
||||
txField(isContractCreation)
|
||||
|
||||
proc to*(tx: Transaction): EthAddress =
|
||||
txField(to)
|
||||
|
||||
proc payload*(tx: Transaction): Blob =
|
||||
txField(payload)
|
||||
|
||||
proc nonce*(tx: Transaction): AccountNonce =
|
||||
txField(nonce)
|
||||
|
||||
proc `payload=`*(tx: var Transaction, data: Blob) =
|
||||
txFieldAsgn(payload, data)
|
||||
|
||||
proc `gasLimit=`*(tx: var Transaction, data: GasInt) =
|
||||
txFieldAsgn(gasLimit, data)
|
||||
|
||||
proc validate*(tx: LegacyTx, fork: Fork) =
|
||||
# Hook called during instantiation to ensure that all transaction
|
||||
# parameters pass validation rules
|
||||
if tx.intrinsicGas(fork) > tx.gasLimit:
|
||||
|
@ -87,18 +153,18 @@ proc validate*(tx: Transaction, fork: Fork) =
|
|||
raise newException(ValidationError, "Invalid signature or failed message verification")
|
||||
|
||||
var
|
||||
vMin = 27
|
||||
vMax = 28
|
||||
vMin = 27'i64
|
||||
vMax = 28'i64
|
||||
|
||||
if tx.V.int >= EIP155_CHAIN_ID_OFFSET:
|
||||
let chainId = (tx.V.int - EIP155_CHAIN_ID_OFFSET) div 2
|
||||
if tx.V >= EIP155_CHAIN_ID_OFFSET:
|
||||
let chainId = (tx.V - EIP155_CHAIN_ID_OFFSET) div 2
|
||||
vMin = 35 + (2 * chainId)
|
||||
vMax = vMin + 1
|
||||
|
||||
var isValid = tx.R >= Uint256.one
|
||||
isValid = isValid and tx.S >= Uint256.one
|
||||
isValid = isValid and tx.V.int >= vMin
|
||||
isValid = isValid and tx.V.int <= vMax
|
||||
isValid = isValid and tx.V >= vMin
|
||||
isValid = isValid and tx.V <= vMax
|
||||
isValid = isValid and tx.S < SECPK1_N
|
||||
isValid = isValid and tx.R < SECPK1_N
|
||||
|
||||
|
@ -107,4 +173,3 @@ proc validate*(tx: Transaction, fork: Fork) =
|
|||
|
||||
if not isValid:
|
||||
raise newException(ValidationError, "Invalid transaction")
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ proc asmSetupComputation(tx: Transaction, sender: EthAddress, vmState: BaseVMSta
|
|||
forkOverride = forkOverride,
|
||||
)
|
||||
|
||||
let contractAddress = generateAddress(sender, tx.accountNonce)
|
||||
let contractAddress = generateAddress(sender, tx.nonce)
|
||||
let msg = Message(
|
||||
kind: evmcCall,
|
||||
depth: 0,
|
||||
|
|
|
@ -535,7 +535,7 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
|||
Number: fixed GasBase,
|
||||
Difficulty: fixed GasBase,
|
||||
GasLimit: fixed GasBase,
|
||||
ChainID: fixed GasBase,
|
||||
ChainIdOp: fixed GasBase,
|
||||
SelfBalance: fixed GasLow,
|
||||
|
||||
# 50s: Stack, Memory, Storage and Flow Operations
|
||||
|
|
|
@ -79,7 +79,7 @@ fill_enum_holes:
|
|||
Difficulty = 0x44, # Get the block's difficulty.
|
||||
GasLimit = 0x45, # Get the block's gas limit.
|
||||
|
||||
ChainId = 0x46, # Get current chain’s EIP-155 unique identifier.
|
||||
ChainIdOp = 0x46, # Get current chain’s EIP-155 unique identifier.
|
||||
SelfBalance = 0x47, # Get current contract's balance.
|
||||
|
||||
# 50s: Stack, Memory, Storage and Flow Operations
|
||||
|
|
|
@ -223,7 +223,7 @@ let PetersburgOpDispatch {.compileTime.}: array[Op, NimNode] = genPetersburgJump
|
|||
|
||||
proc genIstanbulJumpTable(ops: array[Op, NimNode]): array[Op, NimNode] {.compileTime.} =
|
||||
result = ops
|
||||
result[ChainId] = newIdentNode "chainId"
|
||||
result[ChainIdOp] = newIdentNode "chainId"
|
||||
result[SelfBalance] = newIdentNode "selfBalance"
|
||||
result[SStore] = newIdentNode "sstoreEIP2200"
|
||||
|
||||
|
|
|
@ -81,23 +81,25 @@ proc parseBlockHeader*(n: JsonNode): BlockHeader =
|
|||
n.fromJson "nonce", result.nonce
|
||||
|
||||
proc parseTransaction*(n: JsonNode): Transaction =
|
||||
n.fromJson "nonce", result.accountNonce
|
||||
n.fromJson "gasPrice", result.gasPrice
|
||||
n.fromJson "gas", result.gasLimit
|
||||
var tx: LegacyTx
|
||||
n.fromJson "nonce", tx.nonce
|
||||
n.fromJson "gasPrice", tx.gasPrice
|
||||
n.fromJson "gas", tx.gasLimit
|
||||
|
||||
result.isContractCreation = n["to"].kind == JNull
|
||||
if not result.isContractCreation:
|
||||
n.fromJson "to", result.to
|
||||
tx.isContractCreation = n["to"].kind == JNull
|
||||
if not tx.isContractCreation:
|
||||
n.fromJson "to", tx.to
|
||||
|
||||
n.fromJson "value", result.value
|
||||
n.fromJson "input", result.payload
|
||||
n.fromJson "v", result.V
|
||||
n.fromJson "r", result.R
|
||||
n.fromJson "s", result.S
|
||||
n.fromJson "value", tx.value
|
||||
n.fromJson "input", tx.payload
|
||||
n.fromJson "v", tx.V
|
||||
n.fromJson "r", tx.R
|
||||
n.fromJson "s", tx.S
|
||||
|
||||
var sender = result.getSender()
|
||||
var sender = tx.getSender()
|
||||
doAssert sender.prefixHex == n["from"].getStr()
|
||||
doAssert n["hash"].getStr() == result.rlpHash().prefixHex
|
||||
doAssert n["hash"].getStr() == tx.rlpHash().prefixHex
|
||||
result = Transaction(txType: LegacyTxType, legacyTx: tx)
|
||||
|
||||
proc parseLog(n: JsonNode): Log =
|
||||
n.fromJson "address", result.address
|
||||
|
@ -118,18 +120,20 @@ proc parseLogs(n: JsonNode): seq[Log] =
|
|||
result = @[]
|
||||
|
||||
proc parseReceipt*(n: JsonNode): Receipt =
|
||||
var rec: LegacyReceipt
|
||||
if n.hasKey("root"):
|
||||
var hash: Hash256
|
||||
n.fromJson "root", hash
|
||||
result.stateRootOrStatus = hashOrStatus(hash)
|
||||
rec.stateRootOrStatus = hashOrStatus(hash)
|
||||
else:
|
||||
var status: int
|
||||
n.fromJson "status", status
|
||||
result.stateRootOrStatus = hashOrStatus(status == 1)
|
||||
rec.stateRootOrStatus = hashOrStatus(status == 1)
|
||||
|
||||
n.fromJson "cumulativeGasUsed", result.cumulativeGasUsed
|
||||
n.fromJson "logsBloom", result.bloom
|
||||
result.logs = parseLogs(n["logs"])
|
||||
n.fromJson "cumulativeGasUsed", rec.cumulativeGasUsed
|
||||
n.fromJson "logsBloom", rec.bloom
|
||||
rec.logs = parseLogs(n["logs"])
|
||||
Receipt(receiptType: LegacyReceiptType, legacyReceipt: rec)
|
||||
|
||||
proc headerHash*(n: JsonNode): Hash256 =
|
||||
n.fromJson "hash", result
|
||||
|
|
|
@ -525,7 +525,7 @@ proc runTester(tester: var Tester, chainDB: BaseChainDB, testStatusIMPL: var Tes
|
|||
try:
|
||||
let (_, _, _) = tester.applyFixtureBlockToChain(testBlock,
|
||||
chainDB, checkSeal, validation = true, testStatusIMPL)
|
||||
except ValueError, ValidationError, BlockNotFound, MalformedRlpError, RlpTypeMismatch:
|
||||
except ValueError, ValidationError, BlockNotFound, RlpError:
|
||||
# failure is expected on this bad block
|
||||
check (testBlock.hasException or (not testBlock.goodBlock))
|
||||
noError = false
|
||||
|
@ -601,7 +601,7 @@ proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = fal
|
|||
var success = true
|
||||
try:
|
||||
tester.runTester(chainDB, testStatusIMPL)
|
||||
|
||||
success = testStatusIMPL == OK
|
||||
let latestBlockHash = chainDB.getCanonicalHead().blockHash
|
||||
if latestBlockHash != tester.lastBlockHash:
|
||||
verifyStateDB(fixture["postState"], tester.vmState.readOnlyStateDB)
|
||||
|
|
|
@ -184,32 +184,91 @@ proc verifyStateDB*(wantedState: JsonNode, stateDB: ReadOnlyStateDB) =
|
|||
if wantedNonce != actualNonce:
|
||||
raise newException(ValidationError, &"{ac} nonceDiff {wantedNonce.toHex} != {actualNonce.toHex}")
|
||||
|
||||
proc parseAccessList(n: JsonNode): AccessList =
|
||||
if n.kind == JNull:
|
||||
return
|
||||
|
||||
for x in n:
|
||||
var ap = AccessPair(
|
||||
address: parseAddress(x["address"].getStr)
|
||||
)
|
||||
let sks = x["storageKeys"]
|
||||
for sk in sks:
|
||||
ap.storageKeys.add hexToByteArray[32](sk.getStr())
|
||||
result.add ap
|
||||
|
||||
proc signTx(tx: var LegacyTx, privateKey: PrivateKey) =
|
||||
let sig = sign(privateKey, tx.rlpEncode)
|
||||
let raw = sig.toRaw()
|
||||
tx.R = fromBytesBE(Uint256, raw[0..31])
|
||||
tx.S = fromBytesBE(Uint256, raw[32..63])
|
||||
tx.V = raw[64].int64 + 27
|
||||
|
||||
proc signTx(tx: var AccessListTx, privateKey: PrivateKey) =
|
||||
let sig = sign(privateKey, tx.rlpEncode)
|
||||
let raw = sig.toRaw()
|
||||
tx.R = fromBytesBE(Uint256, raw[0..31])
|
||||
tx.S = fromBytesBE(Uint256, raw[32..63])
|
||||
tx.V = raw[64].int64
|
||||
|
||||
proc getFixtureTransaction*(j: JsonNode, dataIndex, gasIndex, valueIndex: int): Transaction =
|
||||
result.accountNonce = j["nonce"].getHexadecimalInt.AccountNonce
|
||||
result.gasPrice = j["gasPrice"].getHexadecimalInt
|
||||
result.gasLimit = j["gasLimit"][gasIndex].getHexadecimalInt
|
||||
let nonce = j["nonce"].getHexadecimalInt.AccountNonce
|
||||
let gasPrice = j["gasPrice"].getHexadecimalInt
|
||||
let gasLimit = j["gasLimit"][gasIndex].getHexadecimalInt
|
||||
|
||||
var toAddr: EthAddress
|
||||
var contract: bool
|
||||
|
||||
# TODO: there are a couple fixtures which appear to distinguish between
|
||||
# empty and 0 transaction.to; check/verify whether correct conditions.
|
||||
let rawTo = j["to"].getStr
|
||||
if rawTo == "":
|
||||
result.to = "0x".parseAddress
|
||||
result.isContractCreation = true
|
||||
toAddr = "0x".parseAddress
|
||||
contract = true
|
||||
else:
|
||||
result.to = rawTo.parseAddress
|
||||
result.isContractCreation = false
|
||||
result.value = fromHex(UInt256, j["value"][valueIndex].getStr)
|
||||
result.payload = j["data"][dataIndex].getStr.safeHexToSeqByte
|
||||
toAddr = rawTo.parseAddress
|
||||
contract = false
|
||||
|
||||
let value = fromHex(UInt256, j["value"][valueIndex].getStr)
|
||||
let payload = j["data"][dataIndex].getStr.safeHexToSeqByte
|
||||
|
||||
var secretKey = j["secretKey"].getStr
|
||||
removePrefix(secretKey, "0x")
|
||||
let privateKey = PrivateKey.fromHex(secretKey).tryGet()
|
||||
let sig = sign(privateKey, result.rlpEncode)
|
||||
let raw = sig.toRaw()
|
||||
|
||||
result.R = fromBytesBE(Uint256, raw[0..31])
|
||||
result.S = fromBytesBE(Uint256, raw[32..63])
|
||||
result.V = raw[64] + 27.byte
|
||||
if j.hasKey("accessLists"):
|
||||
let accList = j["accessLists"][dataIndex]
|
||||
var tx = AccessListTx(
|
||||
nonce: nonce,
|
||||
gasPrice: gasPrice,
|
||||
gasLimit: gasLimit,
|
||||
to: toAddr,
|
||||
isContractCreation: contract,
|
||||
value: value,
|
||||
payload: payload,
|
||||
accessList: parseAccessList(accList),
|
||||
chainId: common.ChainId(1)
|
||||
)
|
||||
signTx(tx, privateKey)
|
||||
Transaction(
|
||||
txType: AccessListTxType,
|
||||
accessListTx: tx
|
||||
)
|
||||
else:
|
||||
var tx = LegacyTx(
|
||||
nonce: nonce,
|
||||
gasPrice: gasPrice,
|
||||
gasLimit: gasLimit,
|
||||
to: toAddr,
|
||||
isContractCreation: contract,
|
||||
value: value,
|
||||
payload: payload
|
||||
)
|
||||
signTx(tx, privateKey)
|
||||
Transaction(
|
||||
txType: LegacyTxType,
|
||||
legacyTx: tx
|
||||
)
|
||||
|
||||
proc hashLogEntries*(logs: seq[Log]): string =
|
||||
toLowerAscii("0x" & $keccakHash(rlp.encode(logs)))
|
||||
|
|
Loading…
Reference in New Issue