mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-13 05:44:40 +00:00
Add multiple clients support to engine api simulator
This commit is contained in:
parent
070b06f809
commit
6139152143
@ -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* = [
|
||||
|
23
hive_integration/nodocker/engine/client_pool.nim
Normal file
23
hive_integration/nodocker/engine/client_pool.nim
Normal 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
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
253
hive_integration/nodocker/engine/engine_env.nim
Normal file
253
hive_integration/nodocker/engine/engine_env.nim
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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* = [
|
||||
|
@ -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
|
||||
|
@ -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).",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
))
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user