eip2718: test_blockchain_json pass test

This commit is contained in:
jangko 2021-05-15 13:37:40 +07:00
parent 2a9c3982d9
commit 79044f1e92
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
10 changed files with 212 additions and 78 deletions

View File

@ -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]

View File

@ -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)

View File

@ -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")

View File

@ -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,

View File

@ -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

View File

@ -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 chains EIP-155 unique identifier.
ChainIdOp = 0x46, # Get current chains EIP-155 unique identifier.
SelfBalance = 0x47, # Get current contract's balance.
# 50s: Stack, Memory, Storage and Flow Operations

View File

@ -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"

View File

@ -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

View File

@ -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)

View File

@ -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)))