hive: add ethereum/engine simulator
some test with multiple client still need polishing. merge test using specially crafted blocks need to be added.
This commit is contained in:
parent
83bdde55aa
commit
4d126f2461
|
@ -0,0 +1,329 @@
|
||||||
|
import
|
||||||
|
std/[times, tables],
|
||||||
|
chronicles,
|
||||||
|
nimcrypto,
|
||||||
|
stew/byteutils,
|
||||||
|
eth/common, chronos,
|
||||||
|
web3/engine_api_types,
|
||||||
|
json_rpc/rpcclient,
|
||||||
|
../../../nimbus/merge/mergeutils,
|
||||||
|
../../../nimbus/debug,
|
||||||
|
./engine_client
|
||||||
|
|
||||||
|
# Consensus Layer Client Mock used to sync the Execution Clients once the TTD has been reached
|
||||||
|
type
|
||||||
|
CLMocker* = ref object
|
||||||
|
nextFeeRecipient*: EthAddress
|
||||||
|
nextPayloadID: PayloadID
|
||||||
|
|
||||||
|
# PoS Chain History Information
|
||||||
|
prevRandaoHistory*: Table[uint64, Hash256]
|
||||||
|
executedPayloadHistory*: Table[uint64, ExecutionPayloadV1]
|
||||||
|
|
||||||
|
# Latest broadcasted data using the PoS Engine API
|
||||||
|
latestFinalizedNumber*: uint64
|
||||||
|
latestFinalizedHeader*: common.BlockHeader
|
||||||
|
latestPayloadBuilt* : ExecutionPayloadV1
|
||||||
|
latestExecutedPayload*: ExecutionPayloadV1
|
||||||
|
latestForkchoice* : ForkchoiceStateV1
|
||||||
|
|
||||||
|
# Merge related
|
||||||
|
firstPoSBlockNumber : Option[uint64]
|
||||||
|
ttdReached : bool
|
||||||
|
|
||||||
|
client : RpcClient
|
||||||
|
ttd : DifficultyInt
|
||||||
|
|
||||||
|
BlockProcessCallbacks* = object
|
||||||
|
onPayloadProducerSelected* : proc(): bool {.gcsafe.}
|
||||||
|
onGetPayloadID* : proc(): bool {.gcsafe.}
|
||||||
|
onGetPayload* : proc(): bool {.gcsafe.}
|
||||||
|
onNewPayloadBroadcast* : proc(): bool {.gcsafe.}
|
||||||
|
onHeadBlockForkchoiceBroadcast* : proc(): bool {.gcsafe.}
|
||||||
|
onSafeBlockForkchoiceBroadcast* : proc(): bool {.gcsafe.}
|
||||||
|
onFinalizedBlockForkchoiceBroadcast* : proc(): bool {.gcsafe.}
|
||||||
|
|
||||||
|
|
||||||
|
proc init*(cl: CLMocker, client: RpcClient, ttd: DifficultyInt) =
|
||||||
|
cl.client = client
|
||||||
|
cl.ttd = ttd
|
||||||
|
|
||||||
|
proc newClMocker*(client: RpcClient, ttd: DifficultyInt): CLMocker =
|
||||||
|
new result
|
||||||
|
result.init(client, ttd)
|
||||||
|
|
||||||
|
proc waitForTTD*(cl: CLMocker): Future[bool] {.async.} =
|
||||||
|
let (header, waitRes) = await cl.client.waitForTTD(cl.ttd)
|
||||||
|
if not waitRes:
|
||||||
|
error "timeout while waiting for TTD"
|
||||||
|
return false
|
||||||
|
|
||||||
|
cl.latestFinalizedHeader = header
|
||||||
|
cl.ttdReached = true
|
||||||
|
|
||||||
|
let headerHash = BlockHash(common.blockHash(cl.latestFinalizedHeader).data)
|
||||||
|
cl.latestForkchoice.headBlockHash = headerHash
|
||||||
|
cl.latestForkchoice.safeBlockHash = headerHash
|
||||||
|
cl.latestForkchoice.finalizedBlockHash = headerHash
|
||||||
|
cl.latestFinalizedNumber = cl.latestFinalizedHeader.blockNumber.truncate(uint64)
|
||||||
|
|
||||||
|
let res = cl.client.forkchoiceUpdatedV1(cl.latestForkchoice)
|
||||||
|
if res.isErr:
|
||||||
|
error "forkchoiceUpdated error", msg=res.error
|
||||||
|
return false
|
||||||
|
|
||||||
|
let s = res.get()
|
||||||
|
if s.payloadStatus.status != PayloadExecutionStatus.valid:
|
||||||
|
error "forkchoiceUpdated response",
|
||||||
|
status=s.payloadStatus.status
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc pickNextPayloadProducer(cl: CLMocker): bool =
|
||||||
|
let nRes = cl.client.blockNumber()
|
||||||
|
if nRes.isErr:
|
||||||
|
error "CLMocker: could not get block number", msg=nRes.error
|
||||||
|
return false
|
||||||
|
|
||||||
|
let lastBlockNumber = nRes.get
|
||||||
|
if cl.latestFinalizedNumber != lastBlockNumber:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var header: common.BlockHeader
|
||||||
|
let hRes = cl.client.headerByNumber(lastBlockNumber, header)
|
||||||
|
if hRes.isErr:
|
||||||
|
error "CLMocker: Could not get block header", msg=hRes.error
|
||||||
|
return false
|
||||||
|
|
||||||
|
let lastBlockHash = header.blockHash
|
||||||
|
if cl.latestFinalizedHeader.blockHash != lastBlockHash:
|
||||||
|
error "CLMocker: Failed to obtain a client on the latest block number"
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc getNextPayloadID(cl: CLMocker): bool =
|
||||||
|
# Generate a random value for the PrevRandao field
|
||||||
|
var nextPrevRandao: Hash256
|
||||||
|
doAssert nimcrypto.randomBytes(nextPrevRandao.data) == 32
|
||||||
|
|
||||||
|
let timestamp = Quantity toUnix(cl.latestFinalizedHeader.timestamp + 1.seconds)
|
||||||
|
let payloadAttributes = PayloadAttributesV1(
|
||||||
|
timestamp: timestamp,
|
||||||
|
prevRandao: FixedBytes[32] nextPrevRandao.data,
|
||||||
|
suggestedFeeRecipient: Address cl.nextFeeRecipient,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Save random value
|
||||||
|
let number = cl.latestFinalizedHeader.blockNumber.truncate(uint64) + 1
|
||||||
|
cl.prevRandaoHistory[number] = nextPrevRandao
|
||||||
|
|
||||||
|
let res = cl.client.forkchoiceUpdatedV1(cl.latestForkchoice, some(payloadAttributes))
|
||||||
|
if res.isErr:
|
||||||
|
error "CLMocker: Could not send forkchoiceUpdatedV1", msg=res.error
|
||||||
|
return false
|
||||||
|
|
||||||
|
let s = res.get()
|
||||||
|
if s.payloadStatus.status != PayloadExecutionStatus.valid:
|
||||||
|
error "CLMocker: Unexpected forkchoiceUpdated Response from Payload builder",
|
||||||
|
status=s.payloadStatus.status
|
||||||
|
|
||||||
|
doAssert s.payLoadID.isSome
|
||||||
|
cl.nextPayloadID = s.payloadID.get()
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc getNextPayload(cl: CLMocker): bool =
|
||||||
|
let res = cl.client.getPayloadV1(cl.nextPayloadID)
|
||||||
|
if res.isErr:
|
||||||
|
error "CLMocker: Could not getPayload",
|
||||||
|
payloadID=toHex(cl.nextPayloadID)
|
||||||
|
return false
|
||||||
|
|
||||||
|
cl.latestPayloadBuilt = res.get()
|
||||||
|
let header = toBlockHeader(cl.latestPayloadBuilt)
|
||||||
|
let blockHash = BlockHash header.blockHash.data
|
||||||
|
if blockHash != cl.latestPayloadBuilt.blockHash:
|
||||||
|
error "getNextPayload blockHash mismatch",
|
||||||
|
expected=cl.latestPayloadBuilt.blockHash.toHex,
|
||||||
|
get=blockHash.toHex
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc broadcastNewPayload(cl: CLMocker, payload: ExecutionPayloadV1): Result[PayloadStatusV1, string] =
|
||||||
|
let res = cl.client.newPayloadV1(payload)
|
||||||
|
return res
|
||||||
|
|
||||||
|
proc broadcastNextNewPayload(cl: CLMocker): bool =
|
||||||
|
let res = cl.broadcastNewPayload(cl.latestPayloadBuilt)
|
||||||
|
if res.isErr:
|
||||||
|
error "CLMocker: broadcastNewPayload Error", msg=res.error
|
||||||
|
return false
|
||||||
|
|
||||||
|
let s = res.get()
|
||||||
|
if s.status == PayloadExecutionStatus.valid:
|
||||||
|
# The client is synced and the payload was immediately validated
|
||||||
|
# https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md:
|
||||||
|
# - If validation succeeds, the response MUST contain {status: VALID, latestValidHash: payload.blockHash}
|
||||||
|
let blockHash = cl.latestPayloadBuilt.blockHash
|
||||||
|
if s.latestValidHash.isNone:
|
||||||
|
error "CLMocker: NewPayload returned VALID status with nil LatestValidHash",
|
||||||
|
expected=blockHash.toHex
|
||||||
|
return false
|
||||||
|
|
||||||
|
let latestValidHash = s.latestValidHash.get()
|
||||||
|
if latestValidHash != BlockHash(blockHash):
|
||||||
|
error "CLMocker: NewPayload returned VALID status with incorrect LatestValidHash",
|
||||||
|
get=latestValidHash.toHex, expected=blockHash.toHex
|
||||||
|
return false
|
||||||
|
|
||||||
|
elif s.status == PayloadExecutionStatus.accepted:
|
||||||
|
# The client is not synced but the payload was accepted
|
||||||
|
# https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md:
|
||||||
|
# - {status: ACCEPTED, latestValidHash: null, validationError: null} if the following conditions are met:
|
||||||
|
# the blockHash of the payload is valid
|
||||||
|
# the payload doesn't extend the canonical chain
|
||||||
|
# the payload hasn't been fully validated.
|
||||||
|
let nullHash = BlockHash Hash256().data
|
||||||
|
let latestValidHash = s.latestValidHash.get(nullHash)
|
||||||
|
if s.latestValidHash.isSome and latestValidHash != nullHash:
|
||||||
|
error "CLMocker: NewPayload returned ACCEPTED status with incorrect LatestValidHash",
|
||||||
|
hash=latestValidHash.toHex
|
||||||
|
return false
|
||||||
|
|
||||||
|
else:
|
||||||
|
error "CLMocker: broadcastNewPayload Response",
|
||||||
|
status=s.status
|
||||||
|
return false
|
||||||
|
|
||||||
|
cl.latestExecutedPayload = cl.latestPayloadBuilt
|
||||||
|
let number = uint64 cl.latestPayloadBuilt.blockNumber
|
||||||
|
cl.executedPayloadHistory[number] = cl.latestPayloadBuilt
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc broadcastForkchoiceUpdated*(cl: CLMocker,
|
||||||
|
update: ForkchoiceStateV1): Result[ForkchoiceUpdatedResponse, string] =
|
||||||
|
let res = cl.client.forkchoiceUpdatedV1(update)
|
||||||
|
return res
|
||||||
|
|
||||||
|
proc broadcastLatestForkchoice(cl: CLMocker): bool =
|
||||||
|
let res = cl.broadcastForkchoiceUpdated(cl.latestForkchoice)
|
||||||
|
if res.isErr:
|
||||||
|
error "CLMocker: broadcastForkchoiceUpdated Error", msg=res.error
|
||||||
|
return false
|
||||||
|
|
||||||
|
let s = res.get()
|
||||||
|
if s.payloadStatus.status != PayloadExecutionStatus.valid:
|
||||||
|
error "CLMocker: broadcastForkchoiceUpdated Response",
|
||||||
|
status=s.payloadStatus.status
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc produceSingleBlock*(cl: CLMocker, cb: BlockProcessCallbacks): bool {.gcsafe.} =
|
||||||
|
doAssert(cl.ttdReached)
|
||||||
|
|
||||||
|
if not cl.pickNextPayloadProducer():
|
||||||
|
return false
|
||||||
|
|
||||||
|
if cb.onPayloadProducerSelected != nil:
|
||||||
|
if not cb.onPayloadProducerSelected():
|
||||||
|
return false
|
||||||
|
|
||||||
|
if not cl.getNextPayloadID():
|
||||||
|
return false
|
||||||
|
|
||||||
|
if cb.onGetPayloadID != nil:
|
||||||
|
if not cb.onGetPayloadID():
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Give the client a delay between getting the payload ID and actually retrieving the payload
|
||||||
|
#time.Sleep(PayloadProductionClientDelay)
|
||||||
|
|
||||||
|
if not cl.getNextPayload():
|
||||||
|
return false
|
||||||
|
|
||||||
|
if cb.onGetPayload != nil:
|
||||||
|
if not cb.onGetPayload():
|
||||||
|
return false
|
||||||
|
|
||||||
|
if not cl.broadcastNextNewPayload():
|
||||||
|
return false
|
||||||
|
|
||||||
|
if cb.onNewPayloadBroadcast != nil:
|
||||||
|
if not cb.onNewPayloadBroadcast():
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Broadcast forkchoice updated with new HeadBlock to all clients
|
||||||
|
let blockHash = cl.latestPayloadBuilt.blockHash
|
||||||
|
cl.latestForkchoice.headBlockHash = blockHash
|
||||||
|
if not cl.broadcastLatestForkchoice():
|
||||||
|
return false
|
||||||
|
|
||||||
|
if cb.onHeadBlockForkchoiceBroadcast != nil:
|
||||||
|
if not cb.onHeadBlockForkchoiceBroadcast():
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Broadcast forkchoice updated with new SafeBlock to all clients
|
||||||
|
cl.latestForkchoice.safeBlockHash = blockHash
|
||||||
|
if not cl.broadcastLatestForkchoice():
|
||||||
|
return false
|
||||||
|
|
||||||
|
if cb.onSafeBlockForkchoiceBroadcast != nil:
|
||||||
|
if not cb.onSafeBlockForkchoiceBroadcast():
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Broadcast forkchoice updated with new FinalizedBlock to all clients
|
||||||
|
cl.latestForkchoice.finalizedBlockHash = blockHash
|
||||||
|
if not cl.broadcastLatestForkchoice():
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Save the number of the first PoS block
|
||||||
|
if cl.firstPoSBlockNumber.isNone:
|
||||||
|
let number = cl.latestFinalizedHeader.blockNumber.truncate(uint64) + 1
|
||||||
|
cl.firstPoSBlockNumber = some(number)
|
||||||
|
|
||||||
|
# Save the header of the latest block in the PoS chain
|
||||||
|
cl.latestFinalizedNumber = cl.latestFinalizedNumber + 1
|
||||||
|
|
||||||
|
# Check if any of the clients accepted the new payload
|
||||||
|
var newHeader: common.BlockHeader
|
||||||
|
let res = cl.client.headerByNumber(cl.latestFinalizedNumber, newHeader)
|
||||||
|
if res.isErr:
|
||||||
|
error "CLMock ProduceSingleBlock", msg=res.error
|
||||||
|
return false
|
||||||
|
|
||||||
|
let newHash = BlockHash newHeader.blockHash.data
|
||||||
|
if newHash != cl.latestPayloadBuilt.blockHash:
|
||||||
|
error "CLMocker: None of the clients accepted the newly constructed payload",
|
||||||
|
hash=newHash.toHex
|
||||||
|
return false
|
||||||
|
|
||||||
|
cl.latestFinalizedHeader = newHeader
|
||||||
|
|
||||||
|
if cb.onFinalizedBlockForkchoiceBroadcast != nil:
|
||||||
|
if not cb.onFinalizedBlockForkchoiceBroadcast():
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
# Loop produce PoS blocks by using the Engine API
|
||||||
|
proc produceBlocks*(cl: CLMocker, blockCount: int, cb: BlockProcessCallbacks): bool {.gcsafe.} =
|
||||||
|
# Produce requested amount of blocks
|
||||||
|
for i in 0..<blockCount:
|
||||||
|
if not cl.produceSingleBlock(cb):
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
# Check whether a block number is a PoS block
|
||||||
|
proc isBlockPoS*(cl: CLMocker, bn: common.BlockNumber): bool =
|
||||||
|
if cl.firstPoSBlockNumber.isNone:
|
||||||
|
return false
|
||||||
|
|
||||||
|
let number = cl.firstPoSBlockNumber.get()
|
||||||
|
let bn = bn.truncate(uint64)
|
||||||
|
if number > bn:
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
|
@ -0,0 +1,191 @@
|
||||||
|
import
|
||||||
|
std/[times, json],
|
||||||
|
stew/byteutils,
|
||||||
|
eth/[common, rlp], chronos,
|
||||||
|
web3/engine_api_types,
|
||||||
|
json_rpc/rpcclient,
|
||||||
|
../../../tests/rpcclient/eth_api,
|
||||||
|
../../../premix/parser,
|
||||||
|
../../../nimbus/rpc/hexstrings,
|
||||||
|
../../../premix/parser
|
||||||
|
|
||||||
|
import web3/engine_api as web3_engine_api
|
||||||
|
|
||||||
|
proc forkchoiceUpdatedV1*(client: RpcClient,
|
||||||
|
update: ForkchoiceStateV1,
|
||||||
|
payloadAttributes = none(PayloadAttributesV1)):
|
||||||
|
Result[ForkchoiceUpdatedResponse, string] =
|
||||||
|
try:
|
||||||
|
let res = waitFor client.engine_forkchoiceUpdatedV1(update, payloadAttributes)
|
||||||
|
return ok(res)
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc getPayloadV1*(client: RpcClient, payloadId: PayloadID): Result[ExecutionPayloadV1, string] =
|
||||||
|
try:
|
||||||
|
let res = waitFor client.engine_getPayloadV1(payloadId)
|
||||||
|
return ok(res)
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc newPayloadV1*(client: RpcClient,
|
||||||
|
payload: ExecutionPayloadV1):
|
||||||
|
Result[PayloadStatusV1, string] =
|
||||||
|
try:
|
||||||
|
let res = waitFor client.engine_newPayloadV1(payload)
|
||||||
|
return ok(res)
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc toBlockNumber(n: Option[HexQuantityStr]): common.BlockNumber =
|
||||||
|
if n.isNone:
|
||||||
|
return 0.toBlockNumber
|
||||||
|
toBlockNumber(hexToInt(string n.get, uint64))
|
||||||
|
|
||||||
|
proc toBlockNonce(n: Option[HexDataStr]): common.BlockNonce =
|
||||||
|
if n.isNone:
|
||||||
|
return default(BlockNonce)
|
||||||
|
hexToByteArray(string n.get, result)
|
||||||
|
|
||||||
|
proc toBaseFeePerGas(n: Option[HexQuantityStr]): Option[UInt256] =
|
||||||
|
if n.isNone:
|
||||||
|
return none(UInt256)
|
||||||
|
some(UInt256.fromHex(string n.get))
|
||||||
|
|
||||||
|
proc toBlockHeader(bc: eth_api.BlockObject): common.BlockHeader =
|
||||||
|
common.BlockHeader(
|
||||||
|
blockNumber: toBlockNumber(bc.number),
|
||||||
|
parentHash : bc.parentHash,
|
||||||
|
nonce : toBlockNonce(bc.nonce),
|
||||||
|
ommersHash : bc.sha3Uncles,
|
||||||
|
bloom : BloomFilter bc.logsBloom,
|
||||||
|
txRoot : bc.transactionsRoot,
|
||||||
|
stateRoot : bc.stateRoot,
|
||||||
|
receiptRoot: bc.receiptsRoot,
|
||||||
|
coinbase : bc.miner,
|
||||||
|
difficulty : UInt256.fromHex(string bc.difficulty),
|
||||||
|
extraData : hexToSeqByte(string bc.extraData),
|
||||||
|
mixDigest : bc.mixHash,
|
||||||
|
gasLimit : hexToInt(string bc.gasLimit, GasInt),
|
||||||
|
gasUsed : hexToInt(string bc.gasUsed, GasInt),
|
||||||
|
timestamp : initTime(hexToInt(string bc.timestamp, int64), 0),
|
||||||
|
fee : toBaseFeePerGas(bc.baseFeePerGas)
|
||||||
|
)
|
||||||
|
|
||||||
|
proc toTransactions(txs: openArray[JsonNode]): seq[Transaction] =
|
||||||
|
for x in txs:
|
||||||
|
result.add parseTransaction(x)
|
||||||
|
|
||||||
|
proc waitForTTD*(client: RpcClient,
|
||||||
|
ttd: DifficultyInt): Future[(common.BlockHeader, bool)] {.async.} =
|
||||||
|
let period = chronos.seconds(5)
|
||||||
|
var loop = 0
|
||||||
|
var emptyHeader: common.BlockHeader
|
||||||
|
while loop < 5:
|
||||||
|
let res = await client.eth_getBlockByNumber("latest", false)
|
||||||
|
if res.isNone:
|
||||||
|
return (emptyHeader, false)
|
||||||
|
let bc = res.get()
|
||||||
|
if hexToInt(string bc.totalDifficulty, int64).u256 >= ttd:
|
||||||
|
return (toBlockHeader(bc), true)
|
||||||
|
|
||||||
|
await sleepAsync(period)
|
||||||
|
inc loop
|
||||||
|
|
||||||
|
return (emptyHeader, false)
|
||||||
|
|
||||||
|
proc blockNumber*(client: RpcClient): Result[uint64, string] =
|
||||||
|
try:
|
||||||
|
let res = waitFor client.eth_blockNumber()
|
||||||
|
return ok(hexToInt(string res, uint64))
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc headerByNumber*(client: RpcClient, number: uint64, output: var common.BlockHeader): Result[void, string] =
|
||||||
|
try:
|
||||||
|
let qty = encodeQuantity(number)
|
||||||
|
let res = waitFor client.eth_getBlockByNumber(string qty, false)
|
||||||
|
if res.isNone:
|
||||||
|
return err("failed to get blockHeader: " & $number)
|
||||||
|
output = toBlockHeader(res.get())
|
||||||
|
return ok()
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc blockByNumber*(client: RpcClient, number: uint64, output: var common.EthBlock): Result[void, string] =
|
||||||
|
try:
|
||||||
|
let qty = encodeQuantity(number)
|
||||||
|
let res = waitFor client.eth_getBlockByNumber(string qty, true)
|
||||||
|
if res.isNone:
|
||||||
|
return err("failed to get block: " & $number)
|
||||||
|
let blk = res.get()
|
||||||
|
output.header = toBlockHeader(blk)
|
||||||
|
output.txs = toTransactions(blk.transactions)
|
||||||
|
return ok()
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc latestHeader*(client: RpcClient, output: var common.BlockHeader): Result[void, string] =
|
||||||
|
try:
|
||||||
|
let res = waitFor client.eth_getBlockByNumber("latest", false)
|
||||||
|
if res.isNone:
|
||||||
|
return err("failed to get latest blockHeader")
|
||||||
|
output = toBlockHeader(res.get())
|
||||||
|
return ok()
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc latestBlock*(client: RpcClient, output: var common.EthBlock): Result[void, string] =
|
||||||
|
try:
|
||||||
|
let res = waitFor client.eth_getBlockByNumber("latest", true)
|
||||||
|
if res.isNone:
|
||||||
|
return err("failed to get latest blockHeader")
|
||||||
|
let blk = res.get()
|
||||||
|
output.header = toBlockHeader(blk)
|
||||||
|
output.txs = toTransactions(blk.transactions)
|
||||||
|
return ok()
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc sendTransaction*(client: RpcClient, tx: common.Transaction): Result[void, string] =
|
||||||
|
try:
|
||||||
|
let encodedTx = rlp.encode(tx)
|
||||||
|
let res = waitFor client.eth_sendRawTransaction(hexDataStr(encodedTx))
|
||||||
|
let txHash = rlpHash(tx)
|
||||||
|
let getHash = Hash256(data: hexToByteArray[32](string res))
|
||||||
|
if txHash != getHash:
|
||||||
|
return err("sendTransaction: tx hash mismatch")
|
||||||
|
return ok()
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc balanceAt*(client: RpcClient, address: EthAddress): Result[UInt256, string] =
|
||||||
|
try:
|
||||||
|
let res = waitFor client.eth_getBalance(ethAddressStr(address), "latest")
|
||||||
|
return ok(UInt256.fromHex(res.string))
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc txReceipt*(client: RpcClient, txHash: Hash256): Result[eth_api.ReceiptObject, string] =
|
||||||
|
try:
|
||||||
|
let res = waitFor client.eth_getTransactionReceipt(txHash)
|
||||||
|
if res.isNone:
|
||||||
|
return err("failed to get receipt: " & txHash.data.toHex)
|
||||||
|
return ok(res.get)
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc storageAt*(client: RpcClient, address: EthAddress, slot: UInt256): Result[UInt256, string] =
|
||||||
|
try:
|
||||||
|
let res = waitFor client.eth_getStorageAt(ethAddressStr(address), encodeQuantity(slot), "latest")
|
||||||
|
return ok(UInt256.fromHex(res.string))
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
||||||
|
|
||||||
|
proc storageAt*(client: RpcClient, address: EthAddress, slot: UInt256, number: common.BlockNumber): Result[UInt256, string] =
|
||||||
|
try:
|
||||||
|
let tag = encodeQuantity(number)
|
||||||
|
let res = waitFor client.eth_getStorageAt(ethAddressStr(address), encodeQuantity(slot), tag.string)
|
||||||
|
return ok(UInt256.fromHex(res.string))
|
||||||
|
except ValueError as e:
|
||||||
|
return err(e.msg)
|
|
@ -0,0 +1,19 @@
|
||||||
|
import
|
||||||
|
test_env,
|
||||||
|
engine_tests,
|
||||||
|
chronos,
|
||||||
|
unittest2
|
||||||
|
|
||||||
|
proc runTest(x: TestSpec, testStatusIMPL: var TestStatus) =
|
||||||
|
var t = setupELClient()
|
||||||
|
t.setRealTTD(x.ttd)
|
||||||
|
x.run(t, testStatusIMPL)
|
||||||
|
t.stopELClient()
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
suite "Engine Tests":
|
||||||
|
for x in engineTestList:
|
||||||
|
test x.name:
|
||||||
|
runTest(x, testStatusIMPL)
|
||||||
|
|
||||||
|
main()
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"chainId": 7,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689",
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"istanbulBlock": 0,
|
||||||
|
"muirGlacierBlock": 0,
|
||||||
|
"berlinBlock": 0,
|
||||||
|
"londonBlock": 0,
|
||||||
|
"clique": {
|
||||||
|
"epoch": 3000,
|
||||||
|
"period": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty": "0x30000",
|
||||||
|
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"gasLimit": "0x2fefd8",
|
||||||
|
"nonce": "0x0000000000000000",
|
||||||
|
"timestamp": "0x1234",
|
||||||
|
"alloc": {
|
||||||
|
"cf49fda3be353c69b41ed96333cd24302da4556f": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"0161e041aad467a890839d5b08b138c1e6373072": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"87da6a8c6e9eff15d703fc2773e32f6af8dbe301": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"b97de4b8c857e4f6bc354f226dc3249aaee49209": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"c5065c9eeebe6df2c2284d046bfc906501846c51": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000314": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234",
|
||||||
|
"0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000315": {
|
||||||
|
"balance": "0x9999999999999999999999999999999",
|
||||||
|
"code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000316": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x444355"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000317": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x600160003555"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
import
|
||||||
|
std/typetraits,
|
||||||
|
test_env,
|
||||||
|
eth/rlp
|
||||||
|
|
||||||
|
type
|
||||||
|
ExecutableData* = object
|
||||||
|
parentHash* : Hash256
|
||||||
|
feeRecipient* : EthAddress
|
||||||
|
stateRoot* : Hash256
|
||||||
|
receiptsRoot* : Hash256
|
||||||
|
logsBloom* : BloomFilter
|
||||||
|
prevRandao* : Hash256
|
||||||
|
number* : uint64
|
||||||
|
gasLimit* : GasInt
|
||||||
|
gasUsed* : GasInt
|
||||||
|
timestamp* : EthTime
|
||||||
|
extraData* : Blob
|
||||||
|
baseFeePerGas*: UInt256
|
||||||
|
blockHash* : Hash256
|
||||||
|
transactions* : seq[Transaction]
|
||||||
|
|
||||||
|
CustomPayload* = object
|
||||||
|
parentHash* : Option[Hash256]
|
||||||
|
feeRecipient* : Option[EthAddress]
|
||||||
|
stateRoot* : Option[Hash256]
|
||||||
|
receiptsRoot* : Option[Hash256]
|
||||||
|
logsBloom* : Option[BloomFilter]
|
||||||
|
prevRandao* : Option[Hash256]
|
||||||
|
number* : Option[uint64]
|
||||||
|
gasLimit* : Option[GasInt]
|
||||||
|
gasUsed* : Option[GasInt]
|
||||||
|
timestamp* : Option[EthTime]
|
||||||
|
extraData* : Option[Blob]
|
||||||
|
baseFeePerGas*: Option[UInt256]
|
||||||
|
blockHash* : Option[Hash256]
|
||||||
|
transactions* : Option[seq[Transaction]]
|
||||||
|
|
||||||
|
proc customizePayload*(basePayload: ExecutableData, customData: CustomPayload): ExecutionPayloadV1 =
|
||||||
|
let txs = if customData.transactions.isSome:
|
||||||
|
customData.transactions.get
|
||||||
|
else:
|
||||||
|
basePayload.transactions
|
||||||
|
|
||||||
|
let txRoot = calcTxRoot(txs)
|
||||||
|
|
||||||
|
var customHeader = EthBlockHeader(
|
||||||
|
parentHash: basePayload.parentHash,
|
||||||
|
ommersHash: EMPTY_UNCLE_HASH,
|
||||||
|
coinbase: basePayload.feeRecipient,
|
||||||
|
stateRoot: basePayload.stateRoot,
|
||||||
|
txRoot: txRoot,
|
||||||
|
receiptRoot: basePayload.receiptsRoot,
|
||||||
|
bloom: basePayload.logsBloom,
|
||||||
|
difficulty: 0.u256,
|
||||||
|
blockNumber: basePayload.number.toBlockNumber,
|
||||||
|
gasLimit: basePayload.gasLimit,
|
||||||
|
gasUsed: basePayload.gasUsed,
|
||||||
|
timestamp: basePayload.timestamp,
|
||||||
|
extraData: basePayload.extraData,
|
||||||
|
mixDigest: basePayload.prevRandao,
|
||||||
|
nonce: default(BlockNonce),
|
||||||
|
fee: some(basePayload.baseFeePerGas)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Overwrite custom information
|
||||||
|
if customData.parentHash.isSome:
|
||||||
|
customHeader.parentHash = customData.parentHash.get
|
||||||
|
|
||||||
|
if customData.feeRecipient.isSome:
|
||||||
|
customHeader.coinbase = customData.feeRecipient.get
|
||||||
|
|
||||||
|
if customData.stateRoot.isSome:
|
||||||
|
customHeader.stateRoot = customData.stateRoot.get
|
||||||
|
|
||||||
|
if customData.receiptsRoot.isSome:
|
||||||
|
customHeader.receiptRoot = customData.receiptsRoot.get
|
||||||
|
|
||||||
|
if customData.logsBloom.isSome:
|
||||||
|
customHeader.bloom = customData.logsBloom.get
|
||||||
|
|
||||||
|
if customData.prevRandao.isSome:
|
||||||
|
customHeader.mixDigest = customData.prevRandao.get
|
||||||
|
|
||||||
|
if customData.number.isSome:
|
||||||
|
customHeader.blockNumber = toBlockNumber(customData.number.get)
|
||||||
|
|
||||||
|
if customData.gasLimit.isSome:
|
||||||
|
customHeader.gasLimit = customData.gasLimit.get
|
||||||
|
|
||||||
|
if customData.gasUsed.isSome:
|
||||||
|
customHeader.gasUsed = customData.gasUsed.get
|
||||||
|
|
||||||
|
if customData.timestamp.isSome:
|
||||||
|
customHeader.timestamp = customData.timestamp.get
|
||||||
|
|
||||||
|
if customData.extraData.isSome:
|
||||||
|
customHeader.extraData = customData.extraData.get
|
||||||
|
|
||||||
|
if customData.baseFeePerGas.isSome:
|
||||||
|
customHeader.baseFee = customData.baseFeePerGas.get
|
||||||
|
|
||||||
|
# Return the new payload
|
||||||
|
result = ExecutionPayloadV1(
|
||||||
|
parentHash: Web3BlockHash customHeader.parentHash.data,
|
||||||
|
feeRecipient: Web3Address customHeader.coinbase,
|
||||||
|
stateRoot: Web3BlockHash customHeader.stateRoot.data,
|
||||||
|
receiptsRoot: Web3BlockHash customHeader.receiptRoot.data,
|
||||||
|
logsBloom: Web3Bloom customHeader.bloom,
|
||||||
|
prevRandao: Web3PrevRandao customHeader.mixDigest.data,
|
||||||
|
blockNumber: Web3Quantity customHeader.blockNumber.truncate(uint64),
|
||||||
|
gasLimit: Web3Quantity customHeader.gasLimit,
|
||||||
|
gasUsed: Web3Quantity customHeader.gasUsed,
|
||||||
|
timestamp: Web3Quantity toUnix(customHeader.timestamp),
|
||||||
|
extraData: Web3ExtraData customHeader.extraData,
|
||||||
|
baseFeePerGas: customHeader.baseFee,
|
||||||
|
blockHash: Web3BlockHash customHeader.blockHash.data
|
||||||
|
)
|
||||||
|
|
||||||
|
for tx in txs:
|
||||||
|
let txData = rlp.encode(tx)
|
||||||
|
result.transactions.add TypedTransaction(txData)
|
||||||
|
|
||||||
|
proc hash256*(h: Web3BlockHash): Hash256 =
|
||||||
|
Hash256(data: distinctBase h)
|
||||||
|
|
||||||
|
proc toExecutableData*(payload: ExecutionPayloadV1): ExecutableData =
|
||||||
|
result = ExecutableData(
|
||||||
|
parentHash : hash256(payload.parentHash),
|
||||||
|
feeRecipient : distinctBase payload.feeRecipient,
|
||||||
|
stateRoot : hash256(payload.stateRoot),
|
||||||
|
receiptsRoot : hash256(payload.receiptsRoot),
|
||||||
|
logsBloom : distinctBase payload.logsBloom,
|
||||||
|
prevRandao : hash256(payload.prevRandao),
|
||||||
|
number : uint64 payload.blockNumber,
|
||||||
|
gasLimit : GasInt payload.gasLimit,
|
||||||
|
gasUsed : GasInt payload.gasUsed,
|
||||||
|
timestamp : fromUnix(int64 payload.timestamp),
|
||||||
|
extraData : distinctBase payload.extraData,
|
||||||
|
baseFeePerGas : payload.baseFeePerGas,
|
||||||
|
blockHash : hash256(payload.blockHash)
|
||||||
|
)
|
||||||
|
|
||||||
|
for data in payload.transactions:
|
||||||
|
let tx = rlp.decode(distinctBase data, Transaction)
|
||||||
|
result.transactions.add tx
|
|
@ -0,0 +1 @@
|
||||||
|
9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c
|
|
@ -0,0 +1,151 @@
|
||||||
|
import
|
||||||
|
std/[os, options, json, times, math],
|
||||||
|
eth/[common, keys],
|
||||||
|
eth/trie/db,
|
||||||
|
eth/p2p as eth_p2p,
|
||||||
|
stew/[results, byteutils],
|
||||||
|
stint,
|
||||||
|
json_rpc/[rpcserver, rpcclient],
|
||||||
|
../../../nimbus/[
|
||||||
|
config,
|
||||||
|
genesis,
|
||||||
|
context,
|
||||||
|
constants,
|
||||||
|
transaction,
|
||||||
|
utils,
|
||||||
|
sealer,
|
||||||
|
p2p/chain,
|
||||||
|
db/db_chain,
|
||||||
|
rpc/p2p,
|
||||||
|
rpc/engine_api,
|
||||||
|
sync/protocol_ethxx,
|
||||||
|
utils/tx_pool
|
||||||
|
],
|
||||||
|
../../../tests/test_helpers,
|
||||||
|
"."/[clmock, engine_client]
|
||||||
|
|
||||||
|
import web3/engine_api_types
|
||||||
|
from web3/ethtypes as web3types import nil
|
||||||
|
|
||||||
|
export
|
||||||
|
common, engine_api_types, times,
|
||||||
|
options, results, constants, utils,
|
||||||
|
TypedTransaction, clmock, engine_client
|
||||||
|
|
||||||
|
type
|
||||||
|
EthBlockHeader* = common.BlockHeader
|
||||||
|
|
||||||
|
TestEnv* = ref object
|
||||||
|
conf: NimbusConf
|
||||||
|
ctx: EthContext
|
||||||
|
ethNode: EthereumNode
|
||||||
|
chainDB: BaseChainDB
|
||||||
|
chainRef: Chain
|
||||||
|
rpcServer: RpcSocketServer
|
||||||
|
sealingEngine: SealingEngineRef
|
||||||
|
rpcClient*: RpcSocketClient
|
||||||
|
gHeader*: EthBlockHeader
|
||||||
|
ttd*: DifficultyInt
|
||||||
|
clMock*: CLMocker
|
||||||
|
nonce: uint64
|
||||||
|
vaultKey: PrivateKey
|
||||||
|
|
||||||
|
Web3BlockHash* = web3types.BlockHash
|
||||||
|
Web3Address* = web3types.Address
|
||||||
|
Web3Bloom* = web3types.FixedBytes[256]
|
||||||
|
Web3Quantity* = web3types.Quantity
|
||||||
|
Web3PrevRandao* = web3types.FixedBytes[32]
|
||||||
|
Web3ExtraData* = web3types.DynamicBytes[0, 32]
|
||||||
|
|
||||||
|
const
|
||||||
|
baseFolder = "hive_integration" / "nodocker" / "engine"
|
||||||
|
genesisFile = baseFolder / "genesis.json"
|
||||||
|
sealerKey = baseFolder / "sealer.key"
|
||||||
|
|
||||||
|
# This is the account that sends vault funding transactions.
|
||||||
|
vaultAccountAddr* = hexToByteArray[20]("0xcf49fda3be353c69b41ed96333cd24302da4556f")
|
||||||
|
vaultKeyHex = "63b508a03c3b5937ceb903af8b1b0c191012ef6eb7e9c3fb7afa94e5d214d376"
|
||||||
|
|
||||||
|
proc setupELClient*(t: TestEnv) =
|
||||||
|
t.ctx = newEthContext()
|
||||||
|
let res = t.ctx.am.importPrivateKey(sealerKey)
|
||||||
|
if res.isErr:
|
||||||
|
echo res.error()
|
||||||
|
quit(QuitFailure)
|
||||||
|
|
||||||
|
t.ethNode = setupEthNode(t.conf, t.ctx, eth)
|
||||||
|
t.chainDB = newBaseChainDB(
|
||||||
|
newMemoryDb(),
|
||||||
|
t.conf.pruneMode == PruneMode.Full,
|
||||||
|
t.conf.networkId,
|
||||||
|
t.conf.networkParams
|
||||||
|
)
|
||||||
|
t.chainRef = newChain(t.chainDB)
|
||||||
|
|
||||||
|
initializeEmptyDb(t.chainDB)
|
||||||
|
let txPool = TxPoolRef.new(t.chainDB, t.conf.engineSigner)
|
||||||
|
|
||||||
|
t.rpcServer = newRpcSocketServer(["localhost:" & $t.conf.rpcPort])
|
||||||
|
t.sealingEngine = SealingEngineRef.new(
|
||||||
|
t.chainRef, t.ctx, t.conf.engineSigner,
|
||||||
|
txPool, EngineStopped
|
||||||
|
)
|
||||||
|
|
||||||
|
setupEthRpc(t.ethNode, t.ctx, t.chainDB, txPool, t.rpcServer)
|
||||||
|
setupEngineAPI(t.sealingEngine, t.rpcServer)
|
||||||
|
|
||||||
|
t.sealingEngine.start()
|
||||||
|
t.rpcServer.start()
|
||||||
|
|
||||||
|
t.rpcClient = newRpcSocketClient()
|
||||||
|
waitFor t.rpcClient.connect("localhost", t.conf.rpcPort)
|
||||||
|
t.gHeader = toGenesisHeader(t.conf.networkParams)
|
||||||
|
|
||||||
|
let kRes = PrivateKey.fromHex(vaultKeyHex)
|
||||||
|
if kRes.isErr:
|
||||||
|
echo kRes.error
|
||||||
|
quit(QuitFailure)
|
||||||
|
|
||||||
|
t.vaultKey = kRes.get
|
||||||
|
|
||||||
|
proc setupELClient*(): TestEnv =
|
||||||
|
result = TestEnv(
|
||||||
|
conf: makeConfig(@["--engine-signer:658bdf435d810c91414ec09147daa6db62406379", "--custom-network:" & genesisFile])
|
||||||
|
)
|
||||||
|
setupELClient(result)
|
||||||
|
|
||||||
|
proc stopELClient*(t: TestEnv) =
|
||||||
|
waitFor t.rpcClient.close()
|
||||||
|
waitFor t.sealingEngine.stop()
|
||||||
|
t.rpcServer.stop()
|
||||||
|
waitFor t.rpcServer.closeWait()
|
||||||
|
|
||||||
|
# TTD is the value specified in the TestSpec + Genesis.Difficulty
|
||||||
|
proc setRealTTD*(t: TestEnv, ttdValue: int64) =
|
||||||
|
let realTTD = t.gHeader.difficulty + ttdValue.u256
|
||||||
|
t.chainDB.config.terminalTotalDifficulty = some(realTTD)
|
||||||
|
t.ttd = realTTD
|
||||||
|
t.clmock = newCLMocker(t.rpcClient, realTTD)
|
||||||
|
|
||||||
|
func gwei(n: int): GasInt {.compileTime.} =
|
||||||
|
GasInt(n * (10 ^ 9))
|
||||||
|
|
||||||
|
proc makeNextTransaction*(t: TestEnv, recipient: EthAddress, amount: UInt256, payload: openArray[byte] = []): Transaction =
|
||||||
|
const
|
||||||
|
gasLimit = 75000.GasInt
|
||||||
|
gasPrice = 30.gwei
|
||||||
|
|
||||||
|
let chainId = t.conf.networkParams.config.chainId
|
||||||
|
let tx = Transaction(
|
||||||
|
txType : TxLegacy,
|
||||||
|
chainId : chainId,
|
||||||
|
nonce : AccountNonce(t.nonce),
|
||||||
|
gasPrice: gasPrice,
|
||||||
|
gasLimit: gasLimit,
|
||||||
|
to : some(recipient),
|
||||||
|
value : amount,
|
||||||
|
payload : @payload
|
||||||
|
)
|
||||||
|
|
||||||
|
inc t.nonce
|
||||||
|
signTransaction(tx, t.vaultKey, chainId, eip155 = true)
|
|
@ -165,3 +165,15 @@ func eip1559TxNormalization*(tx: Transaction;
|
||||||
result.maxFee = tx.gasPrice
|
result.maxFee = tx.gasPrice
|
||||||
if FkLondon <= fork:
|
if FkLondon <= fork:
|
||||||
result.gasPrice = baseFee + min(result.maxPriorityFee, result.maxFee - baseFee)
|
result.gasPrice = baseFee + min(result.maxPriorityFee, result.maxFee - baseFee)
|
||||||
|
|
||||||
|
func effectiveGasTip*(tx: Transaction; baseFee: Option[UInt256]): GasInt =
|
||||||
|
var
|
||||||
|
maxPriorityFee = tx.maxPriorityFee
|
||||||
|
maxFee = tx.maxFee
|
||||||
|
baseFee = baseFee.get(0.u256).truncate(GasInt)
|
||||||
|
|
||||||
|
if tx.txType < TxEip1559:
|
||||||
|
maxPriorityFee = tx.gasPrice
|
||||||
|
maxFee = tx.gasPrice
|
||||||
|
|
||||||
|
min(maxPriorityFee, maxFee - baseFee)
|
||||||
|
|
|
@ -136,7 +136,7 @@ proc parseTransaction*(n: JsonNode): Transaction =
|
||||||
n.fromJson "r", tx.R
|
n.fromJson "r", tx.R
|
||||||
n.fromJson "s", tx.S
|
n.fromJson "s", tx.S
|
||||||
|
|
||||||
if n["type"].kind != JNull:
|
if n.hasKey("type") and n["type"].kind != JNull:
|
||||||
n.fromJson "type", tx.txType
|
n.fromJson "type", tx.txType
|
||||||
|
|
||||||
if tx.txType >= TxEip1559:
|
if tx.txType >= TxEip1559:
|
||||||
|
|
Loading…
Reference in New Issue