More Engine API tests
This commit is contained in:
parent
25bc8e4b22
commit
19f313d891
|
@ -72,7 +72,7 @@ type
|
|||
latestForkchoice* : ForkchoiceStateV1
|
||||
|
||||
# Merge related
|
||||
firstPoSBlockNumber : Option[uint64]
|
||||
firstPoSBlockNumber* : Option[uint64]
|
||||
ttdReached* : bool
|
||||
transitionPayloadTimestamp: Option[int]
|
||||
chainTotalDifficulty : UInt256
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
|
||||
import
|
||||
std/strutils,
|
||||
./engine_spec
|
||||
chronicles,
|
||||
./engine_spec,
|
||||
../../../../nimbus/common/hardforks
|
||||
|
||||
type
|
||||
ForkIDSpec* = ref object of EngineSpec
|
||||
produceBlocksBeforePeering: int
|
||||
produceBlocksBeforePeering*: int
|
||||
|
||||
method withMainFork(cs: ForkIDSpec, fork: EngineFork): BaseSpec =
|
||||
var res = cs.clone()
|
||||
|
@ -22,15 +24,25 @@ method withMainFork(cs: ForkIDSpec, fork: EngineFork): BaseSpec =
|
|||
return res
|
||||
|
||||
method getName(cs: ForkIDSpec): string =
|
||||
name = "Fork ID: Genesis at %d, %s at %d", cs.GetGenesistimestamp(), cs.mainFork, cs.ForkTime)
|
||||
if cs.previousForkTime != 0 (
|
||||
name += ", %s at %d", cs.mainFork.PreviousFork(), cs.previousForkTime)
|
||||
)
|
||||
if cs.produceBlocksBeforePeering > 0 (
|
||||
name += ", Produce %d blocks before peering", cs.produceBlocksBeforePeering)
|
||||
)
|
||||
var name = "Fork ID: Genesis at $1, $2 at $3" % [$cs.getGenesistimestamp(), $cs.mainFork, $cs.forkTime]
|
||||
if cs.previousForkTime != 0:
|
||||
name.add ", $1 at $2" % [$cs.mainFork.pred, $cs.previousForkTime]
|
||||
|
||||
if cs.produceBlocksBeforePeering > 0:
|
||||
name.add ", Produce $1 blocks before peering" % [$cs.produceBlocksBeforePeering]
|
||||
|
||||
return name
|
||||
)
|
||||
|
||||
method getForkConfig*(cs: ForkIDSpec): ChainConfig =
|
||||
let forkConfig = procCall getForkConfig(BaseSpec(cs))
|
||||
if forkConfig.isNil:
|
||||
return nil
|
||||
|
||||
# Merge fork happen at block 0
|
||||
let mainFork = cs.getMainFork()
|
||||
if mainFork == ForkParis:
|
||||
forkConfig.mergeForkBlock = some(0.u256)
|
||||
return forkConfig
|
||||
|
||||
method execute(cs: ForkIDSpec, env: TestEnv): bool =
|
||||
# Wait until TTD is reached by this client
|
||||
|
@ -38,41 +50,8 @@ method execute(cs: ForkIDSpec, env: TestEnv): bool =
|
|||
testCond ok
|
||||
|
||||
# Produce blocks before starting the test if required
|
||||
env.clMock.produceBlocks(cs.produceBlocksBeforePeering, BlockProcessCallbacks())
|
||||
testCond env.clMock.produceBlocks(cs.produceBlocksBeforePeering, BlockProcessCallbacks())
|
||||
|
||||
# Get client index's enode
|
||||
engine = t.Engine
|
||||
conn, err = devp2p.PeerEngineClient(engine, t.CLMock)
|
||||
if err != nil (
|
||||
fatal "Error peering engine client: %v", err)
|
||||
)
|
||||
defer conn.Close()
|
||||
info "Connected to client, remote public key: %s", conn.RemoteKey())
|
||||
|
||||
# Sleep
|
||||
await sleepAsync(1 * time.Second)
|
||||
|
||||
# Timeout value for all requests
|
||||
timeout = 20 * time.Second
|
||||
|
||||
# Send a ping request to verify that we are not immediately disconnected
|
||||
pingReq = &devp2p.Ping()
|
||||
if size, err = conn.Write(pingReq); err != nil (
|
||||
fatal "Could not write to connection: %v", err)
|
||||
else:
|
||||
info "Wrote %d bytes to conn", size)
|
||||
)
|
||||
|
||||
# Finally wait for the pong response
|
||||
msg, err = conn.WaitForResponse(timeout, 0)
|
||||
if err != nil (
|
||||
fatal "Error waiting for response: %v", err)
|
||||
)
|
||||
switch msg = msg.(type) (
|
||||
case *devp2p.Pong:
|
||||
info "Received pong response: %v", msg)
|
||||
default:
|
||||
fatal "Unexpected message type: %v", err)
|
||||
)
|
||||
|
||||
)
|
||||
let engine = env.addEngine()
|
||||
return true
|
||||
|
|
|
@ -240,6 +240,10 @@ method execute(cs: InvalidMissingAncestorReOrgSyncTest, env: TestEnv): bool =
|
|||
# Append the common ancestor
|
||||
shadow.payloads.add env.clMock.latestExecutableData
|
||||
|
||||
if not cs.reOrgFromCanonical:
|
||||
# Add back the original client before side chain production
|
||||
env.cLMock.addEngine(env.engine)
|
||||
|
||||
# Produce blocks but at the same time create an side chain which contains an invalid payload at some point (INV_P)
|
||||
# CommonAncestor◄─▲── P1 ◄─ P2 ◄─ P3 ◄─ ... ◄─ Pn
|
||||
# │
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
import
|
||||
std/strutils,
|
||||
eth/common,
|
||||
chronicles,
|
||||
../cancun/customizer,
|
||||
./engine_spec
|
||||
|
||||
type
|
||||
|
@ -37,45 +40,49 @@ method execute(cs: ReExecutePayloadTest, env: TestEnv): bool =
|
|||
let pbRes = env.clMock.produceBlocks(payloadReExecCount, BlockProcessCallbacks(
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
# Send at least one transaction per payload
|
||||
_, err = env.sendNextTx(
|
||||
env.clMock.nextBlockProducer,
|
||||
BaseTx(
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
),
|
||||
let tc = BaseTx(
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
if err != nil (
|
||||
fatal "Error trying to send transaction: %v", t.TestName, err)
|
||||
)
|
||||
),
|
||||
let ok = env.sendNextTx(env.clMock.nextBlockProducer, tc)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction"
|
||||
return true
|
||||
,
|
||||
onGetPayload: proc(): bool =
|
||||
# Check that the transaction was included
|
||||
if len(env.clMock.latestPayloadBuilt.transactions) == 0 (
|
||||
fatal "Client failed to include the expected transaction in payload built", t.TestName)
|
||||
)
|
||||
),
|
||||
testCond len(env.clMock.latestPayloadBuilt.transactions) != 0:
|
||||
fatal "Client failed to include the expected transaction in payload built"
|
||||
return true
|
||||
))
|
||||
|
||||
testCond pbRes
|
||||
|
||||
# Re-execute the payloads
|
||||
r = env.engine.client.blockNumber()
|
||||
let r = env.engine.client.blockNumber()
|
||||
r.expectNoError()
|
||||
lastBlock = r.blockNumber
|
||||
info "Started re-executing payloads at block: %v", t.TestName, lastBlock)
|
||||
let lastBlock = r.get
|
||||
info "Started re-executing payloads at block", number=lastBlock
|
||||
|
||||
for i = lastBlock - uint64(payloadReExecCount) + 1; i <= lastBlock; i++ (
|
||||
payload, found = env.clMock.executedPayloadHistory[i]
|
||||
if !found (
|
||||
fatal "(test issue) Payload with index %d does not exist", i)
|
||||
)
|
||||
let start = lastBlock - uint64(payloadReExecCount) + 1
|
||||
|
||||
r = env.engine.client.newPayload(payload)
|
||||
for i in start..lastBlock:
|
||||
doAssert env.clMock.executedPayloadHistory.hasKey(i)
|
||||
let payload = env.clMock.executedPayloadHistory[i]
|
||||
let r = env.engine.client.newPayload(payload)
|
||||
r.expectStatus(PayloadExecutionStatus.valid)
|
||||
r.expectLatestValidHash(payload.blockHash)
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
|
||||
type
|
||||
InOrderPayloadExecutionTest* = ref object of EngineSpec
|
||||
Shadow = ref object
|
||||
recipient: EthAddress
|
||||
amountPerTx: UInt256
|
||||
txPerPayload: int
|
||||
payloadCount: int
|
||||
txsIncluded: int
|
||||
|
||||
method withMainFork(cs: InOrderPayloadExecutionTest, fork: EngineFork): BaseSpec =
|
||||
var res = cs.clone()
|
||||
|
@ -92,93 +99,93 @@ method execute(cs: InOrderPayloadExecutionTest, env: TestEnv): bool =
|
|||
testCond ok
|
||||
|
||||
# Send a single block to allow sending newer transaction types on the payloads
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks())
|
||||
testCond env.clMock.produceSingleBlock(BlockProcessCallbacks())
|
||||
|
||||
# First prepare payloads on a first client, which will also contain multiple transactions
|
||||
|
||||
# We will be also verifying that the transactions are correctly interpreted in the canonical chain,
|
||||
# prepare a random account to receive funds.
|
||||
recipient = EthAddress.randomBytes()
|
||||
amountPerTx = big.NewInt(1000)
|
||||
txPerPayload = 20
|
||||
payloadCount = 10
|
||||
txsIncluded = 0
|
||||
var shadow = Shadow(
|
||||
recipient: EthAddress.randomBytes(),
|
||||
amountPerTx: 1000.u256,
|
||||
txPerPayload: 20,
|
||||
payloadCount: 10,
|
||||
txsIncluded: 0,
|
||||
)
|
||||
|
||||
env.clMock.produceBlocks(payloadCount, BlockProcessCallbacks(
|
||||
let pbRes = env.clMock.produceBlocks(shadow.payloadCount, BlockProcessCallbacks(
|
||||
# We send the transactions after we got the Payload ID, before the CLMocker gets the prepared Payload
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
_, err = env.sendNextTxs(
|
||||
env.clMock.nextBlockProducer,
|
||||
BaseTx(
|
||||
recipient: &recipient,
|
||||
amount: amountPerTx,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
),
|
||||
uint64(txPerPayload),
|
||||
let tc = BaseTx(
|
||||
recipient: some(shadow.recipient),
|
||||
amount: shadow.amountPerTx,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
if err != nil (
|
||||
fatal "Error trying to send transaction: %v", t.TestName, err)
|
||||
)
|
||||
),
|
||||
let ok = env.sendNextTxs(env.clMock.nextBlockProducer, tc, shadow.txPerPayload)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction"
|
||||
return true
|
||||
,
|
||||
onGetPayload: proc(): bool =
|
||||
if len(env.clMock.latestPayloadBuilt.Transactions) < (txPerPayload / 2) (
|
||||
fatal "Client failed to include all the expected transactions in payload built: %d < %d", t.TestName, len(env.clMock.latestPayloadBuilt.Transactions), (txPerPayload / 2))
|
||||
)
|
||||
txsIncluded += len(env.clMock.latestPayloadBuilt.Transactions)
|
||||
),
|
||||
if len(env.clMock.latestPayloadBuilt.transactions) < (shadow.txPerPayload div 2):
|
||||
fatal "Client failed to include all the expected transactions in payload built"
|
||||
|
||||
shadow.txsIncluded += len(env.clMock.latestPayloadBuilt.transactions)
|
||||
return true
|
||||
))
|
||||
|
||||
expectedBalance = amountPerTx.Mul(amountPerTx, big.NewInt(int64(txsIncluded)))
|
||||
testCond pbRes
|
||||
let expectedBalance = shadow.amountPerTx * shadow.txsIncluded.u256
|
||||
|
||||
# Check balance on this first client
|
||||
r = env.engine.client.balanceAt(recipient, nil)
|
||||
let r = env.engine.client.balanceAt(shadow.recipient)
|
||||
r.expectBalanceEqual(expectedBalance)
|
||||
|
||||
# Start a second client to send newPayload consecutively without fcU
|
||||
let sec = env.addEngine(false, false)
|
||||
|
||||
# Send the forkchoiceUpdated with the latestExecutedPayload hash, we should get SYNCING back
|
||||
fcU = ForkchoiceStateV1(
|
||||
let fcU = ForkchoiceStateV1(
|
||||
headblockHash: env.clMock.latestExecutedPayload.blockHash,
|
||||
safeblockHash: env.clMock.latestExecutedPayload.blockHash,
|
||||
finalizedblockHash: env.clMock.latestExecutedPayload.blockHash,
|
||||
)
|
||||
|
||||
s = sec.client.forkchoiceUpdated(fcU, nil, env.clMock.latestExecutedPayload.timestamp)
|
||||
var version = sec.version(env.clMock.latestExecutedPayload.timestamp)
|
||||
var s = sec.client.forkchoiceUpdated(version, fcU)
|
||||
s.expectPayloadStatus(PayloadExecutionStatus.syncing)
|
||||
s.expectLatestValidHash(nil)
|
||||
s.ExpectNoValidationError()
|
||||
s.expectLatestValidHash()
|
||||
s.expectNoValidationError()
|
||||
|
||||
# Send all the payloads in the increasing order
|
||||
for k = env.clMock.firstPoSBlockNumber.Uint64(); k <= env.clMock.latestExecutedPayload.blockNumber; k++ (
|
||||
payload = env.clMock.executedPayloadHistory[k]
|
||||
|
||||
s = sec.client.newPayload(payload)
|
||||
let start = env.clMock.firstPoSBlockNumber.get
|
||||
for k in start..env.clMock.latestExecutedPayload.blockNumber.uint64:
|
||||
let payload = env.clMock.executedPayloadHistory[k]
|
||||
let s = sec.client.newPayload(payload)
|
||||
s.expectStatus(PayloadExecutionStatus.valid)
|
||||
s.expectLatestValidHash(payload.blockHash)
|
||||
|
||||
)
|
||||
|
||||
s = sec.client.forkchoiceUpdated(fcU, nil, env.clMock.latestExecutedPayload.timestamp)
|
||||
version = sec.version(env.clMock.latestExecutedPayload.timestamp)
|
||||
s = sec.client.forkchoiceUpdated(version, fcU)
|
||||
s.expectPayloadStatus(PayloadExecutionStatus.valid)
|
||||
s.expectLatestValidHash(fcU.headblockHash)
|
||||
s.ExpectNoValidationError()
|
||||
s.expectNoValidationError()
|
||||
|
||||
# At this point we should have our funded account balance equal to the expected value.
|
||||
q = sec.client.balanceAt(recipient, nil)
|
||||
let q = sec.client.balanceAt(shadow.recipient)
|
||||
q.expectBalanceEqual(expectedBalance)
|
||||
|
||||
# Add the client to the CLMocker
|
||||
env.clMock.addEngine(secondaryClient)
|
||||
env.clMock.addEngine(sec)
|
||||
|
||||
# Produce a single block on top of the canonical chain, all clients must accept this
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks())
|
||||
testCond env.clMock.produceSingleBlock(BlockProcessCallbacks())
|
||||
|
||||
# Head must point to the latest produced payload
|
||||
p = sec.client.TestHeaderByNumber(nil)
|
||||
p.expectHash(env.clMock.latestExecutedPayload.blockHash)
|
||||
)
|
||||
let p = sec.client.latestHeader()
|
||||
p.expectHash(ethHash env.clMock.latestExecutedPayload.blockHash)
|
||||
return true
|
||||
|
||||
type
|
||||
MultiplePayloadsExtendingCanonicalChainTest* = ref object of EngineSpec
|
||||
|
@ -194,12 +201,10 @@ method withMainFork(cs: MultiplePayloadsExtendingCanonicalChainTest, fork: Engin
|
|||
return res
|
||||
|
||||
method getName(cs: MultiplePayloadsExtendingCanonicalChainTest): string =
|
||||
name = "Multiple New Payloads Extending Canonical Chain"
|
||||
if s.SetHeadToFirstPayloadReceived (
|
||||
name += " (FcU to first payload received)"
|
||||
)
|
||||
return name
|
||||
)
|
||||
var name = "Multiple New Payloads Extending Canonical Chain"
|
||||
if cs.setHeadToFirstPayloadReceived:
|
||||
name.add " (FcU to first payload received)"
|
||||
name
|
||||
|
||||
# Consecutive Payload Execution: Secondary client should be able to set the forkchoiceUpdated to payloads received consecutively
|
||||
method execute(cs: MultiplePayloadsExtendingCanonicalChainTest, env: TestEnv): bool =
|
||||
|
@ -208,57 +213,48 @@ method execute(cs: MultiplePayloadsExtendingCanonicalChainTest, env: TestEnv): b
|
|||
testCond ok
|
||||
|
||||
# Produce blocks before starting the test
|
||||
env.clMock.produceBlocks(5, BlockProcessCallbacks())
|
||||
testCond env.clMock.produceBlocks(5, BlockProcessCallbacks())
|
||||
|
||||
callbacks = BlockProcessCallbacks(
|
||||
var callbacks = BlockProcessCallbacks(
|
||||
# We send the transactions after we got the Payload ID, before the CLMocker gets the prepared Payload
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
let recipient = EthAddress.randomBytes()
|
||||
_, err = env.sendNextTx(
|
||||
env.clMock.nextBlockProducer,
|
||||
BaseTx(
|
||||
recipient: &recipient,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
),
|
||||
let tc = BaseTx(
|
||||
recipient: some(recipient),
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
if err != nil (
|
||||
fatal "Error trying to send transaction: %v", t.TestName, err)
|
||||
)
|
||||
),
|
||||
let ok = env.sendNextTx(env.clMock.nextBlockProducer, tc)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction"
|
||||
return true
|
||||
)
|
||||
|
||||
reExecFunc = proc(): bool =
|
||||
payloadCount = 80
|
||||
if cs.payloadCount > 0 (
|
||||
let reExecFunc = proc(): bool =
|
||||
var payloadCount = 80
|
||||
if cs.payloadCount > 0:
|
||||
payloadCount = cs.payloadCount
|
||||
)
|
||||
|
||||
basePayload = env.clMock.latestPayloadBuilt
|
||||
let basePayload = env.clMock.latestExecutableData
|
||||
|
||||
# Check that the transaction was included
|
||||
if len(basePayload.Transactions) == 0 (
|
||||
fatal "Client failed to include the expected transaction in payload built", t.TestName)
|
||||
)
|
||||
testCond len(basePayload.basePayload.transactions)> 0:
|
||||
fatal "Client failed to include the expected transaction in payload built"
|
||||
|
||||
# Fabricate and send multiple new payloads by changing the PrevRandao field
|
||||
for i = 0; i < payloadCount; i++ (
|
||||
newPrevRandao = common.Hash256.randomBytes()
|
||||
customizer = CustomPayloadData(
|
||||
prevRandao: &newPrevRandao,
|
||||
for i in 0..<payloadCount:
|
||||
let newPrevRandao = common.Hash256.randomBytes()
|
||||
let customizer = CustomPayloadData(
|
||||
prevRandao: some(newPrevRandao),
|
||||
)
|
||||
newPayload, err = customizePayload(basePayload)
|
||||
if err != nil (
|
||||
fatal "Unable to customize payload %v: %v", t.TestName, i, err)
|
||||
)
|
||||
|
||||
r = env.engine.client.newPayload(newPayload)
|
||||
let newPayload = customizer.customizePayload(basePayload)
|
||||
let version = env.engine.version(newPayload.timestamp)
|
||||
let r = env.engine.client.newPayload(version, newPayload)
|
||||
r.expectStatus(PayloadExecutionStatus.valid)
|
||||
r.expectLatestValidHash(newPayload.blockHash)
|
||||
)
|
||||
)
|
||||
return true
|
||||
|
||||
if cs.SetHeadToFirstPayloadReceived (
|
||||
if cs.setHeadToFirstPayloadReceived:
|
||||
# We are going to set the head of the chain to the first payload executed by the client
|
||||
# Therefore our re-execution function must be executed after the payload was broadcast
|
||||
callbacks.onNewPayloadBroadcast = reExecFunc
|
||||
|
@ -266,15 +262,18 @@ method execute(cs: MultiplePayloadsExtendingCanonicalChainTest, env: TestEnv): b
|
|||
# Otherwise, we execute the payloads after we get the canonical one so it's
|
||||
# executed last
|
||||
callbacks.onGetPayload = reExecFunc
|
||||
)
|
||||
|
||||
env.clMock.produceSingleBlock(callbacks)
|
||||
testCond env.clMock.produceSingleBlock(callbacks)
|
||||
# At the end the CLMocker continues to try to execute fcU with the original payload, which should not fail
|
||||
)
|
||||
return true
|
||||
|
||||
type
|
||||
NewPayloadOnSyncingClientTest* = ref object of EngineSpec
|
||||
|
||||
Shadow2 = ref object
|
||||
recipient: EthAddress
|
||||
previousPayload: ExecutionPayload
|
||||
|
||||
method withMainFork(cs: NewPayloadOnSyncingClientTest, fork: EngineFork): BaseSpec =
|
||||
var res = cs.clone()
|
||||
res.mainFork = fork
|
||||
|
@ -285,98 +284,94 @@ method getName(cs: NewPayloadOnSyncingClientTest): string =
|
|||
|
||||
# Send a valid payload on a client that is currently SYNCING
|
||||
method execute(cs: NewPayloadOnSyncingClientTest, env: TestEnv): bool =
|
||||
var
|
||||
# Set a random transaction recipient
|
||||
let recipient = EthAddress.randomBytes()
|
||||
previousPayload ExecutableData
|
||||
sec = env.addEngine()
|
||||
var shadow = Shadow2(
|
||||
# Set a random transaction recipient
|
||||
recipient: EthAddress.randomBytes(),
|
||||
)
|
||||
|
||||
var sec = env.addEngine()
|
||||
|
||||
# Wait until TTD is reached by all clients
|
||||
let ok = waitFor env.clMock.waitForTTD()
|
||||
testCond ok
|
||||
|
||||
# Produce blocks before starting the test
|
||||
env.clMock.produceBlocks(5, BlockProcessCallbacks())
|
||||
|
||||
|
||||
testCond env.clMock.produceBlocks(5, BlockProcessCallbacks())
|
||||
|
||||
# Disconnect the first engine client from the CL Mocker and produce a block
|
||||
env.clMock.removeEngine(env.engine)
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
var pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
# Send at least one transaction per payload
|
||||
_, err = env.sendNextTx(
|
||||
env.clMock.nextBlockProducer,
|
||||
BaseTx(
|
||||
recipient: &recipient,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
),
|
||||
let tc = BaseTx(
|
||||
recipient: some(shadow.recipient),
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
if err != nil (
|
||||
fatal "Error trying to send transaction: %v", t.TestName, err)
|
||||
)
|
||||
),
|
||||
let ok = env.sendNextTx(env.clMock.nextBlockProducer, tc)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction"
|
||||
return true
|
||||
,
|
||||
onGetPayload: proc(): bool =
|
||||
# Check that the transaction was included
|
||||
if len(env.clMock.latestPayloadBuilt.Transactions) == 0 (
|
||||
fatal "Client failed to include the expected transaction in payload built", t.TestName)
|
||||
)
|
||||
),
|
||||
testCond len(env.clMock.latestPayloadBuilt.transactions) > 0:
|
||||
fatal "Client failed to include the expected transaction in payload built"
|
||||
return true
|
||||
))
|
||||
|
||||
previousPayload = env.clMock.latestPayloadBuilt
|
||||
testCond true
|
||||
shadow.previousPayload = env.clMock.latestPayloadBuilt
|
||||
|
||||
# Send the fcU to set it to syncing mode
|
||||
r = env.engine.client.forkchoiceUpdated(env.clMock.latestForkchoice, nil, env.clMock.latestHeader.Time)
|
||||
let version = env.engine.version(env.clMock.latestHeader.timestamp)
|
||||
let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice)
|
||||
r.expectPayloadStatus(PayloadExecutionStatus.syncing)
|
||||
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
pbREs = env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
# Send at least one transaction per payload
|
||||
_, err = env.sendNextTx(
|
||||
env.clMock.nextBlockProducer,
|
||||
BaseTx(
|
||||
recipient: &recipient,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
),
|
||||
let tc = BaseTx(
|
||||
recipient: some(shadow.recipient),
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
if err != nil (
|
||||
fatal "Error trying to send transaction: %v", t.TestName, err)
|
||||
)
|
||||
),
|
||||
let ok = env.sendNextTx(env.clMock.nextBlockProducer, tc)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction"
|
||||
return true
|
||||
,
|
||||
# Run test after the new payload has been obtained
|
||||
onGetPayload: proc(): bool =
|
||||
# Send the new payload from the second client to the first, it won't be able to validate it
|
||||
r = env.engine.client.newPayload(env.clMock.latestPayloadBuilt)
|
||||
r.expectStatusEither(PayloadExecutionStatus.accepted, PayloadExecutionStatus.syncing)
|
||||
r.expectLatestValidHash(nil)
|
||||
let r = env.engine.client.newPayload(env.clMock.latestPayloadBuilt)
|
||||
r.expectStatusEither([PayloadExecutionStatus.accepted, PayloadExecutionStatus.syncing])
|
||||
r.expectLatestValidHash()
|
||||
|
||||
# Send the forkchoiceUpdated with a reference to the valid payload on the SYNCING client.
|
||||
var (
|
||||
random = common.Hash256()
|
||||
suggestedFeeRecipient = common.Address()
|
||||
var
|
||||
random = w3Hash()
|
||||
suggestedFeeRecipient = w3Address()
|
||||
|
||||
let customizer = BasePayloadAttributesCustomizer(
|
||||
prevRandao: some(ethHash random),
|
||||
suggestedFeerecipient: some(ethAddr suggestedFeeRecipient),
|
||||
)
|
||||
payloadAttributesCustomizer = &BasePayloadAttributesCustomizer(
|
||||
Random: &random,
|
||||
SuggestedFeerecipient: &suggestedFeeRecipient,
|
||||
)
|
||||
newPayloadAttributes, err = payloadAttributesCustomizer.GetPayloadAttributes(env.clMock.latestPayloadAttributes)
|
||||
if err != nil (
|
||||
fatal "Unable to customize payload attributes: %v", t.TestName, err)
|
||||
)
|
||||
s = env.engine.client.forkchoiceUpdated(ForkchoiceStateV1(
|
||||
|
||||
let newAttr = customizer.getPayloadAttributes(env.clMock.latestPayloadAttributes)
|
||||
var fcu = ForkchoiceStateV1(
|
||||
headblockHash: env.clMock.latestPayloadBuilt.blockHash,
|
||||
safeblockHash: env.clMock.latestPayloadBuilt.blockHash,
|
||||
finalizedblockHash: env.clMock.latestPayloadBuilt.blockHash,
|
||||
), newPayloadAttributes, env.clMock.latestPayloadBuilt.timestamp)
|
||||
)
|
||||
|
||||
var version = env.engine.version(env.clMock.latestPayloadBuilt.timestamp)
|
||||
var s = env.engine.client.forkchoiceUpdated(version, fcu, some(newAttr))
|
||||
s.expectPayloadStatus(PayloadExecutionStatus.syncing)
|
||||
|
||||
# Send the previous payload to be able to continue
|
||||
p = env.engine.client.newPayload(previousPayload)
|
||||
var p = env.engine.client.newPayload(shadow.previousPayload)
|
||||
p.expectStatus(PayloadExecutionStatus.valid)
|
||||
p.expectLatestValidHash(previousPayload.blockHash)
|
||||
p.expectLatestValidHash(shadow.previousPayload.blockHash)
|
||||
|
||||
# Send the new payload again
|
||||
|
||||
|
@ -384,14 +379,16 @@ method execute(cs: NewPayloadOnSyncingClientTest, env: TestEnv): bool =
|
|||
p.expectStatus(PayloadExecutionStatus.valid)
|
||||
p.expectLatestValidHash(env.clMock.latestPayloadBuilt.blockHash)
|
||||
|
||||
s = env.engine.client.forkchoiceUpdated(ForkchoiceStateV1(
|
||||
fcu = ForkchoiceStateV1(
|
||||
headblockHash: env.clMock.latestPayloadBuilt.blockHash,
|
||||
safeblockHash: env.clMock.latestPayloadBuilt.blockHash,
|
||||
finalizedblockHash: env.clMock.latestPayloadBuilt.blockHash,
|
||||
), nil, env.clMock.latestPayloadBuilt.timestamp)
|
||||
)
|
||||
version = env.engine.version(env.clMock.latestPayloadBuilt.timestamp)
|
||||
s = env.engine.client.forkchoiceUpdated(version, fcu)
|
||||
s.expectPayloadStatus(PayloadExecutionStatus.valid)
|
||||
|
||||
return true
|
||||
return true
|
||||
))
|
||||
|
||||
testCond pbRes
|
||||
|
@ -415,59 +412,57 @@ method execute(cs: NewPayloadWithMissingFcUTest, env: TestEnv): bool =
|
|||
testCond ok
|
||||
|
||||
# Get last genesis block hash
|
||||
genesisHash = env.engine.client.latestHeader().Header.Hash()
|
||||
let res = env.engine.client.latestHeader()
|
||||
let genesisHash = res.get.blockHash
|
||||
|
||||
# Produce blocks on the main client, these payloads will be replayed on the secondary client.
|
||||
env.clMock.produceBlocks(5, BlockProcessCallbacks(
|
||||
let pbRes = env.clMock.produceBlocks(5, BlockProcessCallbacks(
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
var recipient common.Address
|
||||
randomBytes(recipient[:])
|
||||
let recipient = common.EthAddress.randomBytes()
|
||||
let tc = BaseTx(
|
||||
recipient: some(recipient),
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
# Send at least one transaction per payload
|
||||
_, err = env.sendNextTx(
|
||||
env.clMock.nextBlockProducer,
|
||||
BaseTx(
|
||||
recipient: &recipient,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
),
|
||||
)
|
||||
if err != nil (
|
||||
fatal "Error trying to send transaction: %v", t.TestName, err)
|
||||
)
|
||||
),
|
||||
let ok = env.sendNextTx(env.clMock.nextBlockProducer, tc)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction"
|
||||
return true
|
||||
,
|
||||
onGetPayload: proc(): bool =
|
||||
# Check that the transaction was included
|
||||
if len(env.clMock.latestPayloadBuilt.Transactions) == 0 (
|
||||
fatal "Client failed to include the expected transaction in payload built", t.TestName)
|
||||
)
|
||||
),
|
||||
testCond len(env.clMock.latestPayloadBuilt.transactions) > 0:
|
||||
fatal "Client failed to include the expected transaction in payload built"
|
||||
return true
|
||||
))
|
||||
testCond pbRes
|
||||
|
||||
var sec = env.addEngine()
|
||||
|
||||
let start = env.clMock.firstPoSBlockNumber.get
|
||||
# Send each payload in the correct order but skip the ForkchoiceUpdated for each
|
||||
for i = env.clMock.firstPoSBlockNumber.Uint64(); i <= env.clMock.latestHeadNumber.Uint64(); i++ (
|
||||
payload = env.clMock.executedPayloadHistory[i]
|
||||
p = sec.newPayload(payload)
|
||||
for i in start..env.clMock.latestHeadNumber.uint64:
|
||||
let payload = env.clMock.executedPayloadHistory[i]
|
||||
let p = sec.client.newPayload(payload)
|
||||
p.expectStatus(PayloadExecutionStatus.valid)
|
||||
p.expectLatestValidHash(payload.blockHash)
|
||||
)
|
||||
|
||||
# Verify that at this point, the client's head still points to the last non-PoS block
|
||||
r = sec.latestHeader()
|
||||
let r = sec.client.latestHeader()
|
||||
r.expectHash(genesisHash)
|
||||
|
||||
# Verify that the head correctly changes after the last ForkchoiceUpdated
|
||||
fcU = ForkchoiceStateV1(
|
||||
headblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.Uint64()].blockHash,
|
||||
safeblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.Uint64()-1].blockHash,
|
||||
finalizedblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.Uint64()-2].blockHash,
|
||||
let fcU = ForkchoiceStateV1(
|
||||
headblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.uint64].blockHash,
|
||||
safeblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.uint64-1].blockHash,
|
||||
finalizedblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.uint64-2].blockHash,
|
||||
)
|
||||
p = sec.forkchoiceUpdated(fcU, nil, env.clMock.latestHeader.Time)
|
||||
let version = sec.version(env.clMock.latestHeader.timestamp)
|
||||
let p = sec.client.forkchoiceUpdated(version, fcU)
|
||||
p.expectPayloadStatus(PayloadExecutionStatus.valid)
|
||||
p.expectLatestValidHash(fcU.headblockHash)
|
||||
|
||||
# Now the head should've changed to the latest PoS block
|
||||
s = sec.latestHeader()
|
||||
s.expectHash(fcU.headblockHash)
|
||||
)
|
||||
let s = sec.client.latestHeader()
|
||||
s.expectHash(ethHash fcU.headblockHash)
|
||||
return true
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
# according to those terms.
|
||||
|
||||
import
|
||||
std/strutils,
|
||||
std/[strutils, typetraits],
|
||||
chronicles,
|
||||
./engine_spec
|
||||
|
||||
type
|
||||
|
@ -36,6 +37,16 @@ method withMainFork(cs: UniquePayloadIDTest, fork: EngineFork): BaseSpec =
|
|||
method getName(cs: UniquePayloadIDTest): string =
|
||||
"Unique Payload ID - " & $cs.fieldModification
|
||||
|
||||
func plusOne(x: FixedBytes[32]): FixedBytes[32] =
|
||||
var z = x.bytes
|
||||
z[0] = z[0] + 1.byte
|
||||
FixedBytes[32](z)
|
||||
|
||||
func plusOne(x: Address): Address =
|
||||
var z = distinctBase x
|
||||
z[0] = z[0] + 1.byte
|
||||
Address(z)
|
||||
|
||||
# Check that the payload id returned on a forkchoiceUpdated call is different
|
||||
# when the attributes change
|
||||
method execute(cs: UniquePayloadIDTest, env: TestEnv): bool =
|
||||
|
@ -43,56 +54,63 @@ method execute(cs: UniquePayloadIDTest, env: TestEnv): bool =
|
|||
let ok = waitFor env.clMock.waitForTTD()
|
||||
testCond ok
|
||||
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
let pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
onPayloadAttributesGenerated: proc(): bool =
|
||||
payloadAttributes = env.clMock.latestPayloadAttributes
|
||||
case cs.fieldModification (
|
||||
var attr = env.clMock.latestPayloadAttributes
|
||||
case cs.fieldModification
|
||||
of PayloadAttributesIncreasetimestamp:
|
||||
payloadAttributes.timestamp += 1
|
||||
attr.timestamp = w3Qty(attr.timestamp, 1)
|
||||
of PayloadAttributesRandom:
|
||||
payloadAttributes.Random[0] = payloadAttributes.Random[0] + 1
|
||||
attr.prevRandao = attr.prevRandao.plusOne
|
||||
of PayloadAttributesSuggestedFeerecipient:
|
||||
payloadAttributes.SuggestedFeeRecipient[0] = payloadAttributes.SuggestedFeeRecipient[0] + 1
|
||||
attr.suggestedFeeRecipient = attr.suggestedFeeRecipient.plusOne
|
||||
of PayloadAttributesAddWithdrawal:
|
||||
newWithdrawal = &types.Withdrawal()
|
||||
payloadAttributes.Withdrawals = append(payloadAttributes.Withdrawals, newWithdrawal)
|
||||
let newWithdrawal = WithdrawalV1()
|
||||
var wd = attr.withdrawals.get
|
||||
wd.add newWithdrawal
|
||||
attr.withdrawals = some(wd)
|
||||
of PayloadAttributesRemoveWithdrawal:
|
||||
payloadAttributes.Withdrawals = payloadAttributes.Withdrawals[1:]
|
||||
var wd = attr.withdrawals.get
|
||||
wd.delete(0)
|
||||
attr.withdrawals = some(wd)
|
||||
of PayloadAttributesModifyWithdrawalAmount,
|
||||
PayloadAttributesModifyWithdrawalIndex,
|
||||
PayloadAttributesModifyWithdrawalValidator,
|
||||
PayloadAttributesModifyWithdrawalAddress:
|
||||
if len(payloadAttributes.Withdrawals) == 0 (
|
||||
fatal "Cannot modify withdrawal when there are no withdrawals")
|
||||
)
|
||||
modifiedWithdrawal = *payloadAttributes.Withdrawals[0]
|
||||
case cs.fieldModification (
|
||||
testCond attr.withdrawals.isSome:
|
||||
fatal "Cannot modify withdrawal when there are no withdrawals"
|
||||
var wds = attr.withdrawals.get
|
||||
testCond wds.len > 0:
|
||||
fatal "Cannot modify withdrawal when there are no withdrawals"
|
||||
|
||||
var wd = wds[0]
|
||||
case cs.fieldModification
|
||||
of PayloadAttributesModifyWithdrawalAmount:
|
||||
modifiedWithdrawal.Amount += 1
|
||||
wd.amount = w3Qty(wd.amount, 1)
|
||||
of PayloadAttributesModifyWithdrawalIndex:
|
||||
modifiedWithdrawal.Index += 1
|
||||
wd.index = w3Qty(wd.index, 1)
|
||||
of PayloadAttributesModifyWithdrawalValidator:
|
||||
modifiedWithdrawal.Validator += 1
|
||||
wd.validatorIndex = w3Qty(wd.validatorIndex, 1)
|
||||
of PayloadAttributesModifyWithdrawalAddress:
|
||||
modifiedWithdrawal.Address[0] = modifiedWithdrawal.Address[0] + 1
|
||||
)
|
||||
payloadAttributes.Withdrawals = append(types.Withdrawals(&modifiedWithdrawal), payloadAttributes.Withdrawals[1:]...)
|
||||
wd.address = wd.address.plusOne
|
||||
else:
|
||||
fatal "Unknown field change", field=cs.fieldModification
|
||||
return false
|
||||
|
||||
wds[0] = wd
|
||||
attr.withdrawals = some(wds)
|
||||
of PayloadAttributesParentBeaconRoot:
|
||||
if payloadAttributes.BeaconRoot == nil (
|
||||
fatal "Cannot modify parent beacon root when there is no parent beacon root")
|
||||
)
|
||||
newBeaconRoot = *payloadAttributes.BeaconRoot
|
||||
newBeaconRoot[0] = newBeaconRoot[0] + 1
|
||||
payloadAttributes.BeaconRoot = &newBeaconRoot
|
||||
default:
|
||||
fatal "Unknown field change: %s", cs.fieldModification)
|
||||
)
|
||||
testCond attr.parentBeaconBlockRoot.isSome:
|
||||
fatal "Cannot modify parent beacon root when there is no parent beacon root"
|
||||
let newBeaconRoot = attr.parentBeaconBlockRoot.get.plusOne
|
||||
attr.parentBeaconBlockRoot = some(newBeaconRoot)
|
||||
|
||||
# Request the payload with the modified attributes and add the payload ID to the list of known IDs
|
||||
let version = env.engine.version(env.clMock.latestHeader.timestamp)
|
||||
let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice, some(payloadAttributes))
|
||||
let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice, some(attr))
|
||||
r.expectNoError()
|
||||
env.clMock.addPayloadID(env.engine, r.get.payloadID.get)
|
||||
),
|
||||
testCond env.clMock.addPayloadID(env.engine, r.get.payloadID.get)
|
||||
return true
|
||||
))
|
||||
)
|
||||
testCond pbRes
|
||||
return true
|
||||
|
|
|
@ -12,7 +12,8 @@ import
|
|||
std/strutils,
|
||||
eth/common,
|
||||
chronicles,
|
||||
./engine_spec
|
||||
./engine_spec,
|
||||
../helper
|
||||
|
||||
type
|
||||
PrevRandaoTransactionTest* = ref object of EngineSpec
|
||||
|
@ -24,13 +25,6 @@ type
|
|||
currentTxIndex: int
|
||||
txs: seq[Transaction]
|
||||
|
||||
proc checkPrevRandaoValue(client: RpcClient, expectedPrevRandao: common.Hash256, blockNumber: uint64): bool =
|
||||
let storageKey = blockNumber.u256
|
||||
let r = client.storageAt(prevRandaoContractAddr, storageKey)
|
||||
let expected = UInt256.fromBytesBE(expectedPrevRandao.data)
|
||||
r.expectStorageEqual(expected)
|
||||
return true
|
||||
|
||||
method withMainFork(cs: PrevRandaoTransactionTest, fork: EngineFork): BaseSpec =
|
||||
var res = cs.clone()
|
||||
res.mainFork = fork
|
||||
|
|
|
@ -10,9 +10,11 @@
|
|||
|
||||
import
|
||||
std/strutils,
|
||||
eth/common,
|
||||
chronicles,
|
||||
./engine_spec,
|
||||
../cancun/customizer
|
||||
../cancun/customizer,
|
||||
../helper
|
||||
|
||||
type
|
||||
SidechainReOrgTest* = ref object of EngineSpec
|
||||
|
@ -37,79 +39,74 @@ method execute(cs: SidechainReOrgTest, env: TestEnv): bool =
|
|||
# Produce two payloads, send fcU with first payload, check transaction outcome, then reorg, check transaction outcome again
|
||||
|
||||
# This single transaction will change its outcome based on the payload
|
||||
tx, err = t.sendNextTx(
|
||||
t.TestContext,
|
||||
t.Engine,
|
||||
BaseTx(
|
||||
recipient: &globals.PrevRandaoContractAddr,
|
||||
amount: big0,
|
||||
payload: nil,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
),
|
||||
let tc = BaseTx(
|
||||
recipient: some(prevRandaoContractAddr),
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
if err != nil (
|
||||
fatal "Error trying to send transaction: %v", t.TestName, err)
|
||||
)
|
||||
info "sent tx %v", t.TestName, tx.Hash())
|
||||
let ok2 = env.sendNextTx(env.engine, tc)
|
||||
testCond ok2:
|
||||
fatal "Error trying to send transaction"
|
||||
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
info "sent tx"
|
||||
|
||||
let pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
onNewPayloadBroadcast: proc(): bool =
|
||||
# At this point the CLMocker has a payload that will result in a specific outcome,
|
||||
# we can produce an alternative payload, send it, fcU to it, and verify the changes
|
||||
alternativePrevRandao = common.Hash()
|
||||
rand.Read(alternativePrevRandao[:])
|
||||
timestamp = env.clMock.latestPayloadBuilt.timestamp + 1
|
||||
payloadAttributes, err = (BasePayloadAttributesCustomizer(
|
||||
Timestamp: ×tamp,
|
||||
Random: &alternativePrevRandao,
|
||||
)).getPayloadAttributes(env.clMock.LatestPayloadAttributes)
|
||||
if err != nil (
|
||||
fatal "Unable to customize payload attributes: %v", t.TestName, err)
|
||||
let alternativePrevRandao = common.Hash256.randomBytes()
|
||||
let timestamp = w3Qty(env.clMock.latestPayloadBuilt.timestamp, 1)
|
||||
let customizer = BasePayloadAttributesCustomizer(
|
||||
timestamp: some(timestamp.uint64),
|
||||
prevRandao: some(alternativePrevRandao),
|
||||
)
|
||||
|
||||
r = env.engine.client.forkchoiceUpdated(
|
||||
&env.clMock.latestForkchoice,
|
||||
payloadAttributes,
|
||||
env.clMock.latestPayloadBuilt.timestamp,
|
||||
)
|
||||
let attr = customizer.getPayloadAttributes(env.clMock.latestPayloadAttributes)
|
||||
|
||||
var version = env.engine.version(env.clMock.latestPayloadBuilt.timestamp)
|
||||
let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice, some(attr))
|
||||
r.expectNoError()
|
||||
|
||||
await sleepAsync(env.clMock.PayloadProductionClientDelay)
|
||||
let period = chronos.seconds(env.clMock.payloadProductionClientDelay)
|
||||
waitFor sleepAsync(period)
|
||||
|
||||
g = env.engine.client.getPayload(r.Response.PayloadID, payloadAttributes)
|
||||
version = env.engine.version(attr.timestamp)
|
||||
let g = env.engine.client.getPayload(r.get.payloadID.get, version)
|
||||
g.expectNoError()
|
||||
alternativePayload = g.Payload
|
||||
if len(alternativePayload.Transactions) == 0 (
|
||||
fatal "alternative payload does not contain the prevRandao opcode tx", t.TestName)
|
||||
)
|
||||
|
||||
s = env.engine.client.newPayload(alternativePayload)
|
||||
let alternativePayload = g.get.executionPayload
|
||||
testCond len(alternativePayload.transactions) > 0:
|
||||
fatal "alternative payload does not contain the prevRandao opcode tx"
|
||||
|
||||
let s = env.engine.client.newPayload(alternativePayload)
|
||||
s.expectStatus(PayloadExecutionStatus.valid)
|
||||
s.expectLatestValidHash(alternativePayload.blockHash)
|
||||
|
||||
# We sent the alternative payload, fcU to it
|
||||
p = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1(
|
||||
let fcu = ForkchoiceStateV1(
|
||||
headBlockHash: alternativePayload.blockHash,
|
||||
safeBlockHash: env.clMock.latestForkchoice.safeBlockHash,
|
||||
finalizedBlockHash: env.clMock.latestForkchoice.finalizedBlockHash,
|
||||
), nil, alternativePayload.timestamp)
|
||||
)
|
||||
version = env.engine.version(alternativePayload.timestamp)
|
||||
let p = env.engine.client.forkchoiceUpdated(version, fcu)
|
||||
p.expectPayloadStatus(PayloadExecutionStatus.valid)
|
||||
|
||||
# PrevRandao should be the alternative prevRandao we sent
|
||||
checkPrevRandaoValue(t, alternativePrevRandao, alternativePayload.blockNumber)
|
||||
),
|
||||
testCond checkPrevRandaoValue(env.engine.client, alternativePrevRandao, alternativePayload.blockNumber.uint64)
|
||||
return true
|
||||
))
|
||||
testCond pbRes
|
||||
# The reorg actually happens after the CLMocker continues,
|
||||
# verify here that the reorg was successful
|
||||
latestBlockNum = env.clMock.LatestHeadNumber.Uint64()
|
||||
checkPrevRandaoValue(t, env.clMock.PrevRandaoHistory[latestBlockNum], latestBlockNum)
|
||||
|
||||
)
|
||||
let latestBlockNum = env.clMock.latestHeadNumber.uint64
|
||||
testCond checkPrevRandaoValue(env.engine.client, env.clMock.prevRandaoHistory[latestBlockNum], latestBlockNum)
|
||||
return true
|
||||
|
||||
# Test performing a re-org that involves removing or modifying a transaction
|
||||
type
|
||||
TransactionReOrgScenario = enum
|
||||
TransactionNoScenario
|
||||
TransactionReOrgScenarioReOrgOut = "Re-Org Out"
|
||||
TransactionReOrgScenarioReOrgBackIn = "Re-Org Back In"
|
||||
TransactionReOrgScenarioReOrgDifferentBlock = "Re-Org to Different Block"
|
||||
|
@ -117,8 +114,8 @@ type
|
|||
|
||||
type
|
||||
TransactionReOrgTest* = ref object of EngineSpec
|
||||
TransactionCount int
|
||||
Scenario TransactionReOrgScenario
|
||||
transactionCount*: int
|
||||
scenario*: TransactionReOrgScenario
|
||||
|
||||
method withMainFork(cs: TransactionReOrgTest, fork: EngineFork): BaseSpec =
|
||||
var res = cs.clone()
|
||||
|
@ -126,10 +123,9 @@ method withMainFork(cs: TransactionReOrgTest, fork: EngineFork): BaseSpec =
|
|||
return res
|
||||
|
||||
method getName(cs: TransactionReOrgTest): string =
|
||||
name = "Transaction Re-Org"
|
||||
if s.Scenario != "" (
|
||||
name = fmt.Sprintf("%s, %s", name, s.Scenario)
|
||||
)
|
||||
var name = "Transaction Re-Org"
|
||||
if cs.scenario != TransactionNoScenario:
|
||||
name.add ", " & $cs.scenario
|
||||
return name
|
||||
|
||||
# Test transaction status after a forkchoiceUpdated re-orgs to an alternative hash where a transaction is not present
|
||||
|
@ -140,11 +136,11 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
|
||||
# Produce blocks before starting the test (So we don't try to reorg back to the genesis block)
|
||||
testCond env.clMock.produceBlocks(5, BlockProcessCallbacks())
|
||||
|
||||
#[
|
||||
# Create transactions that modify the state in order to check after the reorg.
|
||||
var (
|
||||
err error
|
||||
txCount = spec.TransactionCount
|
||||
txCount = cs.transactionCount
|
||||
sstoreContractAddr = common.HexToAddress("0000000000000000000000000000000000000317")
|
||||
)
|
||||
|
||||
|
@ -158,7 +154,6 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
data = common.LeftPadBytes([]byte(byte(i)), 32)
|
||||
t.Logf("transactionReorg, i=%v, data=%v\n", i, data)
|
||||
return t.sendNextTx(
|
||||
t.TestContext,
|
||||
t.Engine,
|
||||
BaseTx(
|
||||
recipient: &sstoreContractAddr,
|
||||
|
@ -173,7 +168,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
)
|
||||
|
||||
var (
|
||||
altPayload ExecutableData
|
||||
shadow.payload ExecutableData
|
||||
nextTx Transaction
|
||||
tx Transaction
|
||||
)
|
||||
|
@ -184,7 +179,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
OnPayloadAttributesGenerated: proc(): bool =
|
||||
# At this point we have not broadcast the transaction.
|
||||
if spec.Scenario == TransactionReOrgScenarioReOrgOut (
|
||||
if cs.scenario == TransactionReOrgScenarioReOrgOut (
|
||||
# Any payload we get should not contain any
|
||||
payloadAttributes = env.clMock.LatestPayloadAttributes
|
||||
rand.Read(payloadAttributes.Random[:])
|
||||
|
@ -195,25 +190,25 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
)
|
||||
g = env.engine.client.getPayload(r.Response.PayloadID, payloadAttributes)
|
||||
g.expectNoError()
|
||||
altPayload = &g.Payload
|
||||
shadow.payload = &g.Payload
|
||||
|
||||
if len(altPayload.Transactions) != 0 (
|
||||
fatal "Empty payload contains transactions: %v", t.TestName, altPayload)
|
||||
if len(shadow.payload.transactions) != 0 (
|
||||
fatal "Empty payload contains transactions: %v", t.TestName, shadow.payload)
|
||||
)
|
||||
)
|
||||
|
||||
if spec.Scenario != TransactionReOrgScenarioReOrgBackIn (
|
||||
if cs.scenario != TransactionReOrgScenarioReOrgBackIn (
|
||||
# At this point we can broadcast the transaction and it will be included in the next payload
|
||||
# Data is the key where a `1` will be stored
|
||||
tx, err = sendTransaction(i)
|
||||
if err != nil (
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction: %v", t.TestName, err)
|
||||
)
|
||||
|
||||
# Get the receipt
|
||||
ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout)
|
||||
defer cancel()
|
||||
receipt, _ = t.Eth.TransactionReceipt(ctx, tx.Hash())
|
||||
receipt, _ = t.Eth.TransactionReceipt(ctx, shadow.txHash)
|
||||
if receipt != nil (
|
||||
fatal "Receipt obtained before tx included in block: %v", t.TestName, receipt)
|
||||
)
|
||||
|
@ -222,36 +217,36 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
),
|
||||
onGetpayload: proc(): bool =
|
||||
# Check that indeed the payload contains the transaction
|
||||
if tx != nil (
|
||||
if shadow.tx.isSome:
|
||||
if !TransactionInPayload(env.clMock.latestPayloadBuilt, tx) (
|
||||
fatal "Payload built does not contain the transaction: %v", t.TestName, env.clMock.latestPayloadBuilt)
|
||||
)
|
||||
)
|
||||
|
||||
if spec.Scenario == TransactionReOrgScenarioReOrgDifferentBlock || spec.Scenario == TransactionReOrgScenarioNewPayloadOnRevert (
|
||||
if cs.scenario == TransactionReOrgScenarioReOrgDifferentBlock || cs.scenario == TransactionReOrgScenarioNewPayloadOnRevert (
|
||||
# Create side payload with different hash
|
||||
var err error
|
||||
customizer = &CustomPayloadData(
|
||||
extraData: &([]byte(0x01)),
|
||||
)
|
||||
altPayload, err = customizer.customizePayload(env.clMock.latestPayloadBuilt)
|
||||
if err != nil (
|
||||
shadow.payload, err = customizer.customizePayload(env.clMock.latestPayloadBuilt)
|
||||
testCond ok:
|
||||
fatal "Error creating reorg payload %v", err)
|
||||
)
|
||||
|
||||
if altPayload.parentHash != env.clMock.latestPayloadBuilt.parentHash (
|
||||
fatal "Incorrect parent hash for payloads: %v != %v", t.TestName, altPayload.parentHash, env.clMock.latestPayloadBuilt.parentHash)
|
||||
if shadow.payload.parentHash != env.clMock.latestPayloadBuilt.parentHash (
|
||||
fatal "Incorrect parent hash for payloads: %v != %v", t.TestName, shadow.payload.parentHash, env.clMock.latestPayloadBuilt.parentHash)
|
||||
)
|
||||
if altPayload.blockHash == env.clMock.latestPayloadBuilt.blockHash (
|
||||
fatal "Incorrect hash for payloads: %v == %v", t.TestName, altPayload.blockHash, env.clMock.latestPayloadBuilt.blockHash)
|
||||
if shadow.payload.blockHash == env.clMock.latestPayloadBuilt.blockHash (
|
||||
fatal "Incorrect hash for payloads: %v == %v", t.TestName, shadow.payload.blockHash, env.clMock.latestPayloadBuilt.blockHash)
|
||||
)
|
||||
elif spec.Scenario == TransactionReOrgScenarioReOrgBackIn (
|
||||
elif cs.scenario == TransactionReOrgScenarioReOrgBackIn (
|
||||
# At this point we broadcast the transaction and request a new payload from the client that must
|
||||
# contain the transaction.
|
||||
# Since we are re-orging out and back in on the next block, the verification of this transaction
|
||||
# being included happens on the next block
|
||||
nextTx, err = sendTransaction(i)
|
||||
if err != nil (
|
||||
shadow.nextTx, err = sendTransaction(i)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction: %v", t.TestName, err)
|
||||
)
|
||||
|
||||
|
@ -275,7 +270,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
g = env.engine.client.getPayload(f.Response.PayloadID, env.clMock.LatestPayloadAttributes)
|
||||
g.expectNoError()
|
||||
|
||||
if !TransactionInPayload(g.Payload, nextTx) (
|
||||
if !TransactionInPayload(g.Payload, shadow.nextTx) (
|
||||
fatal "Payload built does not contain the transaction: %v", t.TestName, g.Payload)
|
||||
)
|
||||
|
||||
|
@ -291,59 +286,59 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
)
|
||||
),
|
||||
onNewPayloadBroadcast: proc(): bool =
|
||||
if tx != nil (
|
||||
if shadow.tx.isSome:
|
||||
# Get the receipt
|
||||
ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout)
|
||||
defer cancel()
|
||||
receipt, _ = t.Eth.TransactionReceipt(ctx, tx.Hash())
|
||||
receipt, _ = t.Eth.TransactionReceipt(ctx, shadow.txHash)
|
||||
if receipt != nil (
|
||||
fatal "Receipt obtained before tx included in block (NewPayload): %v", t.TestName, receipt)
|
||||
)
|
||||
)
|
||||
),
|
||||
onForkchoiceBroadcast: proc(): bool =
|
||||
if spec.Scenario != TransactionReOrgScenarioReOrgBackIn (
|
||||
if cs.scenario != TransactionReOrgScenarioReOrgBackIn (
|
||||
# Transaction is now in the head of the canonical chain, re-org and verify it's removed
|
||||
# Get the receipt
|
||||
txt = env.engine.client.txReceipt(tx.Hash())
|
||||
txt = env.engine.client.txReceipt(shadow.txHash)
|
||||
txt.expectBlockHash(env.clMock.latestForkchoice.headBlockHash)
|
||||
|
||||
if altPayload.parentHash != env.clMock.latestPayloadBuilt.parentHash (
|
||||
fatal "Incorrect parent hash for payloads: %v != %v", t.TestName, altPayload.parentHash, env.clMock.latestPayloadBuilt.parentHash)
|
||||
if shadow.payload.parentHash != env.clMock.latestPayloadBuilt.parentHash (
|
||||
fatal "Incorrect parent hash for payloads: %v != %v", t.TestName, shadow.payload.parentHash, env.clMock.latestPayloadBuilt.parentHash)
|
||||
)
|
||||
if altPayload.blockHash == env.clMock.latestPayloadBuilt.blockHash (
|
||||
fatal "Incorrect hash for payloads: %v == %v", t.TestName, altPayload.blockHash, env.clMock.latestPayloadBuilt.blockHash)
|
||||
if shadow.payload.blockHash == env.clMock.latestPayloadBuilt.blockHash (
|
||||
fatal "Incorrect hash for payloads: %v == %v", t.TestName, shadow.payload.blockHash, env.clMock.latestPayloadBuilt.blockHash)
|
||||
)
|
||||
|
||||
if altPayload == nil (
|
||||
if shadow.payload == nil (
|
||||
fatal "No payload to re-org to", t.TestName)
|
||||
)
|
||||
r = env.engine.client.newPayload(altPayload)
|
||||
r = env.engine.client.newPayload(shadow.payload)
|
||||
r.expectStatus(PayloadExecutionStatus.valid)
|
||||
r.expectLatestValidHash(altPayload.blockHash)
|
||||
r.expectLatestValidHash(shadow.payload.blockHash)
|
||||
|
||||
s = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1(
|
||||
headBlockHash: altPayload.blockHash,
|
||||
headBlockHash: shadow.payload.blockHash,
|
||||
safeBlockHash: env.clMock.latestForkchoice.safeBlockHash,
|
||||
finalizedBlockHash: env.clMock.latestForkchoice.finalizedBlockHash,
|
||||
), nil, altPayload.timestamp)
|
||||
), nil, shadow.payload.timestamp)
|
||||
s.expectPayloadStatus(PayloadExecutionStatus.valid)
|
||||
|
||||
p = env.engine.client.headerByNumber(Head)
|
||||
p.expectHash(altPayload.blockHash)
|
||||
p.expectHash(shadow.payload.blockHash)
|
||||
|
||||
txt = env.engine.client.txReceipt(tx.Hash())
|
||||
if spec.Scenario == TransactionReOrgScenarioReOrgOut (
|
||||
txt = env.engine.client.txReceipt(shadow.txHash)
|
||||
if cs.scenario == TransactionReOrgScenarioReOrgOut (
|
||||
if txt.Receipt != nil (
|
||||
receiptJson, _ = json.MarshalIndent(txt.Receipt, "", " ")
|
||||
fatal "Receipt was obtained when the tx had been re-org'd out: %s", t.TestName, receiptJson)
|
||||
)
|
||||
elif spec.Scenario == TransactionReOrgScenarioReOrgDifferentBlock || spec.Scenario == TransactionReOrgScenarioNewPayloadOnRevert (
|
||||
txt.expectBlockHash(altPayload.blockHash)
|
||||
elif cs.scenario == TransactionReOrgScenarioReOrgDifferentBlock || cs.scenario == TransactionReOrgScenarioNewPayloadOnRevert (
|
||||
txt.expectBlockHash(shadow.payload.blockHash)
|
||||
)
|
||||
|
||||
# Re-org back
|
||||
if spec.Scenario == TransactionReOrgScenarioNewPayloadOnRevert (
|
||||
if cs.scenario == TransactionReOrgScenarioNewPayloadOnRevert (
|
||||
r = env.engine.client.newPayload(env.clMock.latestPayloadBuilt)
|
||||
r.expectStatus(PayloadExecutionStatus.valid)
|
||||
r.expectLatestValidHash(env.clMock.latestPayloadBuilt.blockHash)
|
||||
|
@ -351,21 +346,21 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
env.clMock.BroadcastForkchoiceUpdated(env.clMock.latestForkchoice, nil, 1)
|
||||
)
|
||||
|
||||
if tx != nil (
|
||||
if shadow.tx.isSome:
|
||||
# Now it should be back with main payload
|
||||
txt = env.engine.client.txReceipt(tx.Hash())
|
||||
txt = env.engine.client.txReceipt(shadow.txHash)
|
||||
txt.expectBlockHash(env.clMock.latestForkchoice.headBlockHash)
|
||||
|
||||
if spec.Scenario != TransactionReOrgScenarioReOrgBackIn (
|
||||
if cs.scenario != TransactionReOrgScenarioReOrgBackIn (
|
||||
tx = nil
|
||||
)
|
||||
)
|
||||
|
||||
if spec.Scenario == TransactionReOrgScenarioReOrgBackIn && i > 0 (
|
||||
if cs.scenario == TransactionReOrgScenarioReOrgBackIn && i > 0 (
|
||||
# Reasoning: Most of the clients do not re-add blob transactions to the pool
|
||||
# after a re-org, so we need to wait until the next tx is sent to actually
|
||||
# verify.
|
||||
tx = nextTx
|
||||
tx = shadow.nextTx
|
||||
)
|
||||
|
||||
),
|
||||
|
@ -373,7 +368,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
|
||||
)
|
||||
|
||||
if tx != nil (
|
||||
if shadow.tx.isSome:
|
||||
# Produce one last block and verify that the block contains the transaction
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
onForkchoiceBroadcast: proc(): bool =
|
||||
|
@ -383,7 +378,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
# Get the receipt
|
||||
ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout)
|
||||
defer cancel()
|
||||
receipt, _ = t.Eth.TransactionReceipt(ctx, tx.Hash())
|
||||
receipt, _ = t.Eth.TransactionReceipt(ctx, shadow.txHash)
|
||||
if receipt == nil (
|
||||
fatal "Receipt not obtained after tx included in block: %v", t.TestName, receipt)
|
||||
)
|
||||
|
@ -393,17 +388,18 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
|
|||
)
|
||||
|
||||
)
|
||||
]#
|
||||
|
||||
# Test that performing a re-org back into a previous block of the canonical chain does not produce errors and the chain
|
||||
# is still capable of progressing.
|
||||
type
|
||||
ReOrgBackToCanonicalTest* = ref object of EngineSpec
|
||||
# Depth of the re-org to back in the canonical chain
|
||||
ReOrgDepth uint64
|
||||
reOrgDepth*: int
|
||||
# Number of transactions to send on each payload
|
||||
TransactionPerPayload uint64
|
||||
transactionPerPayload*: int
|
||||
# Whether to execute a sidechain payload on the re-org
|
||||
ExecuteSidePayloadOnReOrg bool
|
||||
executeSidePayloadOnReOrg*: bool
|
||||
|
||||
method withMainFork(cs: ReOrgBackToCanonicalTest, fork: EngineFork): BaseSpec =
|
||||
var res = cs.clone()
|
||||
|
@ -411,19 +407,16 @@ method withMainFork(cs: ReOrgBackToCanonicalTest, fork: EngineFork): BaseSpec =
|
|||
return res
|
||||
|
||||
method getName(cs: ReOrgBackToCanonicalTest): string =
|
||||
name = fmt.Sprintf("Re-Org Back into Canonical Chain, Depth=%d", s.ReOrgDepth)
|
||||
var name = "Re-Org Back into Canonical Chain, Depth=" & $cs.reOrgDepth
|
||||
|
||||
if s.ExecuteSidePayloadOnReOrg (
|
||||
name += ", Execute Side Payload on Re-Org"
|
||||
)
|
||||
if cs.executeSidePayloadOnReOrg:
|
||||
name.add ", Execute Side Payload on Re-Org"
|
||||
return name
|
||||
|
||||
proc getDepth(cs: ReOrgBackToCanonicalTest): int =
|
||||
if s.ReOrgDepth == 0 (
|
||||
if cs.reOrgDepth == 0:
|
||||
return 3
|
||||
)
|
||||
return s.ReOrgDepth
|
||||
)
|
||||
return cs.reOrgDepth
|
||||
|
||||
# Test that performing a re-org back into a previous block of the canonical chain does not produce errors and the chain
|
||||
# is still capable of progressing.
|
||||
|
@ -433,25 +426,25 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool =
|
|||
testCond ok
|
||||
|
||||
# Check the CLMock configured safe and finalized
|
||||
if env.clMock.slotsToSafe.Cmp(new(big.Int).SetUint64(spec.ReOrgDepth)) <= 0 (
|
||||
fatal "[TEST ISSUE] CLMock configured slots to safe less than re-org depth: %v <= %v", t.TestName, env.clMock.slotsToSafe, spec.ReOrgDepth)
|
||||
)
|
||||
if env.clMock.slotsToFinalized.Cmp(new(big.Int).SetUint64(spec.ReOrgDepth)) <= 0 (
|
||||
fatal "[TEST ISSUE] CLMock configured slots to finalized less than re-org depth: %v <= %v", t.TestName, env.clMock.slotsToFinalized, spec.ReOrgDepth)
|
||||
)
|
||||
testCond env.clMock.slotsToSafe > cs.reOrgDepth:
|
||||
fatal "[TEST ISSUE] CLMock configured slots to safe less than re-org depth"
|
||||
|
||||
testCond env.clMock.slotsToFinalized > cs.reOrgDepth:
|
||||
fatal "[TEST ISSUE] CLMock configured slots to finalized less than re-org depth"
|
||||
|
||||
# Produce blocks before starting the test (So we don't try to reorg back to the genesis block)
|
||||
testCond env.clMock.produceBlocks(5, BlockProcessCallbacks())
|
||||
|
||||
#[
|
||||
# We are going to reorg back to a previous hash several times
|
||||
previousHash = env.clMock.latestForkchoice.headBlockHash
|
||||
previousTimestamp = env.clMock.latestPayloadBuilt.timestamp
|
||||
|
||||
if spec.ExecuteSidePayloadOnReOrg (
|
||||
if cs.executeSidePayloadOnReOrg (
|
||||
var (
|
||||
sidePayload ExecutableData
|
||||
sidePayloadParentForkchoice api.ForkchoiceStateV1
|
||||
sidePayloadParentTimestamp uint64
|
||||
shadow.payload ExecutableData
|
||||
shadow.parentForkchoice api.ForkchoiceStateV1
|
||||
shadow.parentTimestamp uint64
|
||||
)
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
OnPayloadAttributesGenerated: proc(): bool =
|
||||
|
@ -464,13 +457,13 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool =
|
|||
)
|
||||
g = env.engine.client.getPayload(r.Response.PayloadID, payloadAttributes)
|
||||
g.expectNoError()
|
||||
sidePayload = &g.Payload
|
||||
sidePayloadParentForkchoice = env.clMock.latestForkchoice
|
||||
sidePayloadParentTimestamp = env.clMock.latestHeader.Time
|
||||
shadow.payload = &g.Payload
|
||||
shadow.parentForkchoice = env.clMock.latestForkchoice
|
||||
shadow.parentTimestamp = env.clMock.latestHeader.Time
|
||||
),
|
||||
))
|
||||
# Continue producing blocks until we reach the depth of the re-org
|
||||
testCond env.clMock.produceBlocks(int(spec.GetDepth()-1), BlockProcessCallbacks(
|
||||
testCond env.clMock.produceBlocks(int(cs.GetDepth()-1), BlockProcessCallbacks(
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
# Send a transaction on each payload of the canonical chain
|
||||
var err error
|
||||
|
@ -485,9 +478,9 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool =
|
|||
gasLimit: 75000,
|
||||
ForkConfig: t.ForkConfig,
|
||||
),
|
||||
spec.TransactionPerPayload,
|
||||
cs.transactionPerPayload,
|
||||
)
|
||||
if err != nil (
|
||||
testCond ok:
|
||||
fatal "Error trying to send transactions: %v", t.TestName, err)
|
||||
)
|
||||
),
|
||||
|
@ -498,19 +491,19 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool =
|
|||
onGetpayload: proc(): bool =
|
||||
# We are about to execute the new payload of the canonical chain, re-org back to
|
||||
# the side payload
|
||||
f = env.engine.client.forkchoiceUpdated(sidePayloadParentForkchoice, nil, sidePayloadParentTimestamp)
|
||||
f = env.engine.client.forkchoiceUpdated(shadow.parentForkchoice, nil, shadow.parentTimestamp)
|
||||
f.expectPayloadStatus(PayloadExecutionStatus.valid)
|
||||
f.expectLatestValidHash(sidePayloadParentForkchoice.headBlockHash)
|
||||
f.expectLatestValidHash(shadow.parentForkchoice.headBlockHash)
|
||||
# Execute the side payload
|
||||
n = env.engine.client.newPayload(sidePayload)
|
||||
n = env.engine.client.newPayload(shadow.payload)
|
||||
n.expectStatus(PayloadExecutionStatus.valid)
|
||||
n.expectLatestValidHash(sidePayload.blockHash)
|
||||
n.expectLatestValidHash(shadow.payload.blockHash)
|
||||
# At this point the next canonical payload will be executed by the CL mock, so we can
|
||||
# continue producing blocks
|
||||
),
|
||||
))
|
||||
else:
|
||||
testCond env.clMock.produceBlocks(int(spec.GetDepth()), BlockProcessCallbacks(
|
||||
testCond env.clMock.produceBlocks(int(cs.GetDepth()), BlockProcessCallbacks(
|
||||
onForkchoiceBroadcast: proc(): bool =
|
||||
# Send a fcU with the headBlockHash pointing back to the previous block
|
||||
forkchoiceUpdatedBack = api.ForkchoiceStateV1(
|
||||
|
@ -533,21 +526,22 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool =
|
|||
# Verify that the client is pointing to the latest payload sent
|
||||
r = env.engine.client.headerByNumber(Head)
|
||||
r.expectHash(env.clMock.latestPayloadBuilt.blockHash)
|
||||
|
||||
)
|
||||
]#
|
||||
|
||||
type
|
||||
ReOrgBackFromSyncingTest* = ref object of EngineSpec
|
||||
|
||||
Shadow = ref object
|
||||
payloads: seq[ExecutableData]
|
||||
|
||||
method withMainFork(cs: ReOrgBackFromSyncingTest, fork: EngineFork): BaseSpec =
|
||||
var res = cs.clone()
|
||||
res.mainFork = fork
|
||||
return res
|
||||
|
||||
method getName(cs: ReOrgBackFromSyncingTest): string =
|
||||
name = "Re-Org Back to Canonical Chain From Syncing Chain"
|
||||
return name
|
||||
)
|
||||
"Re-Org Back to Canonical Chain From Syncing Chain"
|
||||
|
||||
# Test that performs a re-org back to the canonical chain after re-org to syncing/unavailable chain.
|
||||
method execute(cs: ReOrgBackFromSyncingTest, env: TestEnv): bool =
|
||||
|
@ -556,72 +550,71 @@ method execute(cs: ReOrgBackFromSyncingTest, env: TestEnv): bool =
|
|||
testCond ok
|
||||
|
||||
# Produce an alternative chain
|
||||
sidechainPayloads = make([]ExecutableData, 0)
|
||||
testCond env.clMock.produceBlocks(10, BlockProcessCallbacks(
|
||||
var shadow = Shadow()
|
||||
var pbRes = env.clMock.produceBlocks(10, BlockProcessCallbacks(
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
# Send a transaction on each payload of the canonical chain
|
||||
var err error
|
||||
_, err = t.sendNextTx(
|
||||
t.TestContext,
|
||||
t.Engine,
|
||||
BaseTx(
|
||||
recipient: &ZeroAddr,
|
||||
amount: big1,
|
||||
payload: nil,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
),
|
||||
let tc = BaseTx(
|
||||
recipient: some(ZeroAddr),
|
||||
amount: 1.u256,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
if err != nil (
|
||||
fatal "Error trying to send transactions: %v", t.TestName, err)
|
||||
)
|
||||
),
|
||||
let ok = env.sendNextTx(env.engine, tc)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transactions"
|
||||
return true
|
||||
,
|
||||
onGetpayload: proc(): bool =
|
||||
# Check that at least one transaction made it into the payload
|
||||
if len(env.clMock.latestPayloadBuilt.Transactions) == 0 (
|
||||
fatal "No transactions in payload: %v", t.TestName, env.clMock.latestPayloadBuilt)
|
||||
)
|
||||
testCond len(env.clMock.latestPayloadBuilt.transactions) > 0:
|
||||
fatal "No transactions in payload"
|
||||
|
||||
# Generate an alternative payload by simply adding extraData to the block
|
||||
altParentHash = env.clMock.latestPayloadBuilt.parentHash
|
||||
if len(sidechainPayloads) > 0 (
|
||||
altParentHash = sidechainPayloads[len(sidechainPayloads)-1].blockHash
|
||||
var altParentHash = env.clMock.latestPayloadBuilt.parentHash
|
||||
if len(shadow.payloads) > 0:
|
||||
altParentHash = shadow.payloads[^1].blockHash
|
||||
|
||||
let customizer = CustomPayloadData(
|
||||
parentHash: some(ethHash altParentHash),
|
||||
extraData: some(@[0x01.byte]),
|
||||
)
|
||||
customizer = &CustomPayloadData(
|
||||
parentHash: &altParentHash,
|
||||
extraData: &([]byte(0x01)),
|
||||
)
|
||||
altPayload, err = customizer.customizePayload(env.clMock.latestPayloadBuilt)
|
||||
if err != nil (
|
||||
fatal "Unable to customize payload: %v", t.TestName, err)
|
||||
)
|
||||
sidechainPayloads = append(sidechainPayloads, altPayload)
|
||||
),
|
||||
|
||||
let payload = customizer.customizePayload(env.clMock.latestExecutableData)
|
||||
shadow.payloads.add payload
|
||||
return true
|
||||
))
|
||||
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
testCond pbRes
|
||||
|
||||
pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
onGetpayload: proc(): bool =
|
||||
# Re-org to the unavailable sidechain in the middle of block production
|
||||
# to be able to re-org back to the canonical chain
|
||||
r = env.engine.client.newPayload(sidechainPayloads[len(sidechainPayloads)-1])
|
||||
r.expectStatusEither(PayloadExecutionStatus.syncing, PayloadExecutionStatus.accepted)
|
||||
r.expectLatestValidHash(nil)
|
||||
var version = env.engine.version(shadow.payloads[^1].timestamp)
|
||||
let r = env.engine.client.newPayload(version, shadow.payloads[^1])
|
||||
r.expectStatusEither([PayloadExecutionStatus.syncing, PayloadExecutionStatus.accepted])
|
||||
r.expectLatestValidHash()
|
||||
|
||||
# We are going to send one of the alternative payloads and fcU to it
|
||||
forkchoiceUpdatedBack = api.ForkchoiceStateV1(
|
||||
headBlockHash: sidechainPayloads[len(sidechainPayloads)-1].blockHash,
|
||||
let fcu = ForkchoiceStateV1(
|
||||
headBlockHash: shadow.payloads[^1].blockHash,
|
||||
safeBlockHash: env.clMock.latestForkchoice.safeBlockHash,
|
||||
finalizedBlockHash: env.clMock.latestForkchoice.finalizedBlockHash,
|
||||
)
|
||||
|
||||
# It is only expected that the client does not produce an error and the CL Mocker is able to progress after the re-org
|
||||
s = env.engine.client.forkchoiceUpdated(forkchoiceUpdatedBack, nil, sidechainPayloads[len(sidechainPayloads)-1].timestamp)
|
||||
s.expectLatestValidHash(nil)
|
||||
version = env.engine.version(shadow.payloads[^1].timestamp)
|
||||
let s = env.engine.client.forkchoiceUpdated(version, fcu)
|
||||
s.expectLatestValidHash()
|
||||
s.expectPayloadStatus(PayloadExecutionStatus.syncing)
|
||||
|
||||
# After this, the CLMocker will continue and try to re-org to canonical chain once again
|
||||
# CLMocker will fail the test if this is not possible, so nothing left to do.
|
||||
),
|
||||
return true
|
||||
))
|
||||
)
|
||||
testCond pbRes
|
||||
return true
|
||||
|
||||
type
|
||||
ReOrgPrevValidatedPayloadOnSideChainTest* = ref object of EngineSpec
|
||||
|
@ -632,9 +625,15 @@ method withMainFork(cs: ReOrgPrevValidatedPayloadOnSideChainTest, fork: EngineFo
|
|||
return res
|
||||
|
||||
method getName(cs: ReOrgPrevValidatedPayloadOnSideChainTest): string =
|
||||
name = "Re-org to Previously Validated Sidechain Payload"
|
||||
return name
|
||||
)
|
||||
"Re-org to Previously Validated Sidechain Payload"
|
||||
|
||||
func toSeq(x: string): seq[byte] =
|
||||
for z in x:
|
||||
result.add z.byte
|
||||
|
||||
func ethAddress(a, b: int): EthAddress =
|
||||
result[0] = a.byte
|
||||
result[1] = b.byte
|
||||
|
||||
# Test that performs a re-org to a previously validated payload on a side chain.
|
||||
method execute(cs: ReOrgPrevValidatedPayloadOnSideChainTest, env: TestEnv): bool =
|
||||
|
@ -645,99 +644,90 @@ method execute(cs: ReOrgPrevValidatedPayloadOnSideChainTest, env: TestEnv): bool
|
|||
# Produce blocks before starting the test
|
||||
testCond env.clMock.produceBlocks(5, BlockProcessCallbacks())
|
||||
|
||||
var (
|
||||
sidechainPayloads = make([]ExecutableData, 0)
|
||||
sidechainPayloadCount = 5
|
||||
)
|
||||
var shadow = Shadow()
|
||||
|
||||
# Produce a canonical chain while at the same time generate a side chain to which we will re-org.
|
||||
testCond env.clMock.produceBlocks(sidechainPayloadCount, BlockProcessCallbacks(
|
||||
var pbRes = env.clMock.produceBlocks(5, BlockProcessCallbacks(
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
# Send a transaction on each payload of the canonical chain
|
||||
var err error
|
||||
_, err = t.sendNextTx(
|
||||
t.TestContext,
|
||||
t.Engine,
|
||||
BaseTx(
|
||||
recipient: &ZeroAddr,
|
||||
amount: big1,
|
||||
payload: nil,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
ForkConfig: t.ForkConfig,
|
||||
),
|
||||
let tc = BaseTx(
|
||||
recipient: some(ZeroAddr),
|
||||
amount: 1.u256,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
if err != nil (
|
||||
fatal "Error trying to send transactions: %v", t.TestName, err)
|
||||
)
|
||||
),
|
||||
let ok = env.sendNextTx(env.engine, tc)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transactions"
|
||||
return true
|
||||
,
|
||||
onGetpayload: proc(): bool =
|
||||
# Check that at least one transaction made it into the payload
|
||||
if len(env.clMock.latestPayloadBuilt.Transactions) == 0 (
|
||||
fatal "No transactions in payload: %v", t.TestName, env.clMock.latestPayloadBuilt)
|
||||
)
|
||||
# The side chain will consist simply of the same payloads with extra data appended
|
||||
extraData = []byte("side")
|
||||
customData = CustomPayloadData(
|
||||
extraData: &extraData,
|
||||
)
|
||||
if len(sidechainPayloads) > 0 (
|
||||
customData.parentHash = &sidechainPayloads[len(sidechainPayloads)-1].blockHash
|
||||
)
|
||||
altPayload, err = customData.customizePayload(env.clMock.latestPayloadBuilt)
|
||||
if err != nil (
|
||||
fatal "Unable to customize payload: %v", t.TestName, err)
|
||||
)
|
||||
sidechainPayloads = append(sidechainPayloads, altPayload)
|
||||
testCond len(env.clMock.latestPayloadBuilt.transactions) > 0:
|
||||
fatal "No transactions in payload"
|
||||
|
||||
r = env.engine.client.newPayload(altPayload)
|
||||
# The side chain will consist simply of the same payloads with extra data appended
|
||||
var customData = CustomPayloadData(
|
||||
extraData: some(toSeq("side")),
|
||||
)
|
||||
|
||||
if len(shadow.payloads) > 0:
|
||||
customData.parentHash = some(ethHash shadow.payloads[^1].blockHash)
|
||||
|
||||
let payload = customData.customizePayload(env.clMock.latestExecutableData)
|
||||
shadow.payloads.add payload
|
||||
|
||||
let version = env.engine.version(payload.timestamp)
|
||||
let r = env.engine.client.newPayload(version, payload)
|
||||
r.expectStatus(PayloadExecutionStatus.valid)
|
||||
r.expectLatestValidHash(altPayload.blockHash)
|
||||
),
|
||||
r.expectLatestValidHash(payload.blockHash)
|
||||
return true
|
||||
))
|
||||
|
||||
testCond pbRes
|
||||
|
||||
# Attempt to re-org to one of the sidechain payloads, but not the leaf,
|
||||
# and also build a new payload from this sidechain.
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
onGetpayload: proc(): bool =
|
||||
var (
|
||||
prevRandao = common.Hash()
|
||||
suggestedFeeRecipient = common.Address(0x12, 0x34)
|
||||
)
|
||||
rand.Read(prevRandao[:])
|
||||
payloadAttributesCustomizer = &BasePayloadAttributesCustomizer(
|
||||
Random: &prevRandao,
|
||||
SuggestedFeerecipient: &suggestedFeeRecipient,
|
||||
var
|
||||
prevRandao = common.Hash256.randomBytes()
|
||||
suggestedFeeRecipient = ethAddress(0x12, 0x34)
|
||||
|
||||
let payloadAttributesCustomizer = BasePayloadAttributesCustomizer(
|
||||
prevRandao: some(prevRandao),
|
||||
suggestedFeerecipient: some(suggestedFeeRecipient),
|
||||
)
|
||||
|
||||
reOrgPayload = sidechainPayloads[len(sidechainPayloads)-2]
|
||||
reOrgPayloadAttributes = sidechainPayloads[len(sidechainPayloads)-1].PayloadAttributes
|
||||
|
||||
newPayloadAttributes, err = payloadAttributesCustomizer.getPayloadAttributes(reOrgPayloadAttributes)
|
||||
if err != nil (
|
||||
fatal "Unable to customize payload attributes: %v", t.TestName, err)
|
||||
)
|
||||
|
||||
r = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1(
|
||||
let reOrgPayload = shadow.payloads[^2]
|
||||
let reOrgPayloadAttributes = shadow.payloads[^1].attr
|
||||
let newPayloadAttributes = payloadAttributesCustomizer.getPayloadAttributes(reOrgPayloadAttributes)
|
||||
let fcu = ForkchoiceStateV1(
|
||||
headBlockHash: reOrgPayload.blockHash,
|
||||
safeBlockHash: env.clMock.latestForkchoice.safeBlockHash,
|
||||
finalizedBlockHash: env.clMock.latestForkchoice.finalizedBlockHash,
|
||||
), newPayloadAttributes, reOrgPayload.timestamp)
|
||||
)
|
||||
|
||||
var version = env.engine.version(reOrgPayload.timestamp)
|
||||
let r = env.engine.client.forkchoiceUpdated(version, fcu, some(newPayloadAttributes))
|
||||
r.expectPayloadStatus(PayloadExecutionStatus.valid)
|
||||
r.expectLatestValidHash(reOrgPayload.blockHash)
|
||||
|
||||
p = env.engine.client.getPayload(r.Response.PayloadID, newPayloadAttributes)
|
||||
version = env.engine.version(newPayloadAttributes.timestamp)
|
||||
let p = env.engine.client.getPayload(r.get.payloadID.get, version)
|
||||
p.expectPayloadParentHash(reOrgPayload.blockHash)
|
||||
|
||||
s = env.engine.client.newPayload(p.Payload)
|
||||
let payload = p.get.executionPayload
|
||||
let s = env.engine.client.newPayload(payload)
|
||||
s.expectStatus(PayloadExecutionStatus.valid)
|
||||
s.expectLatestValidHash(p.Payload.blockHash)
|
||||
s.expectLatestValidHash(payload.blockHash)
|
||||
|
||||
# After this, the CLMocker will continue and try to re-org to canonical chain once again
|
||||
# CLMocker will fail the test if this is not possible, so nothing left to do.
|
||||
),
|
||||
return true
|
||||
))
|
||||
)
|
||||
testCond pbRes
|
||||
return true
|
||||
|
||||
type
|
||||
SafeReOrgToSideChainTest* = ref object of EngineSpec
|
||||
|
@ -748,9 +738,7 @@ method withMainFork(cs: SafeReOrgToSideChainTest, fork: EngineFork): BaseSpec =
|
|||
return res
|
||||
|
||||
method getName(cs: SafeReOrgToSideChainTest): string =
|
||||
name = "Safe Re-Org to Side Chain"
|
||||
return name
|
||||
)
|
||||
"Safe Re-Org to Side Chain"
|
||||
|
||||
# Test that performs a re-org of the safe block to a side chain.
|
||||
method execute(cs: SafeReOrgToSideChainTest, env: TestEnv): bool =
|
||||
|
@ -759,70 +747,74 @@ method execute(cs: SafeReOrgToSideChainTest, env: TestEnv): bool =
|
|||
testCond ok
|
||||
|
||||
# Produce an alternative chain
|
||||
sidechainPayloads = make([]ExecutableData, 0)
|
||||
var shadow = Shadow()
|
||||
|
||||
if s.slotsToSafe.Uint64() != 1 (
|
||||
fatal "[TEST ISSUE] CLMock configured slots to safe not equal to 1: %v", t.TestName, s.slotsToSafe)
|
||||
)
|
||||
if s.slotsToFinalized.Uint64() != 2 (
|
||||
fatal "[TEST ISSUE] CLMock configured slots to finalized not equal to 2: %v", t.TestName, s.slotsToFinalized)
|
||||
)
|
||||
testCond cs.slotsToSafe == 1:
|
||||
fatal "[TEST ISSUE] CLMock configured slots to safe not equal to 1"
|
||||
|
||||
testCond cs.slotsToFinalized == 2:
|
||||
fatal "[TEST ISSUE] CLMock configured slots to finalized not equal to 2"
|
||||
|
||||
# Produce three payloads `P1`, `P2`, `P3`, along with the side chain payloads `P2'`, `P3'`
|
||||
# First payload is finalized so no alternative payload
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks())
|
||||
testCond env.clMock.produceSingleBlock(BlockProcessCallbacks())
|
||||
|
||||
testCond env.clMock.produceBlocks(2, BlockProcessCallbacks(
|
||||
onGetpayload: proc(): bool =
|
||||
# Generate an alternative payload by simply adding extraData to the block
|
||||
altParentHash = env.clMock.latestPayloadBuilt.parentHash
|
||||
if len(sidechainPayloads) > 0 (
|
||||
altParentHash = sidechainPayloads[len(sidechainPayloads)-1].blockHash
|
||||
var altParentHash = env.clMock.latestPayloadBuilt.parentHash
|
||||
if len(shadow.payloads) > 0:
|
||||
altParentHash = shadow.payloads[^1].blockHash
|
||||
|
||||
let customizer = CustomPayloadData(
|
||||
parentHash: some(ethHash altParentHash),
|
||||
extraData: some(@[0x01.byte]),
|
||||
)
|
||||
customizer = &CustomPayloadData(
|
||||
parentHash: &altParentHash,
|
||||
extraData: &([]byte(0x01)),
|
||||
)
|
||||
altPayload, err = customizer.customizePayload(env.clMock.latestPayloadBuilt)
|
||||
if err != nil (
|
||||
fatal "Unable to customize payload: %v", t.TestName, err)
|
||||
)
|
||||
sidechainPayloads = append(sidechainPayloads, altPayload)
|
||||
),
|
||||
|
||||
let payload = customizer.customizePayload(env.clMock.latestExecutableData)
|
||||
shadow.payloads.add payload
|
||||
return true
|
||||
))
|
||||
|
||||
# Verify current state of labels
|
||||
head = env.engine.client.headerByNumber(Head)
|
||||
head.expectHash(env.clMock.latestPayloadBuilt.blockHash)
|
||||
let head = env.engine.client.namedHeader(Head)
|
||||
head.expectHash(ethHash env.clMock.latestPayloadBuilt.blockHash)
|
||||
|
||||
safe = env.engine.client.headerByNumber(Safe)
|
||||
safe.expectHash(env.clMock.executedPayloadHistory[2].blockHash)
|
||||
let safe = env.engine.client.namedHeader(Safe)
|
||||
safe.expectHash(ethHash env.clMock.executedPayloadHistory[2].blockHash)
|
||||
|
||||
finalized = env.engine.client.headerByNumber(Finalized)
|
||||
finalized.expectHash(env.clMock.executedPayloadHistory[1].blockHash)
|
||||
let finalized = env.engine.client.namedHeader(Finalized)
|
||||
finalized.expectHash(ethHash env.clMock.executedPayloadHistory[1].blockHash)
|
||||
|
||||
# Re-org the safe/head blocks to point to the alternative side chain
|
||||
env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
let pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
|
||||
onGetpayload: proc(): bool =
|
||||
for _, p = range sidechainPayloads (
|
||||
r = env.engine.client.newPayload(p)
|
||||
r.expectStatusEither(PayloadExecutionStatus.valid, PayloadExecutionStatus.accepted)
|
||||
)
|
||||
r = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1(
|
||||
headBlockHash: sidechainPayloads[1].blockHash,
|
||||
safeBlockHash: sidechainPayloads[0].blockHash,
|
||||
for p in shadow.payloads:
|
||||
let version = env.engine.version(p.timestamp)
|
||||
let r = env.engine.client.newPayload(version, p)
|
||||
r.expectStatusEither([PayloadExecutionStatus.valid, PayloadExecutionStatus.accepted])
|
||||
|
||||
let fcu = ForkchoiceStateV1(
|
||||
headBlockHash: shadow.payloads[1].blockHash,
|
||||
safeBlockHash: shadow.payloads[0].blockHash,
|
||||
finalizedBlockHash: env.clMock.executedPayloadHistory[1].blockHash,
|
||||
), nil, sidechainPayloads[1].timestamp)
|
||||
)
|
||||
|
||||
let version = env.engine.version(shadow.payloads[1].timestamp)
|
||||
let r = env.engine.client.forkchoiceUpdated(version, fcu)
|
||||
r.expectPayloadStatus(PayloadExecutionStatus.valid)
|
||||
|
||||
head = env.engine.client.headerByNumber(Head)
|
||||
head.expectHash(sidechainPayloads[1].blockHash)
|
||||
let head = env.engine.client.namedHeader(Head)
|
||||
head.expectHash(ethHash shadow.payloads[1].blockHash)
|
||||
|
||||
safe = env.engine.client.headerByNumber(Safe)
|
||||
safe.expectHash(sidechainPayloads[0].blockHash)
|
||||
let safe = env.engine.client.namedHeader(Safe)
|
||||
safe.expectHash(ethHash shadow.payloads[0].blockHash)
|
||||
|
||||
finalized = env.engine.client.headerByNumber(Finalized)
|
||||
finalized.expectHash(env.clMock.executedPayloadHistory[1].blockHash)
|
||||
let finalized = env.engine.client.namedHeader(Finalized)
|
||||
finalized.expectHash(ethHash env.clMock.executedPayloadHistory[1].blockHash)
|
||||
|
||||
),
|
||||
return true
|
||||
))
|
||||
)
|
||||
|
||||
testCond pbRes
|
||||
return true
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
import
|
||||
std/strutils,
|
||||
eth/common,
|
||||
chronicles,
|
||||
./engine_spec
|
||||
|
||||
type
|
||||
|
@ -24,13 +26,16 @@ type
|
|||
checkType*: BlockStatusRPCcheckType
|
||||
# TODO: Syncing bool
|
||||
|
||||
Shadow = ref object
|
||||
txHash: common.Hash256
|
||||
|
||||
method withMainFork(cs: BlockStatus, fork: EngineFork): BaseSpec =
|
||||
var res = cs.clone()
|
||||
res.mainFork = fork
|
||||
return res
|
||||
|
||||
method getName(cs: BlockStatus): string =
|
||||
"RPC" & $b.checkType
|
||||
"RPC " & $cs.checkType
|
||||
|
||||
# Test to verify Block information available at the Eth RPC after NewPayload/ForkchoiceUpdated
|
||||
method execute(cs: BlockStatus, env: TestEnv): bool =
|
||||
|
@ -38,69 +43,69 @@ method execute(cs: BlockStatus, env: TestEnv): bool =
|
|||
let ok = waitFor env.clMock.waitForTTD()
|
||||
testCond ok
|
||||
|
||||
case b.checkType
|
||||
of SafeOnSafeblockHash, FinalizedOnFinalizedblockHash:
|
||||
var number *big.Int
|
||||
if b.checkType == SafeOnSafeblockHash:
|
||||
if cs.checkType in [SafeOnSafeblockHash, FinalizedOnFinalizedblockHash]:
|
||||
var number = Finalized
|
||||
if cs.checkType == SafeOnSafeblockHash:
|
||||
number = Safe
|
||||
else:
|
||||
number = Finalized
|
||||
|
||||
p = env.engine.client.headerByNumber(number)
|
||||
let p = env.engine.client.namedHeader(number)
|
||||
p.expectError()
|
||||
)
|
||||
|
||||
# Produce blocks before starting the test
|
||||
env.clMock.produceBlocks(5, BlockProcessCallbacks())
|
||||
testCond env.clMock.produceBlocks(5, BlockProcessCallbacks())
|
||||
|
||||
var tx typ.Transaction
|
||||
callbacks = BlockProcessCallbacks(
|
||||
var shadow = Shadow()
|
||||
var callbacks = BlockProcessCallbacks(
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
let tc = BaseTx(
|
||||
recipient: &ZeroAddr,
|
||||
amount: 1.u256,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
),
|
||||
|
||||
let ok = env.sendNextTx(tc)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction: %v", err)
|
||||
recipient: some(ZeroAddr),
|
||||
amount: 1.u256,
|
||||
txType: cs.txType,
|
||||
gasLimit: 75000,
|
||||
)
|
||||
),
|
||||
|
||||
let tx = env.makeNextTx(tc)
|
||||
shadow.txHash = tx.rlpHash
|
||||
let ok = env.sendTx(tx)
|
||||
testCond ok:
|
||||
fatal "Error trying to send transaction"
|
||||
return true
|
||||
)
|
||||
|
||||
case b.checkType (
|
||||
case cs.checkType
|
||||
of LatestOnNewPayload:
|
||||
callbacks.onGetPayload = proc(): bool
|
||||
r = env.engine.client.latestHeader()
|
||||
r.expectHash(env.clMock.latestForkchoice.headblockHash)
|
||||
callbacks.onGetPayload = proc(): bool =
|
||||
let r = env.engine.client.namedHeader(Head)
|
||||
r.expectHash(ethHash env.clMock.latestForkchoice.headblockHash)
|
||||
|
||||
s = env.engine.client.TestBlockNumber()
|
||||
s.ExpectNumber(env.clMock.latestHeadNumber.Uint64())
|
||||
let s = env.engine.client.blockNumber()
|
||||
s.expectNumber(env.clMock.latestHeadNumber.uint64)
|
||||
|
||||
p = env.engine.client.latestHeader()
|
||||
p.expectHash(env.clMock.latestForkchoice.headblockHash)
|
||||
let p = env.engine.client.namedHeader(Head)
|
||||
p.expectHash(ethHash env.clMock.latestForkchoice.headblockHash)
|
||||
|
||||
# Check that the receipt for the transaction we just sent is still not available
|
||||
q = env.engine.client.txReceipt(tx.Hash())
|
||||
let q = env.engine.client.txReceipt(shadow.txHash)
|
||||
q.expectError()
|
||||
return true
|
||||
of LatestOnHeadblockHash:
|
||||
callbacks.onForkchoiceBroadcast = proc(): bool
|
||||
r = env.engine.client.latestHeader()
|
||||
r.expectHash(env.clMock.latestForkchoice.headblockHash)
|
||||
|
||||
s = env.engine.client.txReceipt(tx.Hash())
|
||||
s.ExpectTransactionHash(tx.Hash())
|
||||
callbacks.onForkchoiceBroadcast = proc(): bool =
|
||||
let r = env.engine.client.namedHeader(Head)
|
||||
r.expectHash(ethHash env.clMock.latestForkchoice.headblockHash)
|
||||
let s = env.engine.client.txReceipt(shadow.txHash)
|
||||
s.expectTransactionHash(shadow.txHash)
|
||||
return true
|
||||
of SafeOnSafeblockHash:
|
||||
callbacks.onSafeBlockChange = proc(): bool
|
||||
r = env.engine.client.headerByNumber(Safe)
|
||||
r.expectHash(env.clMock.latestForkchoice.safeblockHash)
|
||||
callbacks.onSafeBlockChange = proc(): bool =
|
||||
let r = env.engine.client.namedHeader(Safe)
|
||||
r.expectHash(ethHash env.clMock.latestForkchoice.safeblockHash)
|
||||
return true
|
||||
of FinalizedOnFinalizedblockHash:
|
||||
callbacks.onFinalizedBlockChange = proc(): bool
|
||||
r = env.engine.client.headerByNumber(Finalized)
|
||||
r.expectHash(env.clMock.latestForkchoice.finalizedblockHash)
|
||||
callbacks.onFinalizedBlockChange = proc(): bool =
|
||||
let r = env.engine.client.namedHeader(Finalized)
|
||||
r.expectHash(ethHash env.clMock.latestForkchoice.finalizedblockHash)
|
||||
return true
|
||||
|
||||
# Perform the test
|
||||
env.clMock.produceSingleBlock(callbacks)
|
||||
|
||||
testCond env.clMock.produceSingleBlock(callbacks)
|
||||
return true
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
# Test versioning of the Engine API methods
|
||||
import
|
||||
std/strutils,
|
||||
chronicles,
|
||||
../cancun/customizer,
|
||||
./engine_spec
|
||||
|
||||
type
|
||||
|
@ -25,7 +27,10 @@ method withMainFork(cs: EngineNewPayloadVersionTest, fork: EngineFork): BaseSpec
|
|||
# when the timestamp payload attribute does not match the upgraded/downgraded version.
|
||||
type
|
||||
ForkchoiceUpdatedOnPayloadRequestTest* = ref object of EngineSpec
|
||||
ForkchoiceUpdatedCustomizer
|
||||
name*: string
|
||||
about*: string
|
||||
forkchoiceUpdatedCustomizer*: ForkchoiceUpdatedCustomizer
|
||||
payloadAttributesCustomizer*: PayloadAttributesCustomizer
|
||||
|
||||
method withMainFork(cs: ForkchoiceUpdatedOnPayloadRequestTest, fork: EngineFork): BaseSpec =
|
||||
var res = cs.clone()
|
||||
|
@ -33,43 +38,35 @@ method withMainFork(cs: ForkchoiceUpdatedOnPayloadRequestTest, fork: EngineFork)
|
|||
return res
|
||||
|
||||
method getName(cs: ForkchoiceUpdatedOnPayloadRequestTest): string =
|
||||
return "ForkchoiceUpdated Version on Payload Request: " + cs.BaseSpec.GetName()
|
||||
"ForkchoiceUpdated Version on Payload Request: " & cs.name
|
||||
|
||||
method execute(cs: ForkchoiceUpdatedOnPayloadRequestTest, env: TestEnv): bool =
|
||||
# Wait until TTD is reached by this client
|
||||
let ok = waitFor env.clMockWaitForTTD()
|
||||
let ok = waitFor env.clMock.waitForTTD()
|
||||
testCond ok
|
||||
|
||||
env.clMock.produceSingleBlock(clmock.BlockProcessCallbacks(
|
||||
let pbRes = env.clMock.produceSingleBlock(clmock.BlockProcessCallbacks(
|
||||
onPayloadAttributesGenerated: proc(): bool =
|
||||
var (
|
||||
payloadAttributes = &env.clMockLatestPayloadAttributes
|
||||
expectedStatus test.PayloadStatus = PayloadExecutionStatus.valid
|
||||
expectedError *int
|
||||
err error
|
||||
)
|
||||
cs.SetEngineAPIVersionResolver(t.ForkConfig)
|
||||
testEngine = t.TestEngine.WithEngineAPIVersionResolver(cs.ForkchoiceUpdatedCustomizer)
|
||||
payloadAttributes, err = cs.GetPayloadAttributes(payloadAttributes)
|
||||
if err != nil (
|
||||
t.Fatalf("FAIL: Error getting custom payload attributes: %v", err)
|
||||
)
|
||||
expectedError, err = cs.GetExpectedError()
|
||||
if err != nil (
|
||||
t.Fatalf("FAIL: Error getting custom expected error: %v", err)
|
||||
)
|
||||
if cs.GetExpectInvalidStatus() (
|
||||
expectedStatus = PayloadExecutionStatus.invalid
|
||||
)
|
||||
var
|
||||
attr = env.clMock.latestPayloadAttributes
|
||||
expectedStatus = PayloadExecutionStatus.valid
|
||||
|
||||
r = env.engine.client.forkchoiceUpdated(env.clMockLatestForkchoice, payloadAttributes, env.clMockLatestHeader.Time)
|
||||
r.ExpectationDescription = cs.Expectation
|
||||
if expectedError != nil (
|
||||
r.expectErrorCode(*expectedError)
|
||||
attr = cs.payloadAttributesCustomizer.getPayloadAttributes(attr)
|
||||
|
||||
let expectedError = cs.forkchoiceUpdatedCustomizer.getExpectedError()
|
||||
if cs.forkchoiceUpdatedCustomizer.getExpectInvalidStatus():
|
||||
expectedStatus = PayloadExecutionStatus.invalid
|
||||
|
||||
cs.forkchoiceUpdatedCustomizer.setEngineAPIVersionResolver(env.engine.com)
|
||||
let version = cs.forkchoiceUpdatedCustomizer.forkchoiceUpdatedVersion(env.clMock.latestHeader.timestamp.uint64)
|
||||
let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice, some(attr))
|
||||
#r.ExpectationDescription = cs.Expectation
|
||||
if expectedError != 0:
|
||||
r.expectErrorCode(expectedError)
|
||||
else:
|
||||
r.expectNoError()
|
||||
r.expectPayloadStatus(expectedStatus)
|
||||
)
|
||||
),
|
||||
return true
|
||||
))
|
||||
)
|
||||
testCond pbRes
|
||||
return true
|
||||
|
|
|
@ -24,10 +24,10 @@ import
|
|||
./cancun_tests
|
||||
|
||||
proc combineTests(): seq[TestDesc] =
|
||||
#result.add wdTestList
|
||||
#result.add ecTestList
|
||||
#result.add authTestList
|
||||
#result.add engineTestList
|
||||
result.add wdTestList
|
||||
result.add ecTestList
|
||||
result.add authTestList
|
||||
result.add engineTestList
|
||||
result.add cancunTestList
|
||||
|
||||
let
|
||||
|
|
|
@ -20,18 +20,18 @@ import
|
|||
import
|
||||
./engine/suggested_fee_recipient,
|
||||
./engine/payload_attributes,
|
||||
#./engine/payload_execution,
|
||||
./engine/payload_execution,
|
||||
./engine/invalid_ancestor,
|
||||
./engine/invalid_payload,
|
||||
./engine/prev_randao,
|
||||
#./engine/payload_id,
|
||||
./engine/payload_id,
|
||||
./engine/forkchoice,
|
||||
#./engine/versioning,
|
||||
./engine/versioning,
|
||||
./engine/bad_hash,
|
||||
#./engine/fork_id,
|
||||
#./engine/reorg,
|
||||
./engine/misc
|
||||
#./engine/rpc
|
||||
./engine/fork_id,
|
||||
./engine/reorg,
|
||||
./engine/misc,
|
||||
./engine/rpc
|
||||
|
||||
proc getGenesis(cs: EngineSpec, param: NetworkParams) =
|
||||
# Set the terminal total difficulty
|
||||
|
@ -63,17 +63,7 @@ proc specExecute(ws: BaseSpec): bool =
|
|||
env.close()
|
||||
|
||||
# Execution specification reference:
|
||||
# https:#github.com/ethereum/execution-apis/blob/main/src/engine/specification.md
|
||||
|
||||
#[var (
|
||||
big0 = new(big.Int)
|
||||
big1 = u256(1)
|
||||
Head *big.Int # Nil
|
||||
Pending = u256(-2)
|
||||
Finalized = u256(-3)
|
||||
Safe = u256(-4)
|
||||
)
|
||||
]#
|
||||
# https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md
|
||||
|
||||
# Register all test combinations for Paris
|
||||
proc makeEngineTest*(): seq[EngineSpec] =
|
||||
|
@ -200,8 +190,87 @@ proc makeEngineTest*(): seq[EngineSpec] =
|
|||
transactionCount: 20,
|
||||
)
|
||||
|
||||
# Payload Execution Tests
|
||||
result.add ReExecutePayloadTest()
|
||||
result.add InOrderPayloadExecutionTest()
|
||||
result.add MultiplePayloadsExtendingCanonicalChainTest(
|
||||
setHeadToFirstPayloadReceived: true,
|
||||
)
|
||||
|
||||
result.add MultiplePayloadsExtendingCanonicalChainTest(
|
||||
setHeadToFirstPayloadReceived: false,
|
||||
)
|
||||
|
||||
result.add NewPayloadOnSyncingClientTest()
|
||||
result.add NewPayloadWithMissingFcUTest()
|
||||
|
||||
const invalidPayloadBlockField = [
|
||||
InvalidTransactionSignature,
|
||||
InvalidTransactionNonce,
|
||||
InvalidTransactionGasPrice,
|
||||
InvalidTransactionGasTipPrice,
|
||||
InvalidTransactionGas,
|
||||
InvalidTransactionValue,
|
||||
InvalidTransactionChainID,
|
||||
]
|
||||
|
||||
# Invalid Transaction Payload Tests
|
||||
for invalidField in invalidPayloadBlockField:
|
||||
let invalidDetectedOnSync = invalidField == InvalidTransactionChainID
|
||||
for syncing in [false, true]:
|
||||
if invalidField != InvalidTransactionGasTipPrice:
|
||||
for testTxType in [TxLegacy, TxEip1559]:
|
||||
result.add InvalidPayloadTestCase(
|
||||
txType: some(testTxType),
|
||||
invalidField: invalidField,
|
||||
syncing: syncing,
|
||||
invalidDetectedOnSync: invalidDetectedOnSync,
|
||||
)
|
||||
else:
|
||||
result.add InvalidPayloadTestCase(
|
||||
txType: some(TxEip1559),
|
||||
invalidField: invalidField,
|
||||
syncing: syncing,
|
||||
invalidDetectedOnSync: invalidDetectedOnSync,
|
||||
)
|
||||
|
||||
const payloadAttributesFieldChange = [
|
||||
PayloadAttributesIncreaseTimestamp,
|
||||
PayloadAttributesRandom,
|
||||
PayloadAttributesSuggestedFeeRecipient,
|
||||
]
|
||||
|
||||
# Payload ID Tests
|
||||
for payloadAttributeFieldChange in payloadAttributesFieldChange:
|
||||
result.add UniquePayloadIDTest(
|
||||
fieldModification: payloadAttributeFieldChange,
|
||||
)
|
||||
|
||||
# Endpoint Versions Tests
|
||||
# Early upgrade of ForkchoiceUpdated when requesting a payload
|
||||
result.add ForkchoiceUpdatedOnPayloadRequestTest(
|
||||
name: "Early upgrade",
|
||||
about: """
|
||||
Early upgrade of ForkchoiceUpdated when requesting a payload.
|
||||
The test sets the fork height to 1, and the block timestamp increments to 2
|
||||
seconds each block.
|
||||
CL Mock prepares the payload attributes for the first block, which should contain
|
||||
the attributes of the next fork.
|
||||
The test then reduces the timestamp by 1, but still uses the next forkchoice updated
|
||||
version, which should result in UNSUPPORTED_FORK_ERROR error.
|
||||
""",
|
||||
forkHeight: 1,
|
||||
blockTimestampIncrement: 2,
|
||||
forkchoiceUpdatedCustomizer: UpgradeForkchoiceUpdatedVersion(
|
||||
expectedError: engineApiUnsupportedFork,
|
||||
),
|
||||
payloadAttributescustomizer: TimestampDeltaPayloadAttributesCustomizer(
|
||||
timestampDelta: -1,
|
||||
),
|
||||
)
|
||||
|
||||
# Register RPC tests
|
||||
#[let blockStatusRPCCheckType = [
|
||||
let blockStatusRPCCheckType = [
|
||||
LatestOnNewPayload,
|
||||
LatestOnHeadBlockHash,
|
||||
SafeOnSafeBlockHash,
|
||||
|
@ -211,6 +280,74 @@ proc makeEngineTest*(): seq[EngineSpec] =
|
|||
for field in blockStatusRPCCheckType:
|
||||
result.add BlockStatus(checkType: field)
|
||||
|
||||
# Fork ID Tests
|
||||
for genesisTimestamp in 0..1:
|
||||
for forkTime in 0..2:
|
||||
for prevForkTime in 0..forkTime:
|
||||
for currentBlock in 0..1:
|
||||
result.add ForkIDSpec(
|
||||
mainFork: ForkParis,
|
||||
genesistimestamp: genesisTimestamp,
|
||||
forkTime: forkTime.uint64,
|
||||
previousForkTime: prevForkTime.uint64,
|
||||
produceBlocksBeforePeering: currentBlock,
|
||||
)
|
||||
|
||||
# Re-org using the Engine API tests
|
||||
|
||||
# Sidechain re-org tests
|
||||
result.add SidechainReOrgTest()
|
||||
result.add ReOrgBackFromSyncingTest(
|
||||
slotsToSafe: 32,
|
||||
slotsToFinalized: 64,
|
||||
)
|
||||
|
||||
result.add ReOrgPrevValidatedPayloadOnSideChainTest(
|
||||
slotsToSafe: 32,
|
||||
slotsToFinalized: 64,
|
||||
)
|
||||
|
||||
result.add SafeReOrgToSideChainTest(
|
||||
slotsToSafe: 1,
|
||||
slotsToFinalized: 2,
|
||||
)
|
||||
|
||||
# Re-org a transaction out of a block, or into a new block
|
||||
result.add TransactionReOrgTest(
|
||||
scenario: TransactionReOrgScenarioReOrgOut,
|
||||
)
|
||||
|
||||
result.add TransactionReOrgTest(
|
||||
scenario: TransactionReOrgScenarioReOrgDifferentBlock,
|
||||
)
|
||||
|
||||
result.add TransactionReOrgTest(
|
||||
scenario: TransactionReOrgScenarioNewPayloadOnRevert,
|
||||
)
|
||||
|
||||
result.add TransactionReOrgTest(
|
||||
scenario: TransactionReOrgScenarioReOrgBackIn,
|
||||
)
|
||||
|
||||
# Re-Org back into the canonical chain tests
|
||||
result.add ReOrgBackToCanonicalTest(
|
||||
slotsToSafe: 10,
|
||||
slotsToFinalized: 20,
|
||||
timeoutSeconds: 60,
|
||||
transactionPerPayload: 1,
|
||||
reOrgDepth: 5,
|
||||
)
|
||||
|
||||
result.add ReOrgBackToCanonicalTest(
|
||||
slotsToSafe: 32,
|
||||
slotsToFinalized: 64,
|
||||
timeoutSeconds: 120,
|
||||
transactionPerPayload: 50,
|
||||
reOrgDepth: 10,
|
||||
executeSidePayloadOnReOrg: true,
|
||||
)
|
||||
|
||||
#[
|
||||
const
|
||||
invalidReorgList = [
|
||||
InvalidStateRoot,
|
||||
|
@ -263,186 +400,8 @@ proc makeEngineTest*(): seq[EngineSpec] =
|
|||
reOrgFromCanonical: reOrgFromCanonical,
|
||||
invalidIndex: invalidIndex,
|
||||
)
|
||||
]#
|
||||
#[
|
||||
# Payload ID Tests
|
||||
for _, payloadAttributeFieldChange := range []PayloadAttributesFieldChange(
|
||||
PayloadAttributesIncreaseTimestamp,
|
||||
PayloadAttributesRandom,
|
||||
PayloadAttributesSuggestedFeeRecipient,
|
||||
) (
|
||||
result.add UniquePayloadIDTest(
|
||||
FieldModification: payloadAttributeFieldChange,
|
||||
))
|
||||
)
|
||||
|
||||
# Endpoint Versions Tests
|
||||
# Early upgrade of ForkchoiceUpdated when requesting a payload
|
||||
result.add
|
||||
ForkchoiceUpdatedOnPayloadRequestTest(
|
||||
BaseSpec: test.BaseSpec(
|
||||
Name: "Early upgrade",
|
||||
About: `
|
||||
Early upgrade of ForkchoiceUpdated when requesting a payload.
|
||||
The test sets the fork height to 1, and the block timestamp increments to 2
|
||||
seconds each block.
|
||||
CL Mock prepares the payload attributes for the first block, which should contain
|
||||
the attributes of the next fork.
|
||||
The test then reduces the timestamp by 1, but still uses the next forkchoice updated
|
||||
version, which should result in UNSUPPORTED_FORK_ERROR error.
|
||||
`,
|
||||
forkHeight: 1,
|
||||
BlockTimestampIncrement: 2,
|
||||
),
|
||||
ForkchoiceUpdatedcustomizer: UpgradeForkchoiceUpdatedVersion(
|
||||
ForkchoiceUpdatedcustomizer: BaseForkchoiceUpdatedCustomizer(
|
||||
PayloadAttributescustomizer: TimestampDeltaPayloadAttributesCustomizer(
|
||||
PayloadAttributescustomizer: BasePayloadAttributesCustomizer(),
|
||||
TimestampDelta: -1,
|
||||
),
|
||||
ExpectedError: globals.UNSUPPORTED_FORK_ERROR,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
# Payload Execution Tests
|
||||
result.add
|
||||
ReExecutePayloadTest(),
|
||||
InOrderPayloadExecutionTest(),
|
||||
MultiplePayloadsExtendingCanonicalChainTest(
|
||||
SetHeadToFirstPayloadReceived: true,
|
||||
),
|
||||
MultiplePayloadsExtendingCanonicalChainTest(
|
||||
SetHeadToFirstPayloadReceived: false,
|
||||
),
|
||||
NewPayloadOnSyncingClientTest(),
|
||||
NewPayloadWithMissingFcUTest(),
|
||||
)
|
||||
|
||||
|
||||
|
||||
# Invalid Transaction Payload Tests
|
||||
for _, invalidField := range []InvalidPayloadBlockField(
|
||||
InvalidTransactionSignature,
|
||||
InvalidTransactionNonce,
|
||||
InvalidTransactionGasPrice,
|
||||
InvalidTransactionGasTipPrice,
|
||||
InvalidTransactionGas,
|
||||
InvalidTransactionValue,
|
||||
InvalidTransactionChainID,
|
||||
) (
|
||||
invalidDetectedOnSync := invalidField == InvalidTransactionChainID
|
||||
for _, syncing in [false, true) (
|
||||
if invalidField != InvalidTransactionGasTipPrice (
|
||||
for _, testTxType := range []TestTransactionType(TxLegacy, TxEip1559) (
|
||||
result.add InvalidPayloadTestCase(
|
||||
BaseSpec: test.BaseSpec(
|
||||
txType: some( testTxType,
|
||||
),
|
||||
InvalidField: invalidField,
|
||||
Syncing: syncing,
|
||||
InvalidDetectedOnSync: invalidDetectedOnSync,
|
||||
))
|
||||
)
|
||||
) else (
|
||||
result.add InvalidPayloadTestCase(
|
||||
BaseSpec: test.BaseSpec(
|
||||
txType: some( TxEip1559,
|
||||
),
|
||||
InvalidField: invalidField,
|
||||
Syncing: syncing,
|
||||
InvalidDetectedOnSync: invalidDetectedOnSync,
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
# Re-org using the Engine API tests
|
||||
|
||||
# Sidechain re-org tests
|
||||
result.add
|
||||
SidechainReOrgTest(),
|
||||
ReOrgBackFromSyncingTest(
|
||||
BaseSpec: test.BaseSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
),
|
||||
),
|
||||
ReOrgPrevValidatedPayloadOnSideChainTest(
|
||||
BaseSpec: test.BaseSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
),
|
||||
),
|
||||
SafeReOrgToSideChainTest(
|
||||
BaseSpec: test.BaseSpec(
|
||||
slotsToSafe: u256(1),
|
||||
slotsToFinalized: u256(2),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// Re-org a transaction out of a block, or into a new block
|
||||
result.add
|
||||
TransactionReOrgTest{
|
||||
Scenario: TransactionReOrgScenarioReOrgOut,
|
||||
},
|
||||
TransactionReOrgTest{
|
||||
Scenario: TransactionReOrgScenarioReOrgDifferentBlock,
|
||||
},
|
||||
TransactionReOrgTest{
|
||||
Scenario: TransactionReOrgScenarioNewPayloadOnRevert,
|
||||
},
|
||||
TransactionReOrgTest{
|
||||
Scenario: TransactionReOrgScenarioReOrgBackIn,
|
||||
},
|
||||
)
|
||||
|
||||
# Re-Org back into the canonical chain tests
|
||||
result.add
|
||||
ReOrgBackToCanonicalTest(
|
||||
BaseSpec: test.BaseSpec(
|
||||
slotsToSafe: u256(10),
|
||||
slotsToFinalized: u256(20),
|
||||
TimeoutSeconds: 60,
|
||||
),
|
||||
TransactionPerPayload: 1,
|
||||
ReOrgDepth: 5,
|
||||
),
|
||||
ReOrgBackToCanonicalTest(
|
||||
BaseSpec: test.BaseSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
TimeoutSeconds: 120,
|
||||
),
|
||||
TransactionPerPayload: 50,
|
||||
ReOrgDepth: 10,
|
||||
ExecuteSidePayloadOnReOrg: true,
|
||||
),
|
||||
)
|
||||
|
||||
# Fork ID Tests
|
||||
for genesisTimestamp := uint64(0); genesisTimestamp <= 1; genesisTimestamp++ (
|
||||
for forkTime := uint64(0); forkTime <= 2; forkTime++ (
|
||||
for prevForkTime := uint64(0); prevForkTime <= forkTime; prevForkTime++ (
|
||||
for currentBlock := 0; currentBlock <= 1; currentBlock++ (
|
||||
result.add
|
||||
ForkIDSpec(
|
||||
BaseSpec: test.BaseSpec(
|
||||
MainFork: config.Paris,
|
||||
Genesistimestamp: pUint64(genesisTimestamp),
|
||||
ForkTime: forkTime,
|
||||
PreviousForkTime: prevForkTime,
|
||||
),
|
||||
ProduceBlocksBeforePeering: currentBlock,
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
]#
|
||||
|
||||
|
||||
|
|
|
@ -10,11 +10,21 @@
|
|||
|
||||
import
|
||||
eth/[common, rlp],
|
||||
chronicles,
|
||||
../../../nimbus/beacon/execution_types,
|
||||
../../../nimbus/beacon/web3_eth_conv
|
||||
../../../nimbus/beacon/web3_eth_conv,
|
||||
./engine_client,
|
||||
./types
|
||||
|
||||
proc txInPayload*(payload: ExecutionPayload, txHash: common.Hash256): bool =
|
||||
for txBytes in payload.transactions:
|
||||
let currTx = rlp.decode(common.Blob txBytes, Transaction)
|
||||
if rlpHash(currTx) == txHash:
|
||||
return true
|
||||
|
||||
proc checkPrevRandaoValue*(client: RpcClient, expectedPrevRandao: common.Hash256, blockNumber: uint64): bool =
|
||||
let storageKey = blockNumber.u256
|
||||
let r = client.storageAt(prevRandaoContractAddr, storageKey)
|
||||
let expected = UInt256.fromBytesBE(expectedPrevRandao.data)
|
||||
r.expectStorageEqual(expected)
|
||||
return true
|
||||
|
|
|
@ -133,6 +133,12 @@ proc makeNextTx*(env: TestEnv, tc: BaseTx): Transaction =
|
|||
proc sendNextTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx): bool =
|
||||
env.sender.sendNextTx(eng.client, tc)
|
||||
|
||||
proc sendNextTxs*(env: TestEnv, eng: EngineEnv, tc: BaseTx, num: int): bool =
|
||||
for i in 0..<num:
|
||||
if not env.sender.sendNextTx(eng.client, tc):
|
||||
return false
|
||||
return true
|
||||
|
||||
proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx, nonce: AccountNonce): bool =
|
||||
env.sender.sendTx(eng.client, tc, nonce)
|
||||
|
||||
|
|
|
@ -62,6 +62,10 @@ const
|
|||
DefaultSleep* = 1
|
||||
prevRandaoContractAddr* = hexToByteArray[20]("0000000000000000000000000000000000000316")
|
||||
GenesisTimestamp* = 0x1234
|
||||
Head* = "latest"
|
||||
Pending* = "pending"
|
||||
Finalized* = "finalized"
|
||||
Safe* = "safe"
|
||||
|
||||
func toAddress*(x: UInt256): EthAddress =
|
||||
var
|
||||
|
@ -172,8 +176,12 @@ template expectLatestValidHash*(res: untyped) =
|
|||
testCond res.isOk:
|
||||
error "Unexpected error", msg=res.error
|
||||
let s = res.get
|
||||
testCond s.latestValidHash.isNone:
|
||||
error "Expect latest valid hash isNone"
|
||||
when s is ForkchoiceUpdatedResponse:
|
||||
testCond s.payloadStatus.latestValidHash.isNone:
|
||||
error "Expect latest valid hash isNone"
|
||||
else:
|
||||
testCond s.latestValidHash.isNone:
|
||||
error "Expect latest valid hash isNone"
|
||||
|
||||
template expectErrorCode*(res: untyped, errCode: int, expectedDesc: string) =
|
||||
testCond res.isErr:
|
||||
|
@ -196,6 +204,17 @@ template expectStatusEither*(res: untyped, cond: openArray[PayloadExecutionStatu
|
|||
testCond s.payloadStatus.status in cond:
|
||||
error "Unexpected expectStatusEither status", expect=cond, get=s.payloadStatus.status
|
||||
|
||||
template expectNoValidationError*(res: untyped) =
|
||||
testCond res.isOk:
|
||||
error "Unexpected expectNoValidationError error", msg=res.error
|
||||
let s = res.get()
|
||||
when s is PayloadStatusV1:
|
||||
testCond s.validationError.isNone:
|
||||
error "Unexpected validation error isSome"
|
||||
else:
|
||||
testCond s.payloadStatus.validationError.isNone:
|
||||
error "Unexpected validation error isSome"
|
||||
|
||||
template expectPayloadStatus*(res: untyped, cond: PayloadExecutionStatus) =
|
||||
testCond res.isOk:
|
||||
error "Unexpected FCU Error", msg=res.error
|
||||
|
@ -258,6 +277,26 @@ template expectBlobGasPrice*(res: untyped, expected: UInt256) =
|
|||
testCond rec.blobGasPrice.get == expected:
|
||||
error "expectBlobGasPrice", expect=expected, get=rec.blobGasPrice.get
|
||||
|
||||
template expectNumber*(res: untyped, expected: uint64) =
|
||||
testCond res.isOk:
|
||||
error "expectNumber", msg=res.error
|
||||
testCond res.get == expected:
|
||||
error "expectNumber", expect=expected, get=res.get
|
||||
|
||||
template expectTransactionHash*(res: untyped, expected: common.Hash256) =
|
||||
testCond res.isOk:
|
||||
error "expectTransactionHash", msg=res.error
|
||||
let rec = res.get
|
||||
testCond rec.txHash == expected:
|
||||
error "expectTransactionHash", expect=expected.short, get=rec.txHash.short
|
||||
|
||||
template expectPayloadParentHash*(res: untyped, expected: Web3Hash) =
|
||||
testCond res.isOk:
|
||||
error "expectPayloadParentHash", msg=res.error
|
||||
let rec = res.get
|
||||
testCond rec.executionPayload.parentHash == expected:
|
||||
error "expectPayloadParentHash", expect=expected.short, get=rec.executionPayload.parentHash.short
|
||||
|
||||
func timestamp*(x: ExecutableData): auto =
|
||||
x.basePayload.timestamp
|
||||
|
||||
|
|
Loading…
Reference in New Issue