Add multiple clients support to engine api simulator

This commit is contained in:
jangko 2023-09-06 16:18:26 +07:00
parent 070b06f809
commit 6139152143
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
15 changed files with 651 additions and 479 deletions

View File

@ -23,21 +23,21 @@ proc prepareAuthCallToken(secret: string, time: int64): string =
let sig = base64urlEncode(sha256.hmac(key, token).data)
token & "." & sig
proc getClient(t: TestEnv, token: string): RpcHttpClient =
proc getClient(env: TestEnv, token: string): RpcHttpClient =
proc authHeaders(): seq[(string, string)] =
@[("Authorization", "Bearer " & token)]
let client = newRpcHttpClient(getHeaders = authHeaders)
waitFor client.connect("127.0.0.1", t.conf.rpcPort, false)
waitFor client.connect("127.0.0.1", env.engine.rpcPort, false)
return client
template genAuthTest(procName: untyped, timeDriftSeconds: int64, customAuthSecretBytes: string, authOK: bool) =
proc procName(t: TestEnv): bool =
proc procName(env: TestEnv): bool =
# Default values
var
# All test cases send a simple TransitionConfigurationV1 to check the Authentication mechanism (JWT)
tConf = TransitionConfigurationV1(
terminalTotalDifficulty: t.ttd
terminalTotalDifficulty: env.engine.ttd
)
testSecret = customAuthSecretBytes
testTime = getTime().toUnix
@ -49,13 +49,13 @@ template genAuthTest(procName: untyped, timeDriftSeconds: int64, customAuthSecre
testTime = testTime + timeDriftSeconds
let token = prepareAuthCallToken(testSecret, testTime)
let client = getClient(t, token)
let client = getClient(env, token)
try:
discard waitFor client.call("engine_exchangeTransitionConfigurationV1", %[%tConf])
testCond authOk:
error "Authentication was supposed to fail authentication but passed"
except CatchableError as ex:
except CatchableError:
testCond not authOk:
error "Authentication was supposed to pass authentication but failed"
return true
@ -70,16 +70,16 @@ genAuthTest(authTest7, maxTimeDriftSeconds - 1, "", true)
type
AuthSpec* = ref object of BaseSpec
exec*: proc(t: TestEnv): bool
exec*: proc(env: TestEnv): bool
proc specExecute(ws: BaseSpec): bool =
let
ws = AuthSpec(ws)
env = setupELClient("", true)
env = TestEnv.new("", true)
env.setRealTTD(0)
env.engine.setRealTTD(0)
result = ws.exec(env)
env.stopELClient()
env.close()
# JWT Authentication Tests
let authTestList* = [

View File

@ -0,0 +1,23 @@
import
json_rpc/rpcclient,
./engine_env
type
ClientPool* = ref object
clients: seq[EngineEnv]
proc add*(pool: ClientPool, client: EngineEnv) =
pool.clients.add client
func first*(pool: ClientPool): EngineEnv =
pool.clients[0]
func len*(pool: ClientPool): int =
pool.clients.len
func `[]`*(pool: ClientPool, idx: int): EngineEnv =
pool.clients[idx]
iterator items*(pool: ClientPool): EngineEnv =
for x in pool.clients:
yield x

View File

@ -10,6 +10,8 @@ import
../../../nimbus/beacon/payload_conv,
../../../nimbus/[constants],
../../../nimbus/common as nimbus_common,
./client_pool,
./engine_env,
./engine_client
import web3/engine_api_types except Hash256 # conflict with the one from eth/common
@ -30,7 +32,8 @@ type
blockTimestampIncrement*: Option[int]
# Block Production State
client : RpcClient
clients : ClientPool
nextBlockProducer* : EngineEnv
nextFeeRecipient* : EthAddress
nextPayloadID* : PayloadID
currentPayloadNumber* : uint64
@ -65,7 +68,7 @@ type
BlockProcessCallbacks* = object
onPayloadProducerSelected* : proc(): bool {.gcsafe.}
onGetPayloadID* : proc(): bool {.gcsafe.}
onRequestNextPayload* : proc(): bool {.gcsafe.}
onGetPayload* : proc(): bool {.gcsafe.}
onNewPayloadBroadcast* : proc(): bool {.gcsafe.}
onForkchoiceBroadcast* : proc(): bool {.gcsafe.}
@ -77,13 +80,13 @@ type
blockValue: Option[UInt256]
blobsBundle: Option[BlobsBundleV1]
func latestPayloadNumber(h: Table[uint64, ExecutionPayload]): uint64 =
func latestPayloadNumber*(h: Table[uint64, ExecutionPayload]): uint64 =
result = 0'u64
for n, _ in h:
if n > result:
result = n
func latestWithdrawalsIndex(h: Table[uint64, ExecutionPayload]): uint64 =
func latestWithdrawalsIndex*(h: Table[uint64, ExecutionPayload]): uint64 =
result = 0'u64
for n, p in h:
if p.withdrawals.isNone:
@ -93,17 +96,20 @@ func latestWithdrawalsIndex(h: Table[uint64, ExecutionPayload]): uint64 =
if w.index.uint64 > result:
result = w.index.uint64
proc init*(cl: CLMocker, client: RpcClient, com: CommonRef) =
cl.client = client
func client(cl: CLMocker): RpcClient =
cl.clients.first.client
proc init(cl: CLMocker, clients: ClientPool, com: CommonRef) =
cl.clients = clients
cl.com = com
cl.slotsToSafe = 1
cl.slotsToFinalized = 2
cl.payloadProductionClientDelay = 1
cl.headerHistory[0] = com.genesisHeader()
proc newClMocker*(client: RpcClient, com: CommonRef): CLMocker =
proc newClMocker*(clients: ClientPool, com: CommonRef): CLMocker =
new result
result.init(client, com)
result.init(clients, com)
proc waitForTTD*(cl: CLMocker): Future[bool] {.async.} =
let ttd = cl.com.ttd()
@ -179,32 +185,6 @@ func timestampToBeaconRoot(timestamp: Quantity): FixedBytes[32] =
let h = keccakHash(timestamp.uint64.toBytesBE)
FixedBytes[32](h.data)
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.latestHeadNumber != lastBlockNumber:
error "CLMocker: unexpected lastBlockNumber",
get = lastBlockNumber,
expect = cl.latestHeadNumber
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.latestHeader.blockHash != lastBlockHash:
error "CLMocker: Failed to obtain a client on the latest block number"
return false
return true
func isShanghai(cl: CLMocker, timestamp: Quantity): bool =
let ts = fromUnix(timestamp.int64)
cl.com.isShanghaiOrLater(ts)
@ -218,26 +198,58 @@ func V1(attr: Option[PayloadAttributes]): Option[PayloadAttributesV1] =
return none(PayloadAttributesV1)
some(attr.get.V1)
func V2(attr: Option[PayloadAttributes]): Option[PayloadAttributesV2] =
if attr.isNone:
return none(PayloadAttributesV2)
some(attr.get.V2)
when false:
func V2(attr: Option[PayloadAttributes]): Option[PayloadAttributesV2] =
if attr.isNone:
return none(PayloadAttributesV2)
some(attr.get.V2)
func V3(attr: Option[PayloadAttributes]): Option[PayloadAttributesV3] =
if attr.isNone:
return none(PayloadAttributesV3)
some(attr.get.V3)
func V3(attr: Option[PayloadAttributes]): Option[PayloadAttributesV3] =
if attr.isNone:
return none(PayloadAttributesV3)
some(attr.get.V3)
proc fcu(cl: CLMocker, version: Version,
update: ForkchoiceStateV1,
attr: Option[PayloadAttributes]):
Result[ForkchoiceUpdatedResponse, string] =
let client = cl.nextBlockProducer.client
case version
of Version.V1: cl.client.forkchoiceUpdatedV1(update, attr.V1)
of Version.V2: cl.client.forkchoiceUpdatedV2(update, attr)
of Version.V3: cl.client.forkchoiceUpdatedV3(update, attr)
of Version.V1: client.forkchoiceUpdatedV1(update, attr.V1)
of Version.V2: client.forkchoiceUpdatedV2(update, attr)
of Version.V3: client.forkchoiceUpdatedV3(update, attr)
proc getNextPayloadID*(cl: CLMocker): bool =
# Picks the next payload producer from the set of clients registered
proc pickNextPayloadProducer(cl: CLMocker): bool =
doAssert cl.clients.len != 0
for i in 0 ..< cl.clients.len:
# Get a client to generate the payload
let id = (cl.latestHeadNumber.int + i) mod cl.clients.len
cl.nextBlockProducer = cl.clients[id]
# Get latest header. Number and hash must coincide with our view of the chain,
# and only then we can build on top of this client's chain
var latestHeader: common.BlockHeader
let res = cl.nextBlockProducer.client.latestHeader(latestHeader)
if res.isErr:
error "CLMocker: Could not get latest block header while selecting client for payload production",
msg=res.error
return false
let lastBlockHash = latestHeader.blockHash
if cl.latestHeader.blockHash != lastBlockHash or
cl.latestHeadNumber != latestHeader.blockNumber.truncate(uint64):
# Selected client latest block hash does not match canonical chain, try again
cl.nextBlockProducer = nil
continue
else:
break
doAssert cl.nextBlockProducer != nil
return true
proc requestNextPayload(cl: CLMocker): bool =
# Generate a random value for the PrevRandao field
var nextPrevRandao: common.Hash256
doAssert randomBytes(nextPrevRandao.data) == 32
@ -271,11 +283,13 @@ proc getNextPayloadID*(cl: CLMocker): bool =
if s.payloadStatus.status != PayloadExecutionStatus.valid:
error "CLMocker: Unexpected forkchoiceUpdated Response from Payload builder",
status=s.payloadStatus.status
return false
if s.payloadStatus.latestValidHash.isNone or s.payloadStatus.latestValidHash.get != cl.latestForkchoice.headBlockHash:
error "CLMocker: Unexpected forkchoiceUpdated LatestValidHash Response from Payload builder",
latest=s.payloadStatus.latestValidHash,
head=cl.latestForkchoice.headBlockHash
return false
doAssert s.payLoadID.isSome
cl.nextPayloadID = s.payloadID.get()
@ -283,8 +297,9 @@ proc getNextPayloadID*(cl: CLMocker): bool =
proc getPayload(cl: CLMocker, payloadId: PayloadID): Result[GetPayloadResponse, string] =
let ts = cl.latestPayloadAttributes.timestamp
let client = cl.nextBlockProducer.client
if cl.isCancun(ts):
let res = cl.client.getPayloadV3(payloadId)
let res = client.getPayloadV3(payloadId)
if res.isErr:
return err(res.error)
let x = res.get
@ -295,7 +310,7 @@ proc getPayload(cl: CLMocker, payloadId: PayloadID): Result[GetPayloadResponse,
))
if cl.isShanghai(ts):
let res = cl.client.getPayloadV2(payloadId)
let res = client.getPayloadV2(payloadId)
if res.isErr:
return err(res.error)
let x = res.get
@ -304,14 +319,14 @@ proc getPayload(cl: CLMocker, payloadId: PayloadID): Result[GetPayloadResponse,
blockValue: some(x.blockValue)
))
let res = cl.client.getPayloadV1(payloadId)
let res = client.getPayloadV1(payloadId)
if res.isErr:
return err(res.error)
return ok(GetPayloadResponse(
executionPayload: executionPayload(res.get),
))
proc getNextPayload*(cl: CLMocker): bool =
proc getNextPayload(cl: CLMocker): bool =
let res = cl.getPayload(cl.nextPayloadID)
if res.isErr:
error "CLMocker: Could not getPayload",
@ -364,17 +379,17 @@ proc getNextPayload*(cl: CLMocker): bool =
return true
func versionedHashes(bb: BlobsBundleV1): seq[BlockHash] =
func versionedHashes(bb: BlobsBundleV1): seq[Web3Hash] =
doAssert(bb.commitments.len > 0)
result = newSeqOfCap[BlockHash](bb.commitments.len)
for com in bb.commitments:
var h = keccakHash(com.bytes)
h.data[0] = VERSIONED_HASH_VERSION_KZG
result.add BlockHash(h.data)
result.add w3Hash h
proc broadcastNewPayload(cl: CLMocker, payload: ExecutionPayload): Result[PayloadStatusV1, string] =
var versionedHashes: seq[BlockHash]
var versionedHashes: seq[Web3Hash]
if cl.latestBlobsBundle.isSome:
# Broadcast the blob bundle to all clients
versionedHashes = versionedHashes(cl.latestBlobsBundle.get)
@ -433,7 +448,7 @@ proc broadcastNextNewPayload(cl: CLMocker): bool =
cl.executedPayloadHistory[number] = cl.latestPayloadBuilt
return true
proc broadcastForkchoiceUpdated*(cl: CLMocker,
proc broadcastForkchoiceUpdated(cl: CLMocker,
update: ForkchoiceStateV1): Result[ForkchoiceUpdatedResponse, string] =
let version = cl.latestExecutedPayload.version
cl.fcu(version, update, none(PayloadAttributes))
@ -483,13 +498,13 @@ proc produceSingleBlock*(cl: CLMocker, cb: BlockProcessCallbacks): bool {.gcsafe
if not cb.onPayloadProducerSelected():
return false
if not cl.getNextPayloadID():
if not cl.requestNextPayload():
return false
cl.setNextWithdrawals(none(seq[WithdrawalV1]))
if cb.onGetPayloadID != nil:
if not cb.onGetPayloadID():
if cb.onRequestNextPayload != nil:
if not cb.onRequestNextPayload():
return false
# Give the client a delay between getting the payload ID and actually retrieving the payload

View File

@ -2,10 +2,10 @@ import
std/tables,
stew/byteutils,
chronicles,
eth/common,
nimcrypto/sysrand,
chronos,
".."/[test_env, helper, types],
../../../nimbus/common,
../../../nimbus/transaction,
../../../nimbus/rpc/rpc_types,
../../../nimbus/beacon/web3_eth_conv,
@ -13,7 +13,7 @@ import
type
EngineSpec* = ref object of BaseSpec
exec*: proc(t: TestEnv): bool
exec*: proc(env: TestEnv): bool
ttd*: int64
chainFile*: string
slotsToFinalized*: int
@ -53,7 +53,7 @@ template testLatestHeader(client: untyped, expectedHash: Web3Hash) =
#proc sendTx(t: TestEnv, recipient: EthAddress, val: UInt256, data: openArray[byte] = []): bool =
# t.tx = t.makeTx(recipient, val, data)
# let rr = t.rpcClient.sendTransaction(t.tx)
# let rr = env.client.sendTransaction(t.tx)
# if rr.isErr:
# error "Unable to send transaction", msg=rr.error
# return false
@ -64,16 +64,16 @@ template testLatestHeader(client: untyped, expectedHash: Web3Hash) =
# Invalid Terminal Block in ForkchoiceUpdated:
# Client must reject ForkchoiceUpdated directives if the referenced HeadBlockHash does not meet the TTD requirement.
proc invalidTerminalBlockForkchoiceUpdated*(t: TestEnv): bool =
proc invalidTerminalBlockForkchoiceUpdated*(env: TestEnv): bool =
let
gHash = w3Hash t.gHeader.blockHash
gHash = w3Hash env.engine.com.genesisHash
forkchoiceState = ForkchoiceStateV1(
headBlockHash: gHash,
safeBlockHash: gHash,
finalizedBlockHash: gHash,
)
let res = t.rpcClient.forkchoiceUpdatedV1(forkchoiceState)
let res = env.client.forkchoiceUpdatedV1(forkchoiceState)
# Execution specification:
# {payloadStatus: {status: INVALID, latestValidHash=0x00..00}, payloadId: null}
# either obtained from the Payload validation process or as a result of
@ -83,7 +83,7 @@ proc invalidTerminalBlockForkchoiceUpdated*(t: TestEnv): bool =
# ValidationError is not validated since it can be either null or a string message
# Check that PoW chain progresses
testCond t.verifyPoWProgress(t.gHeader.blockHash)
testCond env.verifyPoWProgress(env.engine.com.genesisHash)
return true
#[
@ -93,7 +93,7 @@ proc invalidGetPayloadUnderPoW(t: TestEnv): TestStatus =
# We start in PoW and try to get an invalid Payload, which should produce an error but nothing should be disrupted.
let id = PayloadID [1.byte, 2,3,4,5,6,7,8]
let res = t.rpcClient.getPayloadV1(id)
let res = env.client.getPayloadV1(id)
testCond res.isErr
# Check that PoW chain progresses
@ -116,7 +116,7 @@ proc invalidTerminalBlockNewPayload(t: TestEnv): TestStatus =
baseFeePerGas:gBlock.baseFee
)
let hashedPayload = customizePayload(payload, CustomPayload())
let res = t.rpcClient.newPayloadV1(hashedPayload)
let res = env.client.newPayloadV1(hashedPayload)
# Execution specification:
# {status: INVALID, latestValidHash=0x00..00}
@ -142,7 +142,7 @@ proc unknownHeadBlockHash(t: TestEnv): TestStatus =
finalizedBlockHash: clMock.latestForkchoice.finalizedBlockHash,
)
var res = t.rpcClient.forkchoiceUpdatedV1(forkchoiceStateUnknownHeadHash)
var res = env.client.forkchoiceUpdatedV1(forkchoiceStateUnknownHeadHash)
testCond res.isOk
let s = res.get()
@ -158,7 +158,7 @@ proc unknownHeadBlockHash(t: TestEnv): TestStatus =
timestamp: Quantity(timestamp + 1)
)
res = t.rpcClient.forkchoiceUpdatedV1(forkchoiceStateUnknownHeadHash, some(payloadAttr))
res = env.client.forkchoiceUpdatedV1(forkchoiceStateUnknownHeadHash, some(payloadAttr))
testCond res.isOk
testCond s.payloadStatus.status == PayloadExecutionStatus.syncing
testCond s.payloadId.isNone
@ -174,7 +174,7 @@ proc unknownSafeBlockHash(t: TestEnv): TestStatus =
testCond produce5BlockRes
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
let produceSingleBlockRes = clMock.produceSingleBlock(BlockProcessCallbacks(
# Run test after a new payload has been broadcast
onNewPayloadBroadcast: proc(): bool =
@ -207,7 +207,7 @@ proc unknownFinalizedBlockHash(t: TestEnv): TestStatus =
testCond produce5BlockRes
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
let produceSingleBlockRes = clMock.produceSingleBlock(BlockProcessCallbacks(
# Run test after a new payload has been broadcast
onNewPayloadBroadcast: proc(): bool =
@ -259,7 +259,7 @@ template inconsistentForkchoiceStateGen(procname: untyped, inconsistency: Incons
var pList = PayloadList()
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
# Produce blocks before starting the test
let produceBlockRes = clMock.produceBlocks(3, BlockProcessCallbacks(
@ -328,7 +328,7 @@ template invalidPayloadAttributesGen(procname: untyped, syncingCond: bool) =
testCond ok
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
# Produce blocks before starting the test
var produceBlockRes = clMock.produceBlocks(5, BlockProcessCallbacks())
@ -395,7 +395,7 @@ proc preTTDFinalizedBlockHash(t: TestEnv): TestStatus =
safeBlockHash: gHash,
finalizedBlockHash: gHash,
)
client = t.rpcClient
client = env.client
clMock = t.clMock
var res = client.forkchoiceUpdatedV1(forkchoiceState)
@ -446,7 +446,7 @@ template badHashOnNewPayloadGen(procname: untyped, syncingCond: bool, sideChain:
testCond produce5BlockRes
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
let shadow = Shadow()
var produceSingleBlockRes = clMock.produceSingleBlock(BlockProcessCallbacks(
@ -542,7 +542,7 @@ proc parentHashOnExecPayload(t: TestEnv): TestStatus =
testCond produce5BlockRes
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
var produceSingleBlockRes = clMock.produceSingleBlock(BlockProcessCallbacks(
# Run test after the new payload has been obtained
onGetPayload: proc(): bool =
@ -568,7 +568,7 @@ proc invalidTransitionPayload(t: TestEnv): TestStatus =
testCond ok
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
# Produce two blocks before trying to re-org
t.nonce = 2 # Initial PoW chain already contains 2 transactions
@ -611,7 +611,7 @@ template invalidPayloadTestCaseGen(procName: untyped, payloadField: InvalidPaylo
testCond ok
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
template txProc(): bool =
when not emptyTxs:
@ -776,7 +776,7 @@ template blockStatusExecPayloadGen(procname: untyped, transitionBlock: bool) =
testCond produce5BlockRes
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
let shadow = Shadow()
var produceSingleBlockRes = clMock.produceSingleBlock(BlockProcessCallbacks(
@ -836,7 +836,7 @@ template invalidMissingAncestorReOrgGen(procName: untyped,
testCond ok
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
# Produce blocks before starting the test
testCond clMock.produceBlocks(5, BlockProcessCallbacks())
@ -955,7 +955,7 @@ template blockStatusHeadBlockGen(procname: untyped, transitionBlock: bool) =
testCond produce5BlockRes
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
let shadow = Shadow()
var produceSingleBlockRes = clMock.produceSingleBlock(BlockProcessCallbacks(
@ -985,7 +985,7 @@ proc blockStatusSafeBlock(t: TestEnv): TestStatus =
result = TestStatus.OK
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
# On PoW mode, `safe` tag shall return error.
var header: common.BlockHeader
@ -1016,7 +1016,7 @@ proc blockStatusFinalizedBlock(t: TestEnv): TestStatus =
result = TestStatus.OK
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
# On PoW mode, `finalized` tag shall return error.
var header: common.BlockHeader
@ -1055,7 +1055,7 @@ proc blockStatusReorg(t: TestEnv): TestStatus =
testCond produce5BlockRes
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
var produceSingleBlockRes = clMock.produceSingleBlock(BlockProcessCallbacks(
# Run test after a forkchoice with new HeadBlockHash has been broadcasted
onForkchoiceBroadcast: proc(): bool =
@ -1145,7 +1145,7 @@ proc reExecPayloads(t: TestEnv): TestStatus =
testCond produceBlockRes
# Re-execute the payloads
let client = t.rpcClient
let client = env.client
var hRes = client.blockNumber()
testCond hRes.isOk:
error "unable to get blockNumber", msg=hRes.error
@ -1183,7 +1183,7 @@ proc multipleNewCanonicalPayloads(t: TestEnv): TestStatus =
testCond produce5BlockRes
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
var produceSingleBlockRes = clMock.produceSingleBlock(BlockProcessCallbacks(
# Run test after a new payload has been obtained
onGetPayload: proc(): bool =
@ -1233,7 +1233,7 @@ proc outOfOrderPayloads(t: TestEnv): TestStatus =
doAssert randomBytes(recipient) == 20
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
var produceBlockRes = clMock.produceBlocks(payloadCount, BlockProcessCallbacks(
# We send the transactions after we got the Payload ID, before the clMocker gets the prepared Payload
onPayloadProducerSelected: proc(): bool =
@ -1265,7 +1265,7 @@ proc reorgBack(t: TestEnv): TestStatus =
testCond ok
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
testCond clMock.produceSingleBlock(BlockProcessCallbacks())
@ -1308,7 +1308,7 @@ proc reorgBackFromSyncing(t: TestEnv): TestStatus =
# Produce an alternative chain
let pList = SideChainList()
let clMock = t.clMock
let client = t.rpcClient
let client = env.client
let r1 = clMock.produceBlocks(10, BlockProcessCallbacks(
onGetPayload: proc(): bool =
@ -1387,7 +1387,7 @@ proc transactionReorg(t: TestEnv): TestStatus =
contractAddr = hexToByteArray[20]("0000000000000000000000000000000000000317")
let
client = t.rpcClient
client = env.client
clMock = t.clMock
shadow = TxReorgShadow()
@ -1470,7 +1470,7 @@ proc transactionReorg(t: TestEnv): TestStatus =
proc testCondPrevRandaoValue(t: TestEnv, expectedPrevRandao: common.Hash256, blockNumber: uint64): bool =
let storageKey = blockNumber.u256
let client = t.rpcClient
let client = env.client
let res = client.storageAt(prevRandaoContractAddr, storageKey)
if res.isErr:
@ -1496,7 +1496,7 @@ proc sidechainReorg(t: TestEnv): TestStatus =
testCond t.clMock.produceBlocks(5, BlockProcessCallbacks())
let
client = t.rpcClient
client = env.client
clMock = t.clMock
# Produce two payloads, send fcU with first payload, testCond transaction outcome, then reorg, testCond transaction outcome again
@ -1591,7 +1591,7 @@ proc suggestedFeeRecipient(t: TestEnv): TestStatus =
testCond randomBytes(feeRecipient) == 20
let
client = t.rpcClient
client = env.client
clMock = t.clMock
# Send multiple transactions
@ -1662,7 +1662,7 @@ proc prevRandaoOpcodeTx(t: TestEnv): TestStatus =
result = TestStatus.OK
let
client = t.rpcClient
client = env.client
clMock = t.clMock
sendTxFuture = sendTxAsync(t)

View File

@ -11,6 +11,8 @@ import
import web3/engine_api as web3_engine_api
export execution_types
type
Hash256 = eth_types.Hash256
VersionedHash = engine_api_types.VersionedHash

View File

@ -0,0 +1,253 @@
import
std/[os, math],
eth/keys,
eth/p2p as eth_p2p,
json_rpc/[rpcserver, rpcclient],
stew/[results, byteutils],
../../../nimbus/[
config,
constants,
transaction,
core/sealer,
core/chain,
core/tx_pool,
core/block_import,
rpc,
sync/protocol,
beacon/beacon_engine,
common
],
../../../tests/test_helpers,
./engine_client
export
results
type
BaseTx* = object of RootObj
recipient*: Option[EthAddress]
gasLimit* : GasInt
amount* : UInt256
payload* : seq[byte]
txType* : Option[TxType]
BigInitcodeTx* = object of BaseTx
initcodeLength*: int
padByte* : uint8
initcode* : seq[byte]
EngineEnv* = ref object
conf : NimbusConf
com : CommonRef
server : RpcHttpServer
sealer : SealingEngineRef
ttd : DifficultyInt
tx : Transaction
nonce : uint64
client : RpcHttpClient
const
baseFolder = "hive_integration/nodocker/engine"
genesisFile = baseFolder & "/init/genesis.json"
sealerKey = baseFolder & "/init/sealer.key"
chainFolder = baseFolder & "/chains"
# This is the account that sends vault funding transactions.
vaultAddr* = hexToByteArray[20]("0xcf49fda3be353c69b41ed96333cd24302da4556f")
jwtSecret = "0x7365637265747365637265747365637265747365637265747365637265747365"
proc makeCom*(conf: NimbusConf): CommonRef =
CommonRef.new(
newCoreDbRef LegacyDbMemory,
conf.pruneMode == PruneMode.Full,
conf.networkId,
conf.networkParams
)
proc envConfig*(): NimbusConf =
makeConfig(@["--engine-signer:658bdf435d810c91414ec09147daa6db62406379", "--custom-network:" & genesisFile])
proc envConfig*(conf: ChainConfig): NimbusConf =
result = envConfig()
result.networkParams.config = conf
proc newEngineEnv*(conf: var NimbusConf, chainFile: string, enableAuth: bool): EngineEnv =
if chainFile.len > 0:
# disable clique if we are using PoW chain
conf.networkParams.config.consensusType = ConsensusType.POW
let ctx = newEthContext()
ctx.am.importPrivateKey(sealerKey).isOkOr:
echo error
quit(QuitFailure)
let
node = setupEthNode(conf, ctx, eth)
com = makeCom(conf)
chain = newChain(com)
com.initializeEmptyDb()
let txPool = TxPoolRef.new(com, conf.engineSigner)
# txPool must be informed of active head
# so it can know the latest account state
let head = com.db.getCanonicalHead()
doAssert txPool.smartHead(head)
var key: JwtSharedKey
key.fromHex(jwtSecret).isOkOr:
echo "JWT SECRET ERROR: ", error
quit(QuitFailure)
let
hooks = if enableAuth: @[httpJwtAuth(key)]
else: @[]
server = newRpcHttpServer(["127.0.0.1:" & $conf.rpcPort], hooks)
sealer = SealingEngineRef.new(
chain, ctx, conf.engineSigner,
txPool, EngineStopped)
beaconEngine = BeaconEngineRef.new(txPool, chain)
setupEthRpc(node, ctx, com, txPool, server)
setupEngineAPI(beaconEngine, server)
setupDebugRpc(com, server)
# Do not start clique sealing engine if we are using a Proof of Work chain file
if chainFile.len > 0:
if not importRlpBlock(chainFolder / chainFile, com):
quit(QuitFailure)
elif not enableAuth:
sealer.start()
server.start()
let client = newRpcHttpClient()
waitFor client.connect("127.0.0.1", conf.rpcPort, false)
EngineEnv(
conf : conf,
com : com,
server : server,
sealer : sealer,
client : client
)
proc close*(env: EngineEnv) =
waitFor env.client.close()
waitFor env.sealer.stop()
waitFor env.server.closeWait()
proc setRealTTD*(env: EngineEnv, ttdValue: int64) =
let genesis = env.com.genesisHeader
let realTTD = genesis.difficulty + ttdValue.u256
env.com.setTTD some(realTTD)
env.ttd = realTTD
func rpcPort*(env: EngineEnv): Port =
env.conf.rpcPort
func client*(env: EngineEnv): RpcHttpClient =
env.client
func ttd*(env: EngineEnv): UInt256 =
env.ttd
func com*(env: EngineEnv): CommonRef =
env.com
func gwei(n: int64): GasInt {.compileTime.} =
GasInt(n * (10 ^ 9))
proc getTxType(tc: BaseTx, nonce: uint64): TxType =
if tc.txType.isNone:
if nonce mod 2 == 0:
TxLegacy
else:
TxEIP1559
else:
tc.txType.get
proc makeTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BaseTx, nonce: AccountNonce): Transaction =
const
gasPrice = 30.gwei
gasTipPrice = 1.gwei
gasFeeCap = gasPrice
gasTipCap = gasTipPrice
let chainId = env.conf.networkParams.config.chainId
let txType = tc.getTxType(nonce)
# Build the transaction depending on the specified type
let tx = if txType == TxLegacy:
Transaction(
txType : TxLegacy,
nonce : nonce,
to : tc.recipient,
value : tc.amount,
gasLimit: tc.gasLimit,
gasPrice: gasPrice,
payload : tc.payload
)
else:
Transaction(
txType : TxEIP1559,
nonce : nonce,
gasLimit: tc.gasLimit,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
chainId : chainId
)
signTransaction(tx, vaultKey, chainId, eip155 = true)
proc makeTx*(env: EngineEnv, vaultKey: PrivateKey, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction =
if tc.payload.len == 0:
# Prepare initcode payload
if tc.initcode.len != 0:
doAssert(tc.initcode.len <= tc.initcodeLength, "invalid initcode (too big)")
tc.payload = tc.initcode
while tc.payload.len < tc.initcodeLength:
tc.payload.add tc.padByte
doAssert(tc.recipient.isNone, "invalid configuration for big contract tx creator")
env.makeTx(vaultKey, tc.BaseTx, nonce)
proc sendNextTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BaseTx): bool =
env.tx = env.makeTx(vaultKey, tc, env.nonce)
inc env.nonce
let rr = env.client.sendTransaction(env.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BaseTx, nonce: AccountNonce): bool =
env.tx = env.makeTx(vaultKey, tc, nonce)
let rr = env.client.sendTransaction(env.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BigInitcodeTx, nonce: AccountNonce): bool =
env.tx = env.makeTx(vaultKey, tc, nonce)
let rr = env.client.sendTransaction(env.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(env: EngineEnv, tx: Transaction): bool =
env.tx = tx
let rr = env.client.sendTransaction(env.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true

View File

@ -1,5 +1,6 @@
import
"."/[types, test_env],
std/times,
./types,
../sim_utils
import
@ -9,7 +10,7 @@ import
./withdrawal_tests
proc combineTests(): seq[TestDesc] =
result = @wdTestList
result.add wdTestList
result.add ecTestList
result.add authTestList
result.add engineTestList

View File

@ -6,16 +6,18 @@ import
proc specExecute(ws: BaseSpec): bool =
var
ws = EngineSpec(ws)
env = setupELClient(ws.chainFile, false)
env = TestEnv.new(ws.chainFile, false)
env.setRealTTD(ws.ttd)
env.engine.setRealTTD(ws.ttd)
env.setupCLMock()
if ws.slotsToFinalized != 0:
env.slotsToFinalized(ws.slotsToFinalized)
if ws.slotsToSafe != 0:
env.slotsToSafe(ws.slotsToSafe)
result = ws.exec(env)
env.stopELClient()
env.close()
let engineTestList* = [
# Engine API Negative Test Cases

View File

@ -1,4 +1,5 @@
import
std/[options, times],
./test_env,
./types,
chronicles,
@ -7,7 +8,7 @@ import
type
ECSpec* = ref object of BaseSpec
exec*: proc(t: TestEnv): bool
exec*: proc(env: TestEnv): bool
conf*: ChainConfig
const
@ -30,8 +31,8 @@ const
"engine_getPayloadV3",
]
proc ecImpl(t: TestEnv, minExpectedCaps: openArray[string]): bool =
let res = t.rpcClient.exchangeCapabilities(@minExpectedCaps)
proc ecImpl(env: TestEnv, minExpectedCaps: openArray[string]): bool =
let res = env.client.exchangeCapabilities(@minExpectedCaps)
testCond res.isOk:
error "Unable request capabilities", msg=res.error
@ -57,9 +58,9 @@ proc getCCCancun(timestamp: int): ChainConfig =
proc specExecute(ws: BaseSpec): bool =
let ws = ECSpec(ws)
let env = setupELClient(ws.conf)
let env = TestEnv.new(ws.conf)
result = ws.exec(env)
env.stopELClient()
env.close()
# const doesn't work with ref object
let ecTestList* = [

View File

@ -1,283 +1,140 @@
import
std/[os, times, math],
chronicles,
eth/keys,
eth/p2p as eth_p2p,
stew/[results, byteutils],
json_rpc/[rpcserver, rpcclient],
../../../nimbus/[
config,
constants,
transaction,
core/sealer,
core/chain,
core/tx_pool,
core/block_import,
rpc,
sync/protocol,
beacon/beacon_engine,
common
],
../../../tests/test_helpers,
"."/[clmock, engine_client]
json_rpc/rpcclient,
../../../nimbus/config,
../../../nimbus/common,
./clmock,
./engine_client,
./client_pool,
./engine_env
export
common, times,
results, constants,
clmock, engine_client
clmock,
engine_client,
client_pool,
engine_env
type
EthBlockHeader* = common.BlockHeader
TestEnv* = ref object
conf*: NimbusConf
ctx: EthContext
ethNode: EthereumNode
com: CommonRef
chainRef: ChainRef
rpcServer: RpcHttpServer
sealingEngine: SealingEngineRef
rpcClient*: RpcHttpClient
gHeader*: EthBlockHeader
ttd*: DifficultyInt
clMock*: CLMocker
vaultKey*: PrivateKey
tx*: Transaction
nonce*: uint64
BaseTx* = object of RootObj
recipient*: Option[EthAddress]
gasLimit* : GasInt
amount* : UInt256
payload* : seq[byte]
txType* : Option[TxType]
BigInitcodeTx* = object of BaseTx
initcodeLength*: int
padByte* : uint8
initcode* : seq[byte]
conf : NimbusConf
chainFile : string
enableAuth: bool
port : int
rpcPort : int
clients : ClientPool
clMock* : CLMocker
vaultKey : PrivateKey
const
baseFolder = "hive_integration/nodocker/engine"
genesisFile = baseFolder / "init/genesis.json"
sealerKey = baseFolder / "init/sealer.key"
chainFolder = baseFolder / "chains"
# This is the account that sends vault funding transactions.
vaultAccountAddr* = hexToByteArray[20]("0xcf49fda3be353c69b41ed96333cd24302da4556f")
vaultKeyHex = "63b508a03c3b5937ceb903af8b1b0c191012ef6eb7e9c3fb7afa94e5d214d376"
jwtSecret = "0x7365637265747365637265747365637265747365637265747365637265747365"
proc setupELClient*(t: TestEnv, chainFile: string, enableAuth: bool) =
if chainFile.len > 0:
# disable clique if we are using PoW chain
t.conf.networkParams.config.consensusType = ConsensusType.POW
proc makeEnv(conf: NimbusConf): TestEnv =
let env = TestEnv(
conf : conf,
port : 30303,
rpcPort: 8545,
clients: ClientPool(),
)
t.ctx = newEthContext()
let res = t.ctx.am.importPrivateKey(sealerKey)
if res.isErr:
echo res.error()
env.vaultKey = PrivateKey.fromHex(vaultKeyHex).valueOr:
echo error
quit(QuitFailure)
t.ethNode = setupEthNode(t.conf, t.ctx, eth)
t.com = CommonRef.new(
newCoreDbRef LegacyDbMemory,
t.conf.pruneMode == PruneMode.Full,
t.conf.networkId,
t.conf.networkParams
)
t.chainRef = newChain(t.com)
env
t.com.initializeEmptyDb()
let txPool = TxPoolRef.new(t.com, t.conf.engineSigner)
proc addEngine(env: TestEnv, conf: var NimbusConf): EngineEnv =
conf.tcpPort = Port env.port
conf.udpPort = Port env.port
conf.rpcPort = Port env.rpcPort
let engine = newEngineEnv(conf, env.chainFile, env.enableAuth)
env.clients.add engine
inc env.port
inc env.rpcPort
engine
# txPool must be informed of active head
# so it can know the latest account state
let head = t.com.db.getCanonicalHead()
doAssert txPool.smartHead(head)
proc setup(env: TestEnv, conf: var NimbusConf, chainFile: string, enableAuth: bool) =
env.chainFile = chainFile
env.enableAuth = enableAuth
env.conf = conf
discard env.addEngine(conf)
var key: JwtSharedKey
let kr = key.fromHex(jwtSecret)
if kr.isErr:
echo "JWT SECRET ERROR: ", kr.error
quit(QuitFailure)
proc new*(_: type TestEnv, conf: NimbusConf): TestEnv =
let env = makeEnv(conf)
env.setup(env.conf, "", false)
env
let hooks = if enableAuth:
@[httpJwtAuth(key)]
else:
@[]
proc new*(_: type TestEnv, conf: ChainConfig): TestEnv =
let env = makeEnv(envConfig(conf))
env.setup(env.conf, "", false)
env
t.rpcServer = newRpcHttpServer(["127.0.0.1:" & $t.conf.rpcPort], hooks)
t.sealingEngine = SealingEngineRef.new(
t.chainRef, t.ctx, t.conf.engineSigner,
txPool, EngineStopped
)
proc new*(_: type TestEnv, chainFile: string, enableAuth: bool): TestEnv =
let env = makeEnv(envConfig())
env.setup(env.conf, chainFile, enableAuth)
env
let beaconEngine = BeaconEngineRef.new(txPool, t.chainRef)
setupEthRpc(t.ethNode, t.ctx, t.com, txPool, t.rpcServer)
setupEngineAPI(beaconEngine, t.rpcServer)
setupDebugRpc(t.com, t.rpcServer)
proc close*(env: TestEnv) =
for eng in env.clients:
eng.close()
# Do not start clique sealing engine if we are using a Proof of Work chain file
if chainFile.len > 0:
if not importRlpBlock(chainFolder / chainFile, t.com):
quit(QuitFailure)
elif not enableAuth:
t.sealingEngine.start()
proc addEngine*(env: TestEnv): EngineEnv =
var conf = envConfig(env.conf.networkParams.config)
env.addEngine(conf)
t.rpcServer.start()
func client*(env: TestEnv): RpcHttpClient =
env.clients.first.client
t.rpcClient = newRpcHttpClient()
waitFor t.rpcClient.connect("127.0.0.1", t.conf.rpcPort, false)
t.gHeader = t.com.genesisHeader
func engine*(env: TestEnv): EngineEnv =
env.clients.first
let kRes = PrivateKey.fromHex(vaultKeyHex)
if kRes.isErr:
echo kRes.error
quit(QuitFailure)
proc setupCLMock*(env: TestEnv) =
env.clmock = newCLMocker(env.clients, env.engine.com)
t.vaultKey = kRes.get
proc makeTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx, nonce: AccountNonce): Transaction =
eng.makeTx(env.vaultKey, tc, nonce)
proc setupELClient*(chainFile: string, enableAuth: bool): TestEnv =
result = TestEnv(
conf: makeConfig(@["--engine-signer:658bdf435d810c91414ec09147daa6db62406379", "--custom-network:" & genesisFile])
)
setupELClient(result, chainFile, enableAuth)
proc makeTx*(env: TestEnv, eng: EngineEnv, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction =
eng.makeTx(env.vaultKey, tc, nonce)
proc setupELClient*(conf: ChainConfig): TestEnv =
result = TestEnv(
conf: makeConfig(@["--engine-signer:658bdf435d810c91414ec09147daa6db62406379", "--custom-network:" & genesisFile])
)
result.conf.networkParams.config = conf
setupELClient(result, "", false)
proc sendNextTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx): bool =
eng.sendNextTx(env.vaultKey, tc)
proc newTestEnv*(): TestEnv =
TestEnv(
conf: makeConfig(@["--engine-signer:658bdf435d810c91414ec09147daa6db62406379", "--custom-network:" & genesisFile])
)
proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx, nonce: AccountNonce): bool =
eng.sendTx(env.vaultKey, tc, nonce)
proc newTestEnv*(conf: ChainConfig): TestEnv =
result = TestEnv(
conf: makeConfig(@["--engine-signer:658bdf435d810c91414ec09147daa6db62406379", "--custom-network:" & genesisFile])
)
result.conf.networkParams.config = conf
proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
eng.sendTx(env.vaultKey, tc, nonce)
proc stopELClient*(t: TestEnv) =
waitFor t.rpcClient.close()
waitFor t.sealingEngine.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.com.setTTD some(realTTD)
t.ttd = realTTD
t.clmock = newCLMocker(t.rpcClient, t.com)
proc makeTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): Transaction =
env.engine.makeTx(env.vaultKey, tc, nonce)
proc slotsToSafe*(t: TestEnv, x: int) =
t.clMock.slotsToSafe = x
proc makeTx*(env: TestEnv, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction =
env.engine.makeTx(env.vaultKey, tc, nonce)
proc slotsToFinalized*(t: TestEnv, x: int) =
t.clMock.slotsToFinalized = x
proc sendNextTx*(env: TestEnv, tc: BaseTx): bool =
env.engine.sendNextTx(env.vaultKey, tc)
func gwei(n: int64): GasInt {.compileTime.} =
GasInt(n * (10 ^ 9))
proc sendTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): bool =
env.engine.sendTx(env.vaultKey, tc, nonce)
proc getTxType(tc: BaseTx, nonce: uint64): TxType =
if tc.txType.isNone:
if nonce mod 2 == 0:
TxLegacy
else:
TxEIP1559
else:
tc.txType.get
proc sendTx*(env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
env.engine.sendTx(env.vaultKey, tc, nonce)
proc makeTx*(t: TestEnv, tc: BaseTx, nonce: AccountNonce): Transaction =
const
gasPrice = 30.gwei
gasTipPrice = 1.gwei
proc sendTx*(env: TestEnv, tx: Transaction): bool =
env.engine.sendTx(tx)
gasFeeCap = gasPrice
gasTipCap = gasTipPrice
let chainId = t.conf.networkParams.config.chainId
let txType = tc.getTxType(nonce)
# Build the transaction depending on the specified type
let tx = if txType == TxLegacy:
Transaction(
txType : TxLegacy,
nonce : nonce,
to : tc.recipient,
value : tc.amount,
gasLimit: tc.gasLimit,
gasPrice: gasPrice,
payload : tc.payload
)
else:
Transaction(
txType : TxEIP1559,
nonce : nonce,
gasLimit: tc.gasLimit,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
chainId : chainId
)
signTransaction(tx, t.vaultKey, chainId, eip155 = true)
proc makeTx*(t: TestEnv, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction =
if tc.payload.len == 0:
# Prepare initcode payload
if tc.initcode.len != 0:
doAssert(tc.initcode.len <= tc.initcodeLength, "invalid initcode (too big)")
tc.payload = tc.initcode
while tc.payload.len < tc.initcodeLength:
tc.payload.add tc.padByte
doAssert(tc.recipient.isNone, "invalid configuration for big contract tx creator")
t.makeTx(tc.BaseTx, nonce)
proc sendNextTx*(t: TestEnv, tc: BaseTx): bool =
t.tx = t.makeTx(tc, t.nonce)
inc t.nonce
let rr = t.rpcClient.sendTransaction(t.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(t: TestEnv, tc: BaseTx, nonce: AccountNonce): bool =
t.tx = t.makeTx(tc, nonce)
let rr = t.rpcClient.sendTransaction(t.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(t: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
t.tx = t.makeTx(tc, nonce)
let rr = t.rpcClient.sendTransaction(t.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(t: TestEnv, tx: Transaction): bool =
t.tx = tx
let rr = t.rpcClient.sendTransaction(t.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc verifyPoWProgress*(t: TestEnv, lastBlockHash: ethtypes.Hash256): bool =
let res = waitFor verifyPoWProgress(t.rpcClient, lastBlockHash)
proc verifyPoWProgress*(env: TestEnv, lastBlockHash: common.Hash256): bool =
let res = waitFor env.client.verifyPoWProgress(lastBlockHash)
if res.isErr:
error "verify PoW Progress error", msg=res.error
return false
true
proc slotsToSafe*(env: TestEnv, x: int) =
env.clMock.slotsToSafe = x
proc slotsToFinalized*(env: TestEnv, x: int) =
env.clMock.slotsToFinalized = x

View File

@ -3,7 +3,7 @@ import
withdrawals/wd_block_value_spec,
withdrawals/wd_max_init_code_spec,
#withdrawals/wd_payload_body_spec,
withdrawals/wd_reorg_spec,
#withdrawals/wd_reorg_spec,
withdrawals/wd_sync_spec,
./types,
./test_env
@ -11,19 +11,20 @@ import
proc specExecute[T](ws: BaseSpec): bool =
let
ws = T(ws)
conf = ws.getForkConfig()
env = newTestEnv(conf)
discard ws.getGenesis(env.conf.networkParams)
conf = envConfig(ws.getForkConfig())
setupELClient(env, "", false)
env.setRealTTD(0)
discard ws.getGenesis(conf.networkParams)
let env = TestEnv.new(conf)
env.engine.setRealTTD(0)
env.setupCLMock()
ws.configureCLMock(env.clMock)
result = ws.execute(env)
env.stopELClient()
env.close()
let wdTestList* = [
#Re-Org tests
TestDesc(
#[TestDesc(
name: "Withdrawals Fork on Block 1 - 1 Block Re-Org",
about: "Tests a simple 1 block re-org",
run: specExecute[ReorgSpec],
@ -166,7 +167,7 @@ let wdTestList* = [
reOrgBlockCount: 10,
reOrgViaSync: true,
sidechaintimeIncrements: 1,
)),
)),]#
# Sync Tests
TestDesc(
@ -178,7 +179,7 @@ let wdTestList* = [
"- Wait for sync and verify withdrawn account's balance\n",
run: specExecute[SyncSpec],
spec: SyncSpec(
timeoutSeconds: 6000,
timeoutSeconds: 6,
wdForkHeight: 1,
wdBlockCount: 2,
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
@ -246,7 +247,7 @@ let wdTestList* = [
wdAbleAccountCount: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
syncSteps: 1,
)),
TestDesc(
#[TestDesc(
name: "Sync after 128 blocks - Withdrawals on Block 2 - Multiple Withdrawal Accounts",
about: "- Spawn a first client\n" &
"- Go through withdrawals fork on Block 2\n" &
@ -261,7 +262,7 @@ let wdTestList* = [
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
wdAbleAccountCount: 1024,
syncSteps: 1,
)),
)),]#
# EVM Tests (EIP-3651, EIP-3855, EIP-3860)
TestDesc(
@ -282,6 +283,7 @@ let wdTestList* = [
wdForkHeight: 1,
wdBlockCount: 1,
)),
# Base tests
TestDesc(
name: "Withdrawals Fork On Genesis",
about: "Tests the withdrawals fork happening since genesis (e.g. on a testnet).",

View File

@ -166,15 +166,15 @@ func getGenesis*(ws: WDBaseSpec, param: NetworkParams): NetworkParams =
func getTransactionCountPerPayload(ws: WDBaseSpec): int =
ws.txPerBlock.get(16)
proc verifyContractsStorage(ws: WDBaseSpec, t: TestEnv): Result[void, string] =
proc verifyContractsStorage(ws: WDBaseSpec, env: TestEnv): Result[void, string] =
if ws.getTransactionCountPerPayload() < TX_CONTRACT_ADDRESSES.len:
return
# Assume that forkchoice updated has been already sent
let
latestPayloadNumber = t.clMock.latestExecutedPayload.blockNumber.uint64.u256
r = t.rpcClient.storageAt(WARM_COINBASE_ADDRESS, latestPayloadNumber, latestPayloadNumber)
p = t.rpcClient.storageAt(PUSH0_ADDRESS, 0.u256, latestPayloadNumber)
latestPayloadNumber = env.clMock.latestExecutedPayload.blockNumber.uint64.u256
r = env.client.storageAt(WARM_COINBASE_ADDRESS, latestPayloadNumber, latestPayloadNumber)
p = env.client.storageAt(PUSH0_ADDRESS, 0.u256, latestPayloadNumber)
if latestPayloadNumber.truncate(int) >= ws.wdForkHeight:
# Shanghai
@ -227,10 +227,10 @@ func generateWithdrawalsForBlock(ws: WDBaseSpec, nextIndex: int, startAccount: U
inc result.nextIndex
# Base test case execution procedure for withdrawals
proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
proc execute*(ws: WDBaseSpec, env: TestEnv): bool =
result = true
let ok = waitFor t.clMock.waitForTTD()
let ok = waitFor env.clMock.waitForTTD()
testCond ok
# Check if we have pre-Shanghai blocks
@ -240,7 +240,7 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# Genesis should not contain `withdrawalsRoot` either
var h: common.BlockHeader
let r = t.rpcClient.latestHeader(h)
let r = env.client.latestHeader(h)
testCond r.isOk:
error "failed to ge latest header", msg=r.error
testCond h.withdrawalsRoot.isNone:
@ -248,7 +248,7 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
else:
# Genesis is post shanghai, it should contain EmptyWithdrawalsRoot
var h: common.BlockHeader
let r = t.rpcClient.latestHeader(h)
let r = env.client.latestHeader(h)
testCond r.isOk:
error "failed to ge latest header", msg=r.error
testCond h.withdrawalsRoot.isSome:
@ -257,20 +257,21 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
error "genesis should contains wdsRoot==EMPTY_ROOT_HASH"
# Produce any blocks necessary to reach withdrawals fork
var pbRes = t.clMock.produceBlocks(ws.getPreWithdrawalsBlockCount, BlockProcessCallbacks(
var pbRes = env.clMock.produceBlocks(ws.getPreWithdrawalsBlockCount, BlockProcessCallbacks(
onPayloadProducerSelected: proc(): bool =
# Send some transactions
let numTx = ws.getTransactionCountPerPayload()
for i in 0..<numTx:
let destAddr = TX_CONTRACT_ADDRESSES[i mod TX_CONTRACT_ADDRESSES.len]
let ok = t.sendNextTx(BaseTx(
let ok = env.sendNextTx(
env.clMock.nextBlockProducer,
BaseTx(
recipient: some(destAddr),
amount: 1.u256,
txType: ws.txType,
gasLimit: 75000.GasInt,
))
))
testCond ok:
error "Error trying to send transaction"
@ -278,12 +279,12 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
if not ws.skipBaseVerifications:
# Try to send a ForkchoiceUpdatedV2 with non-null
# withdrawals before Shanghai
var r = t.rpcClient.forkchoiceUpdatedV2(
var r = env.client.forkchoiceUpdatedV2(
ForkchoiceStateV1(
headBlockHash: w3Hash t.clMock.latestHeader,
headBlockHash: w3Hash env.clMock.latestHeader,
),
some(PayloadAttributes(
timestamp: w3Qty(t.clMock.latestHeader.timestamp, ws.getBlockTimeIncrements()),
timestamp: w3Qty(env.clMock.latestHeader.timestamp, ws.getBlockTimeIncrements()),
prevRandao: w3PrevRandao(),
suggestedFeeRecipient: w3Address(),
withdrawals: some(newSeq[WithdrawalV1]()),
@ -294,12 +295,12 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# Send a valid Pre-Shanghai request using ForkchoiceUpdatedV2
# (clMock uses V1 by default)
r = t.rpcClient.forkchoiceUpdatedV2(
r = env.client.forkchoiceUpdatedV2(
ForkchoiceStateV1(
headBlockHash: w3Hash t.clMock.latestHeader,
headBlockHash: w3Hash env.clMock.latestHeader,
),
some(PayloadAttributes(
timestamp: w3Qty(t.clMock.latestHeader.timestamp, ws.getBlockTimeIncrements()),
timestamp: w3Qty(env.clMock.latestHeader.timestamp, ws.getBlockTimeIncrements()),
prevRandao: w3PrevRandao(),
suggestedFeeRecipient: w3Address(),
withdrawals: none(seq[WithdrawalV1]),
@ -314,23 +315,23 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
if not ws.skipBaseVerifications:
# Try to get the same payload but use `engine_getPayloadV2`
let g = t.rpcClient.getPayloadV2(t.clMock.nextPayloadID)
g.expectPayload(t.clMock.latestPayloadBuilt)
let g = env.client.getPayloadV2(env.clMock.nextPayloadID)
g.expectPayload(env.clMock.latestPayloadBuilt)
# Send produced payload but try to include non-nil
# `withdrawals`, it should fail.
let emptyWithdrawalsList = newSeq[Withdrawal]()
let customizer = CustomPayload(
withdrawals: some(emptyWithdrawalsList),
beaconRoot: ethHash t.clMock.latestPayloadAttributes.parentBeaconBlockRoot
beaconRoot: ethHash env.clMock.latestPayloadAttributes.parentBeaconBlockRoot
)
let payloadPlusWithdrawals = customizePayload(t.clMock.latestPayloadBuilt, customizer)
var r = t.rpcClient.newPayloadV2(payloadPlusWithdrawals.V1V2)
let payloadPlusWithdrawals = customizePayload(env.clMock.latestPayloadBuilt, customizer)
var r = env.client.newPayloadV2(payloadPlusWithdrawals.V1V2)
#r.ExpectationDescription = "Sent pre-shanghai payload using NewPayloadV2+Withdrawals, error is expected"
r.expectErrorCode(engineApiInvalidParams)
# Send valid ExecutionPayloadV1 using engine_newPayloadV2
r = t.rpcClient.newPayloadV2(t.clMock.latestPayloadBuilt.V1V2)
r = env.client.newPayloadV2(env.clMock.latestPayloadBuilt.V1V2)
#r.ExpectationDescription = "Sent pre-shanghai payload using NewPayloadV2, no error is expected"
r.expectStatus(valid)
return true
@ -340,7 +341,7 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# We sent a pre-shanghai FCU.
# Keep expecting `nil` until Shanghai.
var h: common.BlockHeader
let r = t.rpcClient.latestHeader(h)
let r = env.client.latestHeader(h)
#r.ExpectationDescription = "Requested "latest" block expecting block to contain
#" withdrawalRoot=nil, because (block %d).timestamp < shanghaiTime
r.expectWithdrawalsRoot(h, none(common.Hash256))
@ -348,7 +349,7 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
,
onForkchoiceBroadcast: proc(): bool =
if not ws.skipBaseVerifications:
let r = ws.verifyContractsStorage(t)
let r = ws.verifyContractsStorage(env)
testCond r.isOk:
error "verifyContractsStorage error", msg=r.error
return true
@ -362,17 +363,17 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
startAccount = ws.getWithdrawalsStartAccount()
nextIndex = 0
pbRes = t.clMock.produceBlocks(ws.wdBlockCount, BlockProcessCallbacks(
pbRes = env.clMock.produceBlocks(ws.wdBlockCount, BlockProcessCallbacks(
onPayloadProducerSelected: proc(): bool =
if not ws.skipBaseVerifications:
# Try to send a PayloadAttributesV1 with null withdrawals after
# Shanghai
let r = t.rpcClient.forkchoiceUpdatedV2(
let r = env.client.forkchoiceUpdatedV2(
ForkchoiceStateV1(
headBlockHash: w3Hash t.clMock.latestHeader,
headBlockHash: w3Hash env.clMock.latestHeader,
),
some(PayloadAttributes(
timestamp: w3Qty(t.clMock.latestHeader.timestamp, ws.getBlockTimeIncrements()),
timestamp: w3Qty(env.clMock.latestHeader.timestamp, ws.getBlockTimeIncrements()),
prevRandao: w3PrevRandao(),
suggestedFeeRecipient: w3Address(),
withdrawals: none(seq[WithdrawalV1]),
@ -383,20 +384,22 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# Send some withdrawals
let wfb = ws.generateWithdrawalsForBlock(nextIndex, startAccount)
t.clMock.nextWithdrawals = some(w3Withdrawals wfb.wds)
ws.wdHistory.put(t.clMock.currentPayloadNumber, wfb.wds)
env.clMock.nextWithdrawals = some(w3Withdrawals wfb.wds)
ws.wdHistory.put(env.clMock.currentPayloadNumber, wfb.wds)
# Send some transactions
let numTx = ws.getTransactionCountPerPayload()
for i in 0..<numTx:
let destAddr = TX_CONTRACT_ADDRESSES[i mod TX_CONTRACT_ADDRESSES.len]
let ok = t.sendNextTx(BaseTx(
let ok = env.sendNextTx(
env.clMock.nextBlockProducer,
BaseTx(
recipient: some(destAddr),
amount: 1.u256,
txType: ws.txType,
gasLimit: 75000.GasInt,
))
))
testCond ok:
error "Error trying to send transaction"
@ -411,21 +414,21 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# be checked first instead of responding `INVALID`
let customizer = CustomPayload(
removeWithdrawals: true,
beaconRoot: ethHash t.clMock.latestPayloadAttributes.parentBeaconBlockRoot
beaconRoot: ethHash env.clMock.latestPayloadAttributes.parentBeaconBlockRoot
)
let nilWithdrawalsPayload = customizePayload(t.clMock.latestPayloadBuilt, customizer)
let r = t.rpcClient.newPayloadV2(nilWithdrawalsPayload.V1V2)
let nilWithdrawalsPayload = customizePayload(env.clMock.latestPayloadBuilt, customizer)
let r = env.client.newPayloadV2(nilWithdrawalsPayload.V1V2)
#r.ExpectationDescription = "Sent shanghai payload using ExecutionPayloadV1, error is expected"
r.expectErrorCode(engineApiInvalidParams)
# Verify the list of withdrawals returned on the payload built
# completely matches the list provided in the
# engine_forkchoiceUpdatedV2 method call
let res = ws.wdHistory.get(t.clMock.currentPayloadNumber)
let res = ws.wdHistory.get(env.clMock.currentPayloadNumber)
doAssert(res.isOk, "withdrawals sent list was not saved")
let sentList = res.get
let wdList = t.clMock.latestPayloadBuilt.withdrawals.get
let wdList = env.clMock.latestPayloadBuilt.withdrawals.get
testCond sentList.len == wdList.len:
error "Incorrect list of withdrawals on built payload",
want=sentList.len,
@ -441,15 +444,15 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# Check withdrawal addresses and verify withdrawal balances
# have not yet been applied
if not ws.skipBaseVerifications:
let addrList = ws.wdHistory.getAddressesWithdrawnOnBlock(t.clMock.latestExecutedPayload.blockNumber.uint64)
let addrList = ws.wdHistory.getAddressesWithdrawnOnBlock(env.clMock.latestExecutedPayload.blockNumber.uint64)
for address in addrList:
# Test balance at `latest`, which should not yet have the
# withdrawal applied.
let expectedAccountBalance = ws.wdHistory.getExpectedAccountBalance(
address,
t.clMock.latestExecutedPayload.blockNumber.uint64-1)
env.clMock.latestExecutedPayload.blockNumber.uint64-1)
let r = t.rpcClient.balanceAt(address)
let r = env.client.balanceAt(address)
#r.ExpectationDescription = fmt.Sprintf(`
# Requested balance for account %s on "latest" block
# after engine_newPayloadV2, expecting balance to be equal
@ -457,12 +460,12 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# has not yet been applied.
# `,
# addr,
# t.clMock.LatestExecutedPayload.Number-1,
# env.clMock.LatestExecutedPayload.Number-1,
#)
r.expectBalanceEqual(expectedAccountBalance)
if ws.testCorrupedHashPayloads:
var payload = t.clMock.latestExecutedPayload
var payload = env.clMock.latestExecutedPayload
# Corrupt the hash
var randomHash: common.Hash256
@ -471,7 +474,7 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# On engine_newPayloadV2 `INVALID_BLOCK_HASH` is deprecated
# in favor of reusing `INVALID`
let n = t.rpcClient.newPayloadV2(payload.V1V2)
let n = env.client.newPayloadV2(payload.V1V2)
n.expectStatus(invalid)
return true
,
@ -479,11 +482,11 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# Check withdrawal addresses and verify withdrawal balances
# have been applied
if not ws.skipBaseVerifications:
let addrList = ws.wdHistory.getAddressesWithdrawnOnBlock(t.clMock.latestExecutedPayload.blockNumber.uint64)
let addrList = ws.wdHistory.getAddressesWithdrawnOnBlock(env.clMock.latestExecutedPayload.blockNumber.uint64)
for address in addrList:
# Test balance at `latest`, which should have the
# withdrawal applied.
let r = t.rpcClient.balanceAt(address)
let r = env.client.balanceAt(address)
#r.ExpectationDescription = fmt.Sprintf(`
# Requested balance for account %s on "latest" block
# after engine_forkchoiceUpdatedV2, expecting balance to
@ -491,27 +494,27 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# has not yet been applied.
# `,
# addr,
# t.clMock.LatestExecutedPayload.Number,
# env.clMock.LatestExecutedPayload.Number,
#)
let expectedAccountBalance = ws.wdHistory.getExpectedAccountBalance(
address,
t.clMock.latestExecutedPayload.blockNumber.uint64)
env.clMock.latestExecutedPayload.blockNumber.uint64)
r.expectBalanceEqual(expectedAccountBalance)
let wds = ws.wdHistory.getWithdrawals(t.clMock.latestExecutedPayload.blockNumber.uint64)
let wds = ws.wdHistory.getWithdrawals(env.clMock.latestExecutedPayload.blockNumber.uint64)
let expectedWithdrawalsRoot = some(calcWithdrawalsRoot(wds.list))
# Check the correct withdrawal root on `latest` block
var h: common.BlockHeader
let r = t.rpcClient.latestHeader(h)
let r = env.client.latestHeader(h)
#r.ExpectationDescription = fmt.Sprintf(`
# Requested "latest" block after engine_forkchoiceUpdatedV2,
# to verify withdrawalsRoot with the following withdrawals:
# %s`, jsWithdrawals)
r.expectWithdrawalsRoot(h, expectedWithdrawalsRoot)
let res = ws.verifyContractsStorage(t)
let res = ws.verifyContractsStorage(env)
testCond res.isOk:
error "verifyContractsStorage error", msg=res.error
return true
@ -523,15 +526,15 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
# Also check one block before the withdrawal took place, verify that
# withdrawal has not been updated.
if not ws.skipBaseVerifications:
let maxBlock = t.clMock.latestExecutedPayload.blockNumber.uint64
let maxBlock = env.clMock.latestExecutedPayload.blockNumber.uint64
for bn in 0..maxBlock:
let res = ws.wdHistory.verifyWithdrawals(bn, some(bn.u256), t.rpcClient)
let res = ws.wdHistory.verifyWithdrawals(bn, some(bn.u256), env.client)
testCond res.isOk:
error "verify wd error", msg=res.error
# Check the correct withdrawal root on past blocks
var h: common.BlockHeader
let r = t.rpcClient.headerByNumber(bn, h)
let r = env.client.headerByNumber(bn, h)
var expectedWithdrawalsRoot: Option[common.Hash256]
if bn >= ws.wdForkHeight.uint64:
@ -545,7 +548,7 @@ proc execute*(ws: WDBaseSpec, t: TestEnv): bool =
r.expectWithdrawalsRoot(h, expectedWithdrawalsRoot)
# Verify on `latest`
let bnu = t.clMock.latestExecutedPayload.blockNumber.uint64
let res = ws.wdHistory.verifyWithdrawals(bnu, none(UInt256), t.rpcClient)
let bnu = env.clMock.latestExecutedPayload.blockNumber.uint64
let res = ws.wdHistory.verifyWithdrawals(bnu, none(UInt256), env.client)
testCond res.isOk:
error "verify wd error", msg=res.error

View File

@ -11,13 +11,13 @@ import
type
BlockValueSpec* = ref object of WDBaseSpec
proc execute*(ws: BlockValueSpec, t: TestEnv): bool =
proc execute*(ws: BlockValueSpec, env: TestEnv): bool =
WDBaseSpec(ws).skipBaseVerifications = true
testCond WDBaseSpec(ws).execute(t)
testCond WDBaseSpec(ws).execute(env)
# Get the latest block and the transactions included
var blk: EthBlock
let b = t.rpcClient.latestBlock(blk)
let b = env.client.latestBlock(blk)
b.expectNoError()
var totalValue: UInt256
@ -26,7 +26,7 @@ proc execute*(ws: BlockValueSpec, t: TestEnv): bool =
for tx in blk.txs:
let txHash = rlpHash(tx)
let r = t.rpcClient.txReceipt(txHash)
let r = env.client.txReceipt(txHash)
r.expectNoError()
let
@ -35,9 +35,9 @@ proc execute*(ws: BlockValueSpec, t: TestEnv): bool =
totalValue += txTip.uint64.u256 * rec.gasUsed.u256
doAssert(t.cLMock.latestBlockValue.isSome)
testCond totalValue == t.cLMock.latestBlockValue.get:
doAssert(env.cLMock.latestBlockValue.isSome)
testCond totalValue == env.cLMock.latestBlockValue.get:
error "Unexpected block value returned on GetPayloadV2",
expect=totalValue,
get=t.cLMock.latestBlockValue.get
get=env.cLMock.latestBlockValue.get
return true

View File

@ -24,8 +24,8 @@ type
const
MAX_INITCODE_SIZE = EIP3860_MAX_INITCODE_SIZE
proc execute*(ws: MaxInitcodeSizeSpec, t: TestEnv): bool =
testCond waitFor t.clMock.waitForTTD()
proc execute*(ws: MaxInitcodeSizeSpec, env: TestEnv): bool =
testCond waitFor env.clMock.waitForTTD()
var
invalidTxCreator = BigInitcodeTx(
@ -41,17 +41,17 @@ proc execute*(ws: MaxInitcodeSizeSpec, t: TestEnv): bool =
if ws.overflowMaxInitcodeTxCountBeforeFork > 0:
doAssert(ws.getPreWithdrawalsBlockCount > 0, "invalid test configuration")
for i in 0..<ws.overflowMaxInitcodeTxCountBeforeFork:
testCond t.sendTx(invalidTxCreator, i):
testCond env.sendTx(invalidTxCreator, i):
error "Error sending max initcode transaction before Shanghai"
# Produce all blocks needed to reach Shanghai
info "Blocks until Shanghai", count=ws.getPreWithdrawalsBlockCount
var txIncluded = 0'u64
var pbRes = t.clMock.produceBlocks(ws.getPreWithdrawalsBlockCount, BlockProcessCallbacks(
var pbRes = env.clMock.produceBlocks(ws.getPreWithdrawalsBlockCount, BlockProcessCallbacks(
onGetPayload: proc(): bool =
info "Got Pre-Shanghai", blockNumber=t.clMock.latestPayloadBuilt.blockNumber.uint64
txIncluded += t.clMock.latestPayloadBuilt.transactions.len.uint64
info "Got Pre-Shanghai", blockNumber=env.clMock.latestPayloadBuilt.blockNumber.uint64
txIncluded += env.clMock.latestPayloadBuilt.transactions.len.uint64
return true
))
@ -62,9 +62,9 @@ proc execute*(ws: MaxInitcodeSizeSpec, t: TestEnv): bool =
error "No max initcode txs included before Shanghai. Txs must have been included before the MAX_INITCODE_SIZE limit was enabled"
# Create a payload, no txs should be included
pbRes = t.clMock.produceSingleBlock(BlockProcessCallbacks(
pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
onGetPayload: proc(): bool =
testCond t.clMock.latestPayloadBuilt.transactions.len == 0:
testCond env.clMock.latestPayloadBuilt.transactions.len == 0:
error "Client included tx exceeding the MAX_INITCODE_SIZE in payload"
return true
))
@ -73,42 +73,42 @@ proc execute*(ws: MaxInitcodeSizeSpec, t: TestEnv): bool =
# Send transactions after the fork
for i in txIncluded..<txIncluded + ws.overflowMaxInitcodeTxCountAfterFork:
let tx = t.makeTx(invalidTxCreator, i)
testCond not t.sendTx(tx):
let tx = env.makeTx(invalidTxCreator, i)
testCond not env.sendTx(tx):
error "Client accepted tx exceeding the MAX_INITCODE_SIZE"
let res = t.rpcClient.txByHash(rlpHash(tx))
let res = env.client.txByHash(rlpHash(tx))
testCond res.isErr:
error "Invalid tx was not unknown to the client"
# Try to include an invalid tx in new payload
let
validTx = t.makeTx(validTxCreator, txIncluded)
invalidTx = t.makeTx(invalidTxCreator, txIncluded)
let
validTx = env.makeTx(validTxCreator, txIncluded)
invalidTx = env.makeTx(invalidTxCreator, txIncluded)
pbRes = t.clMock.produceSingleBlock(BlockProcessCallbacks(
pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
onPayloadProducerSelected: proc(): bool =
testCond t.sendTx(validTx)
testCond env.sendTx(validTx)
return true
,
onGetPayload: proc(): bool =
let validTxBytes = rlp.encode(validTx)
testCond t.clMock.latestPayloadBuilt.transactions.len == 1:
testCond env.clMock.latestPayloadBuilt.transactions.len == 1:
error "Client did not include valid tx with MAX_INITCODE_SIZE"
testCond validTxBytes == distinctBase(t.clMock.latestPayloadBuilt.transactions[0]):
testCond validTxBytes == distinctBase(env.clMock.latestPayloadBuilt.transactions[0]):
error "valid Tx bytes mismatch"
# Customize the payload to include a tx with an invalid initcode
let customData = CustomPayload(
beaconRoot: ethHash t.clMock.latestPayloadAttributes.parentBeaconBlockRoot,
beaconRoot: ethHash env.clMock.latestPayloadAttributes.parentBeaconBlockRoot,
transactions: some( @[invalidTx] ),
)
let customPayload = customizePayload(t.clMock.latestPayloadBuilt, customData)
let res = t.rpcClient.newPayloadV2(customPayload.V1V2)
let customPayload = customizePayload(env.clMock.latestPayloadBuilt, customData)
let res = env.client.newPayloadV2(customPayload.V1V2)
res.expectStatus(invalid)
res.expectLatestValidHash(t.clMock.latestPayloadBuilt.parentHash)
res.expectLatestValidHash(env.clMock.latestPayloadBuilt.parentHash)
return true
))

View File

@ -1,6 +1,8 @@
import
chronicles,
json_rpc/rpcclient,
./wd_base_spec,
./wd_history,
../test_env,
../engine_client,
../types
@ -14,40 +16,51 @@ type
syncShouldFail*: bool
timeoutSeconds*: int
proc execute*(ws: SyncSpec, t: TestEnv): bool =
proc doSync(ws: SyncSpec, client: RpcClient, clMock: CLMocker): Future[bool] {.async.} =
let period = chronos.seconds(1)
var loop = 0
while loop < ws.timeoutSeconds:
let res = client.newPayloadV2(clMock.latestExecutedPayload.V1V2)
discard res
let r = client.forkchoiceUpdatedV2(clMock.latestForkchoice)
if r.isErr:
error "fcu error", msg=r.error
return false
let s = r.get
if s.payloadStatus.status == PayloadExecutionStatus.valid:
return true
if s.payloadStatus.status == PayloadExecutionStatus.invalid:
error "Syncing client rejected valid chain"
await sleepAsync(period)
inc loop
return false
proc execute*(ws: SyncSpec, env: TestEnv): bool =
# Do the base withdrawal test first, skipping base verifications
WDBaseSpec(ws).skipBaseVerifications = true
testCond WDBaseSpec(ws).execute(t)
testCond WDBaseSpec(ws).execute(env)
#[
# Spawn a secondary client which will need to sync to the primary client
secondaryEngine, err := hive_rpc.HiveRPCEngineStarter{}.StartClient(t.T, t.TestContext, t.Genesis, t.ClientParams, t.ClientFiles, t.Engine)
if err != nil {
error "Unable to spawn a secondary client: %v", t.TestName, err)
let sec = env.addEngine()
secondaryEngineTest := test.NewTestEngineClient(t, secondaryEngine)
t.clMock.AddEngineClient(secondaryEngine)
if ws.SyncSteps > 1 {
if ws.syncSteps > 1:
# TODO
discard
else:
# Send the FCU to trigger sync on the secondary client
loop:
for {
select {
case <-t.TimeoutContext.Done():
error "Timeout while waiting for secondary client to sync", t.TestName)
case <-time.After(time.Second):
secondaryEngineTest.TestEngineNewPayloadV2(
&t.clMock.latestExecutedPayload,
r := secondaryEngineTest.TestEngineForkchoiceUpdatedV2(
&t.clMock.latestForkchoice,
nil,
if r.Response.PayloadStatus.Status == test.Valid {
break loop
if r.Response.PayloadStatus.Status == test.Invalid {
error "Syncing client rejected valid chain: %s", t.TestName, r.Response)
let ok = waitFor doSync(ws, sec.client, env.clMock)
if not ok:
return false
let bn = env.clMock.latestHeader.blockNumber.truncate(uint64)
let res = ws.wdHistory.verifyWithdrawals(bn, none(UInt256), sec.client)
if res.isErr:
error "wd history error", msg=res.error
return false
ws.wdHistory.VerifyWithdrawals(t.clMock.latestHeader.Number.Uint64(), nil, secondaryEngineTest)
]#
return true