Add BeaconSync reorg tests (#1782)
* Add BeaconSync reorg tests * Fix redefinition error in tx_sender.nim
This commit is contained in:
parent
2f2c5127ea
commit
501d8a369a
|
@ -75,11 +75,6 @@ type
|
|||
onSafeBlockChange * : proc(): bool {.gcsafe.}
|
||||
onFinalizedBlockChange* : proc(): bool {.gcsafe.}
|
||||
|
||||
GetPayloadResponse = object
|
||||
executionPayload: ExecutionPayload
|
||||
blockValue: Option[UInt256]
|
||||
blobsBundle: Option[BlobsBundleV1]
|
||||
|
||||
func latestPayloadNumber*(h: Table[uint64, ExecutionPayload]): uint64 =
|
||||
result = 0'u64
|
||||
for n, _ in h:
|
||||
|
@ -99,17 +94,21 @@ func latestWithdrawalsIndex*(h: Table[uint64, ExecutionPayload]): uint64 =
|
|||
func client(cl: CLMocker): RpcClient =
|
||||
cl.clients.first.client
|
||||
|
||||
proc init(cl: CLMocker, clients: ClientPool, com: CommonRef) =
|
||||
cl.clients = clients
|
||||
proc init(cl: CLMocker, eng: EngineEnv, com: CommonRef) =
|
||||
cl.clients = ClientPool()
|
||||
cl.clients.add eng
|
||||
cl.com = com
|
||||
cl.slotsToSafe = 1
|
||||
cl.slotsToFinalized = 2
|
||||
cl.payloadProductionClientDelay = 1
|
||||
cl.headerHistory[0] = com.genesisHeader()
|
||||
|
||||
proc newClMocker*(clients: ClientPool, com: CommonRef): CLMocker =
|
||||
proc newClMocker*(eng: EngineEnv, com: CommonRef): CLMocker =
|
||||
new result
|
||||
result.init(clients, com)
|
||||
result.init(eng, com)
|
||||
|
||||
proc addEngine*(cl: CLMocker, eng: EngineEnv) =
|
||||
cl.clients.add eng
|
||||
|
||||
proc waitForTTD*(cl: CLMocker): Future[bool] {.async.} =
|
||||
let ttd = cl.com.ttd()
|
||||
|
@ -193,32 +192,6 @@ func isCancun(cl: CLMocker, timestamp: Quantity): bool =
|
|||
let ts = fromUnix(timestamp.int64)
|
||||
cl.com.isCancunOrLater(ts)
|
||||
|
||||
func V1(attr: Option[PayloadAttributes]): Option[PayloadAttributesV1] =
|
||||
if attr.isNone:
|
||||
return none(PayloadAttributesV1)
|
||||
some(attr.get.V1)
|
||||
|
||||
when false:
|
||||
func V2(attr: Option[PayloadAttributes]): Option[PayloadAttributesV2] =
|
||||
if attr.isNone:
|
||||
return none(PayloadAttributesV2)
|
||||
some(attr.get.V2)
|
||||
|
||||
func V3(attr: Option[PayloadAttributes]): Option[PayloadAttributesV3] =
|
||||
if attr.isNone:
|
||||
return none(PayloadAttributesV3)
|
||||
some(attr.get.V3)
|
||||
|
||||
proc fcu(cl: CLMocker, version: Version,
|
||||
update: ForkchoiceStateV1,
|
||||
attr: Option[PayloadAttributes]):
|
||||
Result[ForkchoiceUpdatedResponse, string] =
|
||||
let client = cl.nextBlockProducer.client
|
||||
case version
|
||||
of Version.V1: client.forkchoiceUpdatedV1(update, attr.V1)
|
||||
of Version.V2: client.forkchoiceUpdatedV2(update, attr)
|
||||
of Version.V3: client.forkchoiceUpdatedV3(update, attr)
|
||||
|
||||
# Picks the next payload producer from the set of clients registered
|
||||
proc pickNextPayloadProducer(cl: CLMocker): bool =
|
||||
doAssert cl.clients.len != 0
|
||||
|
@ -274,7 +247,8 @@ proc requestNextPayload(cl: CLMocker): bool =
|
|||
cl.prevRandaoHistory[number] = nextPrevRandao
|
||||
|
||||
let version = cl.latestPayloadAttributes.version
|
||||
let res = cl.fcu(version, cl.latestForkchoice, some(cl.latestPayloadAttributes))
|
||||
let client = cl.nextBlockProducer.client
|
||||
let res = client.forkchoiceUpdated(version, cl.latestForkchoice, some(cl.latestPayloadAttributes))
|
||||
if res.isErr:
|
||||
error "CLMocker: Could not send forkchoiceUpdated", version=version, msg=res.error
|
||||
return false
|
||||
|
@ -299,32 +273,11 @@ proc getPayload(cl: CLMocker, payloadId: PayloadID): Result[GetPayloadResponse,
|
|||
let ts = cl.latestPayloadAttributes.timestamp
|
||||
let client = cl.nextBlockProducer.client
|
||||
if cl.isCancun(ts):
|
||||
let res = client.getPayloadV3(payloadId)
|
||||
if res.isErr:
|
||||
return err(res.error)
|
||||
let x = res.get
|
||||
return ok(GetPayloadResponse(
|
||||
executionPayload: executionPayload(x.executionPayload),
|
||||
blockValue: some(x.blockValue),
|
||||
blobsBundle: some(x.blobsBundle)
|
||||
))
|
||||
|
||||
if cl.isShanghai(ts):
|
||||
let res = client.getPayloadV2(payloadId)
|
||||
if res.isErr:
|
||||
return err(res.error)
|
||||
let x = res.get
|
||||
return ok(GetPayloadResponse(
|
||||
executionPayload: executionPayload(x.executionPayload),
|
||||
blockValue: some(x.blockValue)
|
||||
))
|
||||
|
||||
let res = client.getPayloadV1(payloadId)
|
||||
if res.isErr:
|
||||
return err(res.error)
|
||||
return ok(GetPayloadResponse(
|
||||
executionPayload: executionPayload(res.get),
|
||||
))
|
||||
client.getPayload(payloadId, Version.V3)
|
||||
elif cl.isShanghai(ts):
|
||||
client.getPayload(payloadId, Version.V2)
|
||||
else:
|
||||
client.getPayload(payloadId, Version.V1)
|
||||
|
||||
proc getNextPayload(cl: CLMocker): bool =
|
||||
let res = cl.getPayload(cl.nextPayloadID)
|
||||
|
@ -451,7 +404,8 @@ proc broadcastNextNewPayload(cl: CLMocker): bool =
|
|||
proc broadcastForkchoiceUpdated(cl: CLMocker,
|
||||
update: ForkchoiceStateV1): Result[ForkchoiceUpdatedResponse, string] =
|
||||
let version = cl.latestExecutedPayload.version
|
||||
cl.fcu(version, update, none(PayloadAttributes))
|
||||
let client = cl.nextBlockProducer.client
|
||||
client.forkchoiceUpdated(version, update, none(PayloadAttributes))
|
||||
|
||||
proc broadcastLatestForkchoice(cl: CLMocker): bool =
|
||||
let res = cl.broadcastForkchoiceUpdated(cl.latestForkchoice)
|
||||
|
@ -480,7 +434,6 @@ proc broadcastLatestForkchoice(cl: CLMocker): bool =
|
|||
|
||||
return true
|
||||
|
||||
|
||||
proc produceSingleBlock*(cl: CLMocker, cb: BlockProcessCallbacks): bool {.gcsafe.} =
|
||||
doAssert(cl.ttdReached)
|
||||
|
||||
|
|
|
@ -19,9 +19,6 @@ type
|
|||
slotsToFinalized*: int
|
||||
slotsToSafe*: int
|
||||
|
||||
const
|
||||
prevRandaoContractAddr = hexToByteArray[20]("0000000000000000000000000000000000000316")
|
||||
|
||||
template testNP(res, cond: untyped, validHash = none(common.Hash256)) =
|
||||
testCond res.isOk
|
||||
let s = res.get()
|
||||
|
|
|
@ -11,7 +11,9 @@ import
|
|||
|
||||
import web3/engine_api as web3_engine_api
|
||||
|
||||
export execution_types
|
||||
export
|
||||
execution_types,
|
||||
rpcclient
|
||||
|
||||
type
|
||||
Hash256 = eth_types.Hash256
|
||||
|
@ -43,20 +45,6 @@ proc forkchoiceUpdatedV1*(client: RpcClient,
|
|||
wrapTrySimpleRes:
|
||||
client.engine_forkchoiceUpdatedV1(update, payloadAttributes)
|
||||
|
||||
#proc forkchoiceUpdatedV2*(client: RpcClient,
|
||||
# update: ForkchoiceStateV1,
|
||||
# payloadAttributes = none(PayloadAttributesV2)):
|
||||
# Result[ForkchoiceUpdatedResponse, string] =
|
||||
# wrapTrySimpleRes:
|
||||
# client.engine_forkchoiceUpdatedV2(update, payloadAttributes)
|
||||
|
||||
#proc forkchoiceUpdatedV3*(client: RpcClient,
|
||||
# update: ForkchoiceStateV1,
|
||||
# payloadAttributes = none(PayloadAttributesV3)):
|
||||
# Result[ForkchoiceUpdatedResponse, string] =
|
||||
# wrapTrySimpleRes:
|
||||
# client.engine_forkchoiceUpdatedV3(update, payloadAttributes)
|
||||
|
||||
proc forkchoiceUpdatedV2*(client: RpcClient,
|
||||
update: ForkchoiceStateV1,
|
||||
payloadAttributes = none(PayloadAttributes)):
|
||||
|
@ -83,6 +71,50 @@ proc getPayloadV3*(client: RpcClient, payloadId: PayloadID): Result[GetPayloadV3
|
|||
wrapTrySimpleRes:
|
||||
client.engine_getPayloadV3(payloadId)
|
||||
|
||||
proc getPayload*(client: RpcClient,
|
||||
payloadId: PayloadID,
|
||||
version: Version): Result[GetPayloadResponse, string] =
|
||||
if version == Version.V3:
|
||||
let x = client.getPayloadV3(payloadId).valueOr:
|
||||
return err(error)
|
||||
ok(GetPayloadResponse(
|
||||
executionPayload: executionPayload(x.executionPayload),
|
||||
blockValue: some(x.blockValue),
|
||||
blobsBundle: some(x.blobsBundle)
|
||||
))
|
||||
elif version == Version.V2:
|
||||
let x = client.getPayloadV2(payloadId).valueOr:
|
||||
return err(error)
|
||||
ok(GetPayloadResponse(
|
||||
executionPayload: executionPayload(x.executionPayload),
|
||||
blockValue: some(x.blockValue)
|
||||
))
|
||||
else:
|
||||
let x = client.getPayloadV1(payloadId).valueOr:
|
||||
return err(error)
|
||||
ok(GetPayloadResponse(
|
||||
executionPayload: executionPayload(x),
|
||||
))
|
||||
|
||||
proc forkchoiceUpdated*(client: RpcClient,
|
||||
update: ForkchoiceStateV1,
|
||||
attr: PayloadAttributes):
|
||||
Result[ForkchoiceUpdatedResponse, string] =
|
||||
case attr.version
|
||||
of Version.V1: client.forkchoiceUpdatedV1(update, some attr.V1)
|
||||
of Version.V2: client.forkchoiceUpdatedV2(update, some attr)
|
||||
of Version.V3: client.forkchoiceUpdatedV3(update, some attr)
|
||||
|
||||
proc forkchoiceUpdated*(client: RpcClient,
|
||||
version: Version,
|
||||
update: ForkchoiceStateV1,
|
||||
attr = none(PayloadAttributes)):
|
||||
Result[ForkchoiceUpdatedResponse, string] =
|
||||
case version
|
||||
of Version.V1: client.forkchoiceUpdatedV1(update, attr.V1)
|
||||
of Version.V2: client.forkchoiceUpdatedV2(update, attr)
|
||||
of Version.V3: client.forkchoiceUpdatedV3(update, attr)
|
||||
|
||||
proc newPayloadV1*(client: RpcClient,
|
||||
payload: ExecutionPayloadV1):
|
||||
Result[PayloadStatusV1, string] =
|
||||
|
@ -110,6 +142,15 @@ proc newPayloadV3*(client: RpcClient,
|
|||
wrapTrySimpleRes:
|
||||
client.engine_newPayloadV3(payload, versionedHashes, parentBeaconBlockRoot)
|
||||
|
||||
proc newPayload*(client: RpcClient,
|
||||
payload: ExecutionPayload,
|
||||
version: Version):
|
||||
Result[PayloadStatusV1, string] =
|
||||
if version == Version.V1:
|
||||
client.newPayloadV1(payload.V1)
|
||||
else:
|
||||
client.newPayloadV2(payload.V2)
|
||||
|
||||
proc exchangeCapabilities*(client: RpcClient,
|
||||
methods: seq[string]):
|
||||
Result[seq[string], string] =
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import
|
||||
std/[os, math],
|
||||
std/os,
|
||||
eth/keys,
|
||||
eth/p2p as eth_p2p,
|
||||
chronos,
|
||||
json_rpc/[rpcserver, rpcclient],
|
||||
stew/[results, byteutils],
|
||||
stew/[results],
|
||||
../../../nimbus/[
|
||||
config,
|
||||
constants,
|
||||
transaction,
|
||||
core/sealer,
|
||||
core/chain,
|
||||
core/tx_pool,
|
||||
|
@ -20,25 +19,12 @@ import
|
|||
beacon/beacon_engine,
|
||||
common
|
||||
],
|
||||
../../../tests/test_helpers,
|
||||
./engine_client
|
||||
../../../tests/test_helpers
|
||||
|
||||
export
|
||||
results
|
||||
|
||||
type
|
||||
BaseTx* = object of RootObj
|
||||
recipient*: Option[EthAddress]
|
||||
gasLimit* : GasInt
|
||||
amount* : UInt256
|
||||
payload* : seq[byte]
|
||||
txType* : Option[TxType]
|
||||
|
||||
BigInitcodeTx* = object of BaseTx
|
||||
initcodeLength*: int
|
||||
padByte* : uint8
|
||||
initcode* : seq[byte]
|
||||
|
||||
EngineEnv* = ref object
|
||||
conf : NimbusConf
|
||||
com : CommonRef
|
||||
|
@ -46,8 +32,6 @@ type
|
|||
server : RpcHttpServer
|
||||
sealer : SealingEngineRef
|
||||
ttd : DifficultyInt
|
||||
tx : Transaction
|
||||
nonce : uint64
|
||||
client : RpcHttpClient
|
||||
sync : BeaconSyncRef
|
||||
|
||||
|
@ -56,12 +40,8 @@ const
|
|||
genesisFile = baseFolder & "/init/genesis.json"
|
||||
sealerKey = baseFolder & "/init/sealer.key"
|
||||
chainFolder = baseFolder & "/chains"
|
||||
|
||||
# This is the account that sends vault funding transactions.
|
||||
vaultAddr* = hexToByteArray[20]("0xcf49fda3be353c69b41ed96333cd24302da4556f")
|
||||
jwtSecret = "0x7365637265747365637265747365637265747365637265747365637265747365"
|
||||
|
||||
|
||||
proc makeCom*(conf: NimbusConf): CommonRef =
|
||||
CommonRef.new(
|
||||
newCoreDbRef LegacyDbMemory,
|
||||
|
@ -189,98 +169,3 @@ func node*(env: EngineEnv): ENode =
|
|||
|
||||
proc connect*(env: EngineEnv, node: ENode) =
|
||||
waitFor env.node.connectToNode(node)
|
||||
|
||||
func gwei(n: int64): GasInt {.compileTime.} =
|
||||
GasInt(n * (10 ^ 9))
|
||||
|
||||
proc getTxType(tc: BaseTx, nonce: uint64): TxType =
|
||||
if tc.txType.isNone:
|
||||
if nonce mod 2 == 0:
|
||||
TxLegacy
|
||||
else:
|
||||
TxEIP1559
|
||||
else:
|
||||
tc.txType.get
|
||||
|
||||
proc makeTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BaseTx, nonce: AccountNonce): Transaction =
|
||||
const
|
||||
gasPrice = 30.gwei
|
||||
gasTipPrice = 1.gwei
|
||||
|
||||
gasFeeCap = gasPrice
|
||||
gasTipCap = gasTipPrice
|
||||
|
||||
let chainId = env.conf.networkParams.config.chainId
|
||||
let txType = tc.getTxType(nonce)
|
||||
|
||||
# Build the transaction depending on the specified type
|
||||
let tx = if txType == TxLegacy:
|
||||
Transaction(
|
||||
txType : TxLegacy,
|
||||
nonce : nonce,
|
||||
to : tc.recipient,
|
||||
value : tc.amount,
|
||||
gasLimit: tc.gasLimit,
|
||||
gasPrice: gasPrice,
|
||||
payload : tc.payload
|
||||
)
|
||||
else:
|
||||
Transaction(
|
||||
txType : TxEIP1559,
|
||||
nonce : nonce,
|
||||
gasLimit: tc.gasLimit,
|
||||
maxFee : gasFeeCap,
|
||||
maxPriorityFee: gasTipCap,
|
||||
to : tc.recipient,
|
||||
value : tc.amount,
|
||||
payload : tc.payload,
|
||||
chainId : chainId
|
||||
)
|
||||
|
||||
signTransaction(tx, vaultKey, chainId, eip155 = true)
|
||||
|
||||
proc makeTx*(env: EngineEnv, vaultKey: PrivateKey, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction =
|
||||
if tc.payload.len == 0:
|
||||
# Prepare initcode payload
|
||||
if tc.initcode.len != 0:
|
||||
doAssert(tc.initcode.len <= tc.initcodeLength, "invalid initcode (too big)")
|
||||
tc.payload = tc.initcode
|
||||
|
||||
while tc.payload.len < tc.initcodeLength:
|
||||
tc.payload.add tc.padByte
|
||||
|
||||
doAssert(tc.recipient.isNone, "invalid configuration for big contract tx creator")
|
||||
env.makeTx(vaultKey, tc.BaseTx, nonce)
|
||||
|
||||
proc sendNextTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BaseTx): bool =
|
||||
env.tx = env.makeTx(vaultKey, tc, env.nonce)
|
||||
inc env.nonce
|
||||
let rr = env.client.sendTransaction(env.tx)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
return true
|
||||
|
||||
proc sendTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BaseTx, nonce: AccountNonce): bool =
|
||||
env.tx = env.makeTx(vaultKey, tc, nonce)
|
||||
let rr = env.client.sendTransaction(env.tx)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
return true
|
||||
|
||||
proc sendTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BigInitcodeTx, nonce: AccountNonce): bool =
|
||||
env.tx = env.makeTx(vaultKey, tc, nonce)
|
||||
let rr = env.client.sendTransaction(env.tx)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
return true
|
||||
|
||||
proc sendTx*(env: EngineEnv, tx: Transaction): bool =
|
||||
env.tx = tx
|
||||
let rr = env.client.sendTransaction(env.tx)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
return true
|
||||
|
|
|
@ -7,13 +7,15 @@ import
|
|||
./clmock,
|
||||
./engine_client,
|
||||
./client_pool,
|
||||
./engine_env
|
||||
./engine_env,
|
||||
./tx_sender
|
||||
|
||||
export
|
||||
clmock,
|
||||
engine_client,
|
||||
client_pool,
|
||||
engine_env
|
||||
engine_env,
|
||||
tx_sender
|
||||
|
||||
type
|
||||
TestEnv* = ref object
|
||||
|
@ -23,26 +25,18 @@ type
|
|||
port : int
|
||||
rpcPort : int
|
||||
clients : ClientPool
|
||||
sender : TxSender
|
||||
clMock* : CLMocker
|
||||
vaultKey : PrivateKey
|
||||
|
||||
const
|
||||
vaultKeyHex = "63b508a03c3b5937ceb903af8b1b0c191012ef6eb7e9c3fb7afa94e5d214d376"
|
||||
|
||||
proc makeEnv(conf: NimbusConf): TestEnv =
|
||||
let env = TestEnv(
|
||||
TestEnv(
|
||||
conf : conf,
|
||||
port : 30303,
|
||||
rpcPort: 8545,
|
||||
clients: ClientPool(),
|
||||
sender : TxSender.new(conf.networkParams),
|
||||
)
|
||||
|
||||
env.vaultKey = PrivateKey.fromHex(vaultKeyHex).valueOr:
|
||||
echo error
|
||||
quit(QuitFailure)
|
||||
|
||||
env
|
||||
|
||||
proc addEngine(env: TestEnv, conf: var NimbusConf): EngineEnv =
|
||||
conf.tcpPort = Port env.port
|
||||
conf.udpPort = Port env.port
|
||||
|
@ -85,47 +79,58 @@ func engine*(env: TestEnv): EngineEnv =
|
|||
env.clients.first
|
||||
|
||||
proc setupCLMock*(env: TestEnv) =
|
||||
env.clmock = newCLMocker(env.clients, env.engine.com)
|
||||
env.clmock = newCLMocker(env.engine, env.engine.com)
|
||||
|
||||
proc addEngine*(env: TestEnv): EngineEnv =
|
||||
proc addEngine*(env: TestEnv, addToCL: bool = true): EngineEnv =
|
||||
doAssert(env.clMock.isNil.not)
|
||||
var conf = env.conf # clone the conf
|
||||
let eng = env.addEngine(conf)
|
||||
eng.connect(env.engine.node)
|
||||
if addToCL:
|
||||
env.clMock.addEngine(eng)
|
||||
eng
|
||||
|
||||
proc makeTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx, nonce: AccountNonce): Transaction =
|
||||
eng.makeTx(env.vaultKey, tc, nonce)
|
||||
proc makeTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): Transaction =
|
||||
env.sender.makeTx(tc, nonce)
|
||||
|
||||
proc makeTx*(env: TestEnv, eng: EngineEnv, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction =
|
||||
eng.makeTx(env.vaultKey, tc, nonce)
|
||||
proc makeTx*(env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): Transaction =
|
||||
env.sender.makeTx(tc, nonce)
|
||||
|
||||
proc makeTxs*(env: TestEnv, tc: BaseTx, num: int): seq[Transaction] =
|
||||
result = newSeqOfCap[Transaction](num)
|
||||
for _ in 0..<num:
|
||||
result.add env.sender.makeNextTx(tc)
|
||||
|
||||
proc sendNextTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx): bool =
|
||||
eng.sendNextTx(env.vaultKey, tc)
|
||||
env.sender.sendNextTx(eng.client, tc)
|
||||
|
||||
proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx, nonce: AccountNonce): bool =
|
||||
eng.sendTx(env.vaultKey, tc, nonce)
|
||||
env.sender.sendTx(eng.client, tc, nonce)
|
||||
|
||||
proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
|
||||
eng.sendTx(env.vaultKey, tc, nonce)
|
||||
env.sender.sendTx(eng.client, tc, nonce)
|
||||
|
||||
|
||||
proc makeTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): Transaction =
|
||||
env.engine.makeTx(env.vaultKey, tc, nonce)
|
||||
|
||||
proc makeTx*(env: TestEnv, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction =
|
||||
env.engine.makeTx(env.vaultKey, tc, nonce)
|
||||
proc sendTxs*(env: TestEnv, eng: EngineEnv, txs: openArray[Transaction]): bool =
|
||||
for tx in txs:
|
||||
if not sendTx(eng.client, tx):
|
||||
return false
|
||||
true
|
||||
|
||||
proc sendNextTx*(env: TestEnv, tc: BaseTx): bool =
|
||||
env.engine.sendNextTx(env.vaultKey, tc)
|
||||
let client = env.engine.client
|
||||
env.sender.sendNextTx(client, tc)
|
||||
|
||||
proc sendTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): bool =
|
||||
env.engine.sendTx(env.vaultKey, tc, nonce)
|
||||
let client = env.engine.client
|
||||
env.sender.sendTx(client, tc, nonce)
|
||||
|
||||
proc sendTx*(env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
|
||||
env.engine.sendTx(env.vaultKey, tc, nonce)
|
||||
let client = env.engine.client
|
||||
env.sender.sendTx(client, tc, nonce)
|
||||
|
||||
proc sendTx*(env: TestEnv, tx: Transaction): bool =
|
||||
env.engine.sendTx(tx)
|
||||
let client = env.engine.client
|
||||
sendTx(client, tx)
|
||||
|
||||
proc verifyPoWProgress*(env: TestEnv, lastBlockHash: common.Hash256): bool =
|
||||
let res = waitFor env.client.verifyPoWProgress(lastBlockHash)
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
import
|
||||
std/[tables, math],
|
||||
eth/keys,
|
||||
stew/endians2,
|
||||
nimcrypto/sha2,
|
||||
chronicles,
|
||||
./engine_client,
|
||||
../../../nimbus/transaction,
|
||||
../../../nimbus/common
|
||||
|
||||
type
|
||||
BaseTx* = object of RootObj
|
||||
recipient*: Option[EthAddress]
|
||||
gasLimit* : GasInt
|
||||
amount* : UInt256
|
||||
payload* : seq[byte]
|
||||
txType* : Option[TxType]
|
||||
|
||||
BigInitcodeTx* = object of BaseTx
|
||||
initcodeLength*: int
|
||||
padByte* : uint8
|
||||
initcode* : seq[byte]
|
||||
|
||||
TestAccount = object
|
||||
key : PrivateKey
|
||||
address: EthAddress
|
||||
index : int
|
||||
|
||||
TxSender* = ref object
|
||||
accounts: seq[TestAccount]
|
||||
nonceMap: Table[EthAddress, uint64]
|
||||
txSent : int
|
||||
chainId : ChainID
|
||||
|
||||
MakeTxParams* = object
|
||||
chainId*: ChainID
|
||||
key* : PrivateKey
|
||||
nonce* : AccountNonce
|
||||
|
||||
const
|
||||
TestAccountCount = 1000
|
||||
|
||||
func toAddress(key: PrivateKey): EthAddress =
|
||||
toKeyPair(key).pubkey.toCanonicalAddress()
|
||||
|
||||
proc createAccount(idx: int): TestAccount =
|
||||
let
|
||||
seed = toBytesBE(idx.uint64)
|
||||
seedHash = sha256.digest(seed)
|
||||
|
||||
result.index = idx
|
||||
result.key = PrivateKey.fromRaw(seedHash.data).valueOr:
|
||||
echo error
|
||||
quit(QuitFailure)
|
||||
result.address = toAddress(result.key)
|
||||
|
||||
proc createAccounts(sender: TxSender) =
|
||||
for i in 0..<TestAccountCount:
|
||||
sender.accounts.add createAccount(i.int)
|
||||
|
||||
proc getNextAccount(sender: TxSender): TestAccount =
|
||||
sender.accounts[sender.txSent mod sender.accounts.len]
|
||||
|
||||
proc getNextNonce(sender: TxSender, address: EthAddress): uint64 =
|
||||
let nonce = sender.nonceMap.getOrDefault(address, 0'u64)
|
||||
sender.nonceMap[address] = nonce + 1
|
||||
nonce
|
||||
|
||||
proc fillBalance(sender: TxSender, params: NetworkParams) =
|
||||
for x in sender.accounts:
|
||||
params.genesis.alloc[x.address] = GenesisAccount(
|
||||
balance: UInt256.fromHex("0x123450000000000000000"),
|
||||
)
|
||||
|
||||
proc new*(_: type TxSender, params: NetworkParams): TxSender =
|
||||
result = TxSender(chainId: params.config.chainId)
|
||||
result.createAccounts()
|
||||
result.fillBalance(params)
|
||||
|
||||
func gwei(n: int64): GasInt {.compileTime.} =
|
||||
GasInt(n * (10 ^ 9))
|
||||
|
||||
proc getTxType(tc: BaseTx, nonce: uint64): TxType =
|
||||
if tc.txType.isNone:
|
||||
if nonce mod 2 == 0:
|
||||
TxLegacy
|
||||
else:
|
||||
TxEIP1559
|
||||
else:
|
||||
tc.txType.get
|
||||
|
||||
proc makeTx(params: MakeTxParams, tc: BaseTx): Transaction =
|
||||
const
|
||||
gasPrice = 30.gwei
|
||||
gasTipPrice = 1.gwei
|
||||
|
||||
gasFeeCap = gasPrice
|
||||
gasTipCap = gasTipPrice
|
||||
|
||||
let txType = tc.getTxType(params.nonce)
|
||||
|
||||
# Build the transaction depending on the specified type
|
||||
let tx = if txType == TxLegacy:
|
||||
Transaction(
|
||||
txType : TxLegacy,
|
||||
nonce : params.nonce,
|
||||
to : tc.recipient,
|
||||
value : tc.amount,
|
||||
gasLimit: tc.gasLimit,
|
||||
gasPrice: gasPrice,
|
||||
payload : tc.payload
|
||||
)
|
||||
else:
|
||||
Transaction(
|
||||
txType : TxEIP1559,
|
||||
nonce : params.nonce,
|
||||
gasLimit: tc.gasLimit,
|
||||
maxFee : gasFeeCap,
|
||||
maxPriorityFee: gasTipCap,
|
||||
to : tc.recipient,
|
||||
value : tc.amount,
|
||||
payload : tc.payload,
|
||||
chainId : params.chainId
|
||||
)
|
||||
|
||||
signTransaction(tx, params.key, params.chainId, eip155 = true)
|
||||
|
||||
proc makeTx(params: MakeTxParams, tc: BigInitcodeTx): Transaction =
|
||||
var tx = tc
|
||||
if tx.payload.len == 0:
|
||||
# Prepare initcode payload
|
||||
if tx.initcode.len != 0:
|
||||
doAssert(tx.initcode.len <= tx.initcodeLength, "invalid initcode (too big)")
|
||||
tx.payload = tx.initcode
|
||||
|
||||
while tx.payload.len < tx.initcodeLength:
|
||||
tx.payload.add tx.padByte
|
||||
|
||||
doAssert(tx.recipient.isNone, "invalid configuration for big contract tx creator")
|
||||
params.makeTx(tx.BaseTx)
|
||||
|
||||
proc makeTx*(sender: TxSender, tc: BaseTx, nonce: AccountNonce): Transaction =
|
||||
let acc = sender.getNextAccount()
|
||||
let params = MakeTxParams(
|
||||
chainId: sender.chainId,
|
||||
key: acc.key,
|
||||
nonce: nonce
|
||||
)
|
||||
params.makeTx(tc)
|
||||
|
||||
proc makeTx*(sender: TxSender, tc: BigInitcodeTx, nonce: AccountNonce): Transaction =
|
||||
let acc = sender.getNextAccount()
|
||||
let params = MakeTxParams(
|
||||
chainId: sender.chainId,
|
||||
key: acc.key,
|
||||
nonce: nonce
|
||||
)
|
||||
params.makeTx(tc)
|
||||
|
||||
proc makeNextTx*(sender: TxSender, tc: BaseTx): Transaction =
|
||||
let
|
||||
acc = sender.getNextAccount()
|
||||
nonce = sender.getNextNonce(acc.address)
|
||||
params = MakeTxParams(
|
||||
chainId: sender.chainId,
|
||||
key: acc.key,
|
||||
nonce: nonce
|
||||
)
|
||||
params.makeTx(tc)
|
||||
|
||||
proc sendNextTx*(sender: TxSender, client: RpcClient, tc: BaseTx): bool =
|
||||
let tx = sender.makeNextTx(tc)
|
||||
let rr = client.sendTransaction(tx)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
return true
|
||||
|
||||
proc sendTx*(sender: TxSender, client: RpcClient, tc: BaseTx, nonce: AccountNonce): bool =
|
||||
let
|
||||
acc = sender.getNextAccount()
|
||||
params = MakeTxParams(
|
||||
chainId: sender.chainId,
|
||||
key: acc.key,
|
||||
nonce: nonce
|
||||
)
|
||||
tx = params.makeTx(tc)
|
||||
|
||||
let rr = client.sendTransaction(tx)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
return true
|
||||
|
||||
proc sendTx*(sender: TxSender, client: RpcClient, tc: BigInitcodeTx, nonce: AccountNonce): bool =
|
||||
let
|
||||
acc = sender.getNextAccount()
|
||||
params = MakeTxParams(
|
||||
chainId: sender.chainId,
|
||||
key: acc.key,
|
||||
nonce: nonce
|
||||
)
|
||||
tx = params.makeTx(tc)
|
||||
|
||||
let rr = client.sendTransaction(tx)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
return true
|
||||
|
||||
proc sendTx*(client: RpcClient, tx: Transaction): bool =
|
||||
let rr = client.sendTransaction(tx)
|
||||
if rr.isErr:
|
||||
error "Unable to send transaction", msg=rr.error
|
||||
return false
|
||||
return true
|
|
@ -1,6 +1,7 @@
|
|||
import
|
||||
std/[options, typetraits, strutils],
|
||||
eth/common,
|
||||
stew/byteutils,
|
||||
web3/ethtypes,
|
||||
web3/engine_api_types,
|
||||
../../../nimbus/beacon/execution_types,
|
||||
|
@ -19,6 +20,7 @@ type
|
|||
const
|
||||
DefaultTimeout* = 60 # seconds
|
||||
DefaultSleep* = 1
|
||||
prevRandaoContractAddr* = hexToByteArray[20]("0000000000000000000000000000000000000316")
|
||||
|
||||
template testCond*(expr: untyped) =
|
||||
if not (expr):
|
||||
|
@ -85,6 +87,16 @@ template expectStatus*(res, cond: untyped) =
|
|||
testCond s.status == PayloadExecutionStatus.cond:
|
||||
error "Unexpected newPayload status", expect=PayloadExecutionStatus.cond, get=s.status
|
||||
|
||||
template expectStatusEither*(res, cond1, cond2: untyped) =
|
||||
testCond res.isOk:
|
||||
error "Unexpected newPayload error", msg=res.error
|
||||
let s = res.get()
|
||||
testCond s.status == PayloadExecutionStatus.cond1 or s.status == PayloadExecutionStatus.cond2:
|
||||
error "Unexpected newPayload status",
|
||||
expect1=PayloadExecutionStatus.cond1,
|
||||
expect2=PayloadExecutionStatus.cond2,
|
||||
get=s.status
|
||||
|
||||
template expectWithdrawalsRoot*(res: untyped, h: common.BlockHeader, wdRoot: Option[common.Hash256]) =
|
||||
testCond res.isOk:
|
||||
error "Unexpected error", msg=res.error
|
||||
|
|
|
@ -3,7 +3,7 @@ import
|
|||
withdrawals/wd_block_value_spec,
|
||||
withdrawals/wd_max_init_code_spec,
|
||||
#withdrawals/wd_payload_body_spec,
|
||||
#withdrawals/wd_reorg_spec,
|
||||
withdrawals/wd_reorg_spec,
|
||||
withdrawals/wd_sync_spec,
|
||||
./types,
|
||||
./test_env
|
||||
|
@ -24,7 +24,72 @@ proc specExecute[T](ws: BaseSpec): bool =
|
|||
|
||||
let wdTestList* = [
|
||||
#Re-Org tests
|
||||
#[TestDesc(
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Block 1 - 8 Block Re-Org, Sync",
|
||||
about: "Tests a 8 block re-org using NewPayload. Re-org does not change withdrawals fork height",
|
||||
run: specExecute[ReorgSpec],
|
||||
spec: ReorgSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
timeoutSeconds: 300,
|
||||
wdForkHeight: 1, # Genesis is Pre-Withdrawals
|
||||
wdBlockCount: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
reOrgBlockCount: 8,
|
||||
reOrgViaSync: true,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Block 8 - 10 Block Re-Org Sync",
|
||||
about: " Tests a 10 block re-org using sync",
|
||||
# Re-org does not change withdrawals fork height, but changes
|
||||
# the payload at the height of the fork
|
||||
run: specExecute[ReorgSpec],
|
||||
spec: ReorgSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
timeoutSeconds: 300,
|
||||
wdForkHeight: 8, # Genesis is Pre-Withdrawals
|
||||
wdBlockCount: 8,
|
||||
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
reOrgBlockCount: 10,
|
||||
reOrgViaSync: true,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org Sync",
|
||||
about: "Tests a 10 block re-org using sync",
|
||||
# Sidechain reaches withdrawals fork at a lower block height
|
||||
# than the canonical chain
|
||||
run: specExecute[ReorgSpec],
|
||||
spec: ReorgSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
timeoutSeconds: 300,
|
||||
wdForkHeight: 8, # Genesis is Pre-Withdrawals
|
||||
wdBlockCount: 8,
|
||||
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
reOrgBlockCount: 10,
|
||||
reOrgViaSync: true,
|
||||
sidechaintimeIncrements: 2,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Canonical Block 8 / Side Block 9 - 10 Block Re-Org Sync",
|
||||
about: "Tests a 10 block re-org using sync",
|
||||
# Sidechain reaches withdrawals fork at a higher block height
|
||||
# than the canonical chain
|
||||
run: specExecute[ReorgSpec],
|
||||
spec: ReorgSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
timeoutSeconds: 300,
|
||||
wdForkHeight: 8, # Genesis is Pre-Withdrawals
|
||||
wdBlockCount: 8,
|
||||
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
timeIncrements: 2,
|
||||
reOrgBlockCount: 10,
|
||||
reOrgViaSync: true,
|
||||
sidechaintimeIncrements: 1,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Block 1 - 1 Block Re-Org",
|
||||
about: "Tests a simple 1 block re-org",
|
||||
run: specExecute[ReorgSpec],
|
||||
|
@ -52,20 +117,6 @@ let wdTestList* = [
|
|||
reOrgBlockCount: 8,
|
||||
reOrgViaSync: false,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Block 1 - 8 Block Re-Org, Sync",
|
||||
about: "Tests a 8 block re-org using NewPayload. Re-org does not change withdrawals fork height",
|
||||
run: specExecute[ReorgSpec],
|
||||
spec: ReorgSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
timeoutSeconds: 300,
|
||||
wdForkHeight: 1, # Genesis is Pre-Withdrawals
|
||||
wdBlockCount: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
reOrgBlockCount: 8,
|
||||
reOrgViaSync: true,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Block 8 - 10 Block Re-Org NewPayload",
|
||||
about: "Tests a 10 block re-org using NewPayload\n" &
|
||||
|
@ -82,22 +133,6 @@ let wdTestList* = [
|
|||
reOrgBlockCount: 10,
|
||||
reOrgViaSync: false,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Block 8 - 10 Block Re-Org Sync",
|
||||
about: " Tests a 10 block re-org using sync",
|
||||
# Re-org does not change withdrawals fork height, but changes
|
||||
# the payload at the height of the fork
|
||||
run: specExecute[ReorgSpec],
|
||||
spec: ReorgSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
timeoutSeconds: 300,
|
||||
wdForkHeight: 8, # Genesis is Pre-Withdrawals
|
||||
wdBlockCount: 8,
|
||||
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
reOrgBlockCount: 10,
|
||||
reOrgViaSync: true,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org",
|
||||
about: "Tests a 10 block re-org using NewPayload",
|
||||
|
@ -115,23 +150,6 @@ let wdTestList* = [
|
|||
reOrgViaSync: false,
|
||||
sidechaintimeIncrements: 2,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org Sync",
|
||||
about: "Tests a 10 block re-org using sync",
|
||||
# Sidechain reaches withdrawals fork at a lower block height
|
||||
# than the canonical chain
|
||||
run: specExecute[ReorgSpec],
|
||||
spec: ReorgSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
timeoutSeconds: 300,
|
||||
wdForkHeight: 8, # Genesis is Pre-Withdrawals
|
||||
wdBlockCount: 8,
|
||||
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
reOrgBlockCount: 10,
|
||||
reOrgViaSync: true,
|
||||
sidechaintimeIncrements: 2,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Canonical Block 8 / Side Block 9 - 10 Block Re-Org",
|
||||
about: "Tests a 10 block re-org using NewPayload",
|
||||
|
@ -150,24 +168,6 @@ let wdTestList* = [
|
|||
reOrgViaSync: false,
|
||||
sidechaintimeIncrements: 1,
|
||||
)),
|
||||
TestDesc(
|
||||
name: "Withdrawals Fork on Canonical Block 8 / Side Block 9 - 10 Block Re-Org Sync",
|
||||
about: "Tests a 10 block re-org using sync",
|
||||
# Sidechain reaches withdrawals fork at a higher block height
|
||||
# than the canonical chain
|
||||
run: specExecute[ReorgSpec],
|
||||
spec: ReorgSpec(
|
||||
slotsToSafe: u256(32),
|
||||
slotsToFinalized: u256(64),
|
||||
timeoutSeconds: 300,
|
||||
wdForkHeight: 8, # Genesis is Pre-Withdrawals
|
||||
wdBlockCount: 8,
|
||||
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
|
||||
timeIncrements: 2,
|
||||
reOrgBlockCount: 10,
|
||||
reOrgViaSync: true,
|
||||
sidechaintimeIncrements: 1,
|
||||
)),]#
|
||||
|
||||
# Sync Tests
|
||||
TestDesc(
|
||||
|
|
|
@ -32,8 +32,8 @@ type
|
|||
skipBaseVerifications*: bool # For code reuse of the base spec procedure
|
||||
|
||||
WithdrawalsForBlock = object
|
||||
wds: seq[Withdrawal]
|
||||
nextIndex: int
|
||||
wds*: seq[Withdrawal]
|
||||
nextIndex*: int
|
||||
|
||||
const
|
||||
GenesisTimestamp = 0x1234
|
||||
|
@ -46,13 +46,13 @@ const
|
|||
]
|
||||
|
||||
# Get the per-block timestamp increments configured for this test
|
||||
func getBlockTimeIncrements(ws: WDBaseSpec): int =
|
||||
func getBlockTimeIncrements*(ws: WDBaseSpec): int =
|
||||
if ws.timeIncrements == 0:
|
||||
return 1
|
||||
ws.timeIncrements
|
||||
|
||||
# Timestamp delta between genesis and the withdrawals fork
|
||||
func getWithdrawalsGenesisTimeDelta(ws: WDBaseSpec): int =
|
||||
func getWithdrawalsGenesisTimeDelta*(ws: WDBaseSpec): int =
|
||||
ws.wdForkHeight * ws.getBlockTimeIncrements()
|
||||
|
||||
# Calculates Shanghai fork timestamp given the amount of blocks that need to be
|
||||
|
@ -93,7 +93,7 @@ func addUnconditionalBytecode(g: Genesis, start, stop: UInt256) =
|
|||
)
|
||||
acc = acc + 1
|
||||
|
||||
func getWithdrawableAccountCount(ws: WDBaseSpec):int =
|
||||
func getWithdrawableAccountCount*(ws: WDBaseSpec):int =
|
||||
if ws.wdAbleAccountCount == 0:
|
||||
# Withdraw to MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK accounts by default
|
||||
return MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK
|
||||
|
@ -163,7 +163,7 @@ func getGenesis*(ws: WDBaseSpec, param: NetworkParams): NetworkParams =
|
|||
|
||||
param
|
||||
|
||||
func getTransactionCountPerPayload(ws: WDBaseSpec): int =
|
||||
func getTransactionCountPerPayload*(ws: WDBaseSpec): int =
|
||||
ws.txPerBlock.get(16)
|
||||
|
||||
proc verifyContractsStorage(ws: WDBaseSpec, env: TestEnv): Result[void, string] =
|
||||
|
@ -201,11 +201,11 @@ func getPreWithdrawalsBlockCount*(ws: WDBaseSpec): int =
|
|||
ws.wdForkHeight - 1
|
||||
|
||||
# Number of payloads to be produced (pre and post withdrawals) during the entire test
|
||||
func getTotalPayloadCount(ws: WDBaseSpec): int =
|
||||
func getTotalPayloadCount*(ws: WDBaseSpec): int =
|
||||
ws.getPreWithdrawalsBlockCount() + ws.wdBlockCount
|
||||
|
||||
# Generates a list of withdrawals based on current configuration
|
||||
func generateWithdrawalsForBlock(ws: WDBaseSpec, nextIndex: int, startAccount: UInt256): WithdrawalsForBlock =
|
||||
func generateWithdrawalsForBlock*(ws: WDBaseSpec, nextIndex: int, startAccount: UInt256): WithdrawalsForBlock =
|
||||
let
|
||||
differentAccounts = ws.getWithdrawableAccountCount()
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import
|
||||
std/tables,
|
||||
stint,
|
||||
chronos,
|
||||
chronicles,
|
||||
eth/common,
|
||||
./wd_base_spec,
|
||||
./wd_history,
|
||||
../test_env,
|
||||
../engine_client,
|
||||
../types
|
||||
../types,
|
||||
../../../nimbus/beacon/web3_eth_conv
|
||||
|
||||
# Withdrawals re-org spec:
|
||||
# Specifies a withdrawals test where the withdrawals re-org can happen
|
||||
|
@ -13,311 +17,315 @@ import
|
|||
# withdrawals block.
|
||||
type
|
||||
ReorgSpec* = ref object of WDBaseSpec
|
||||
reOrgBlockCount* : uint64 # How many blocks the re-org will replace, including the head
|
||||
reOrgViaSync* : bool # Whether the client should fetch the sidechain by syncing from the secondary client
|
||||
sidechainTimeIncrements*: uint64
|
||||
# How many blocks the re-org will replace, including the head
|
||||
reOrgBlockCount* : int
|
||||
# Whether the client should fetch the sidechain by syncing from the secondary client
|
||||
reOrgViaSync* : bool
|
||||
sidechainTimeIncrements*: int
|
||||
slotsToSafe* : UInt256
|
||||
slotsToFinalized* : UInt256
|
||||
timeoutSeconds* : int
|
||||
|
||||
#[
|
||||
func (ws *WithdrawalsReorgSpec) GetSidechainSplitHeight() uint64 {
|
||||
if ws.ReOrgBlockCount > ws.getTotalPayloadCount() {
|
||||
panic("invalid payload/re-org configuration")
|
||||
|
||||
return ws.getTotalPayloadCount() + 1 - ws.ReOrgBlockCount
|
||||
Sidechain = ref object
|
||||
startAccount: UInt256
|
||||
nextIndex : int
|
||||
wdHistory : WDHistory
|
||||
sidechain : Table[uint64, ExecutionPayload]
|
||||
payloadId : PayloadID
|
||||
height : uint64
|
||||
attr : Option[PayloadAttributes]
|
||||
|
||||
func (ws *WithdrawalsReorgSpec) GetSidechainBlockTimeIncrements() uint64 {
|
||||
if ws.SidechainTimeIncrements == 0 {
|
||||
Canonical = ref object
|
||||
startAccount: UInt256
|
||||
nextIndex : int
|
||||
|
||||
proc getSidechainSplitHeight(ws: ReorgSpec): int =
|
||||
doAssert(ws.reOrgBlockCount <= ws.getTotalPayloadCount())
|
||||
return ws.getTotalPayloadCount() + 1 - ws.reOrgBlockCount
|
||||
|
||||
proc getSidechainBlockTimeIncrements(ws: ReorgSpec): int=
|
||||
if ws.sidechainTimeIncrements == 0:
|
||||
return ws.getBlockTimeIncrements()
|
||||
ws.sidechainTimeIncrements
|
||||
|
||||
return ws.SidechainTimeIncrements
|
||||
proc getSidechainWdForkHeight(ws: ReorgSpec): int =
|
||||
if ws.getSidechainBlockTimeIncrements() != ws.getBlockTimeIncrements():
|
||||
# Block timestamp increments in both chains are different so need to
|
||||
# calculate different heights, only if split happens before fork.
|
||||
# We cannot split by having two different genesis blocks.
|
||||
doAssert(ws.getSidechainSplitHeight() != 0, "invalid sidechain split height")
|
||||
|
||||
func (ws *WithdrawalsReorgSpec) GetSidechainWithdrawalsForkHeight() uint64 {
|
||||
if ws.getSidechainBlockTimeIncrements() != ws.getBlockTimeIncrements() {
|
||||
# Block timestamp increments in both chains are different so need to calculate different heights, only if split happens before fork
|
||||
if ws.getSidechainSplitHeight() == 0 {
|
||||
# We cannot split by having two different genesis blocks.
|
||||
panic("invalid sidechain split height")
|
||||
|
||||
if ws.getSidechainSplitHeight() <= ws.WithdrawalsForkHeight {
|
||||
if ws.getSidechainSplitHeight() <= ws.wdForkHeight:
|
||||
# We need to calculate the height of the fork on the sidechain
|
||||
sidechainSplitBlockTimestamp := ((ws.getSidechainSplitHeight() - 1) * ws.getBlockTimeIncrements())
|
||||
remainingTime := (ws.getWithdrawalsGenesisTimeDelta() - sidechainSplitBlockTimestamp)
|
||||
if remainingTime == 0 {
|
||||
let sidechainSplitBlocktimestamp = (ws.getSidechainSplitHeight() - 1) * ws.getBlockTimeIncrements()
|
||||
let remainingTime = ws.getWithdrawalsGenesisTimeDelta() - sidechainSplitBlocktimestamp
|
||||
if remainingTime == 0 :
|
||||
return ws.getSidechainSplitHeight()
|
||||
|
||||
return ((remainingTime - 1) / ws.SidechainTimeIncrements) + ws.getSidechainSplitHeight()
|
||||
return ((remainingTime - 1) div ws.sidechainTimeIncrements) + ws.getSidechainSplitHeight()
|
||||
|
||||
return ws.WithdrawalsForkHeight
|
||||
]#
|
||||
return ws.wdForkHeight
|
||||
|
||||
proc execute*(ws: ReorgSpec, t: TestEnv): bool =
|
||||
testCond waitFor t.clMock.waitForTTD()
|
||||
proc execute*(ws: ReorgSpec, env: TestEnv): bool =
|
||||
result = true
|
||||
|
||||
testCond waitFor env.clMock.waitForTTD()
|
||||
|
||||
return true
|
||||
#[
|
||||
# Spawn a secondary client which will produce the sidechain
|
||||
secondaryEngine, err := hive_rpc.HiveRPCEngineStarter{}.StartClient(t.T, t.TestContext, t.Genesis, t.ClientParams, t.ClientFiles, t.Engine)
|
||||
if err != nil {
|
||||
error "Unable to spawn a secondary client: %v", t.TestName, err)
|
||||
}
|
||||
secondaryEngineTest := test.NewTestEngineClient(t, secondaryEngine)
|
||||
# t.clMock.AddEngineClient(secondaryEngine)
|
||||
let sec = env.addEngine(addToCL = false)
|
||||
|
||||
var (
|
||||
canonicalStartAccount = big.NewInt(0x1000)
|
||||
canonicalNextIndex = uint64(0)
|
||||
sidechainStartAccount = new(big.Int).SetBit(common.Big0, 160, 1)
|
||||
sidechainNextIndex = uint64(0)
|
||||
sidechainwdHistory = make(wdHistory)
|
||||
sidechain = make(map[uint64]*typ.ExecutableData)
|
||||
sidechainPayloadId *beacon.PayloadID
|
||||
)
|
||||
var
|
||||
canonical = Canonical(
|
||||
startAccount: u256(0x1000),
|
||||
nextIndex : 0,
|
||||
)
|
||||
sidechain = Sidechain(
|
||||
startAccount: 1.u256 shl 160,
|
||||
nextIndex : 0,
|
||||
wdHistory : WDHistory(),
|
||||
sidechain : initTable[uint64, ExecutionPayload]()
|
||||
)
|
||||
|
||||
# Sidechain withdraws on the max account value range 0xffffffffffffffffffffffffffffffffffffffff
|
||||
sidechainStartAccount.Sub(sidechainStartAccount, big.NewInt(int64(ws.getWithdrawableAccountCount())+1))
|
||||
sidechain.startAccount -= u256(ws.getWithdrawableAccountCount()+1)
|
||||
|
||||
t.clMock.ProduceBlocks(int(ws.getPreWithdrawalsBlockCount()+ws.WithdrawalsBlockCount), clmock.BlockProcessCallbacks{
|
||||
OnPayloadProducerSelected: proc(): bool =
|
||||
t.clMock.NextWithdrawals = nil
|
||||
let numBlocks = ws.getPreWithdrawalsBlockCount()+ws.wdBlockCount
|
||||
let pbRes = env.clMock.produceBlocks(numBlocks, BlockProcessCallbacks(
|
||||
onPayloadProducerSelected: proc(): bool =
|
||||
env.clMock.nextWithdrawals = none(seq[WithdrawalV1])
|
||||
|
||||
if t.clMock.CurrentPayloadNumber >= ws.WithdrawalsForkHeight {
|
||||
if env.clMock.currentPayloadNumber >= ws.wdForkHeight.uint64:
|
||||
# Prepare some withdrawals
|
||||
t.clMock.NextWithdrawals, canonicalNextIndex = ws.GenerateWithdrawalsForBlock(canonicalNextIndex, canonicalStartAccount)
|
||||
ws.wdHistory[t.clMock.CurrentPayloadNumber] = t.clMock.NextWithdrawals
|
||||
}
|
||||
let wfb = ws.generateWithdrawalsForBlock(canonical.nextIndex, canonical.startAccount)
|
||||
env.clMock.nextWithdrawals = some(w3Withdrawals wfb.wds)
|
||||
canonical.nextIndex = wfb.nextIndex
|
||||
ws.wdHistory.put(env.clMock.currentPayloadNumber, wfb.wds)
|
||||
|
||||
if t.clMock.CurrentPayloadNumber >= ws.getSidechainSplitHeight() {
|
||||
if env.clMock.currentPayloadNumber >= ws.getSidechainSplitHeight().uint64:
|
||||
# We have split
|
||||
if t.clMock.CurrentPayloadNumber >= ws.getSidechainWithdrawalsForkHeight() {
|
||||
if env.clMock.currentPayloadNumber >= ws.getSidechainWdForkHeight().uint64:
|
||||
# And we are past the withdrawals fork on the sidechain
|
||||
sidechainwdHistory[t.clMock.CurrentPayloadNumber], sidechainNextIndex = ws.GenerateWithdrawalsForBlock(sidechainNextIndex, sidechainStartAccount)
|
||||
} # else nothing to do
|
||||
} else {
|
||||
# We have not split
|
||||
sidechainwdHistory[t.clMock.CurrentPayloadNumber] = t.clMock.NextWithdrawals
|
||||
sidechainNextIndex = canonicalNextIndex
|
||||
}
|
||||
let wfb = ws.generateWithdrawalsForBlock(sidechain.nextIndex, sidechain.startAccount)
|
||||
sidechain.wdHistory.put(env.clMock.currentPayloadNumber, wfb.wds)
|
||||
sidechain.nextIndex = wfb.nextIndex
|
||||
else:
|
||||
if env.clMock.nextWithdrawals.isSome:
|
||||
let wds = ethWithdrawals env.clMock.nextWithdrawals.get()
|
||||
sidechain.wdHistory.put(env.clMock.currentPayloadNumber, wds)
|
||||
sidechain.nextIndex = canonical.nextIndex
|
||||
|
||||
},
|
||||
OnRequestNextPayload: proc(): bool =
|
||||
return true
|
||||
,
|
||||
onRequestNextPayload: proc(): bool =
|
||||
# Send transactions to be included in the payload
|
||||
txs, err := helper.SendNextTransactions(
|
||||
t.TestContext,
|
||||
t.clMock.NextBlockProducer,
|
||||
&helper.BaseTransactionCreator{
|
||||
Recipient: &globals.PrevRandaoContractAddr,
|
||||
Amount: common.Big1,
|
||||
Payload: nil,
|
||||
TxType: t.TestTransactionType,
|
||||
GasLimit: 75000,
|
||||
},
|
||||
ws.getTransactionCountPerPayload(),
|
||||
let txs = env.makeTxs(
|
||||
BaseTx(
|
||||
recipient: some(prevRandaoContractAddr),
|
||||
amount: 1.u256,
|
||||
txType: ws.txType,
|
||||
gasLimit: 75000.GasInt,
|
||||
),
|
||||
ws.getTransactionCountPerPayload()
|
||||
)
|
||||
if err != nil {
|
||||
error "Error trying to send transactions: %v", t.TestName, err)
|
||||
}
|
||||
|
||||
testCond env.sendTxs(env.clMock.nextBlockProducer, txs):
|
||||
error "Error trying to send transaction"
|
||||
|
||||
# Error will be ignored here since the tx could have been already relayed
|
||||
secondaryEngine.SendTransactions(t.TestContext, txs...)
|
||||
discard env.sendTxs(sec, txs)
|
||||
|
||||
if t.clMock.CurrentPayloadNumber >= ws.getSidechainSplitHeight() {
|
||||
if env.clMock.currentPayloadNumber >= ws.getSidechainSplitHeight().uint64:
|
||||
# Also request a payload from the sidechain
|
||||
fcU := beacon.ForkchoiceStateV1{
|
||||
HeadBlockHash: t.clMock.latestForkchoice.HeadBlockHash,
|
||||
}
|
||||
var fcState = ForkchoiceStateV1(
|
||||
headBlockHash: env.clMock.latestForkchoice.headBlockHash,
|
||||
)
|
||||
|
||||
if t.clMock.CurrentPayloadNumber > ws.getSidechainSplitHeight() {
|
||||
if lastSidePayload, ok := sidechain[t.clMock.CurrentPayloadNumber-1]; !ok {
|
||||
panic("sidechain payload not found")
|
||||
} else {
|
||||
fcU.HeadBlockHash = lastSidePayload.BlockHash
|
||||
}
|
||||
}
|
||||
if env.clMock.currentPayloadNumber > ws.getSidechainSplitHeight().uint64:
|
||||
let lastSidePayload = sidechain.sidechain[env.clMock.currentPayloadNumber-1]
|
||||
fcState.headBlockHash = lastSidePayload.blockHash
|
||||
|
||||
var version int
|
||||
pAttributes := typ.PayloadAttributes{
|
||||
Random: t.clMock.latestPayloadAttributes.Random,
|
||||
SuggestedFeeRecipient: t.clMock.latestPayloadAttributes.SuggestedFeeRecipient,
|
||||
}
|
||||
if t.clMock.CurrentPayloadNumber > ws.getSidechainSplitHeight() {
|
||||
pAttributes.Timestamp = sidechain[t.clMock.CurrentPayloadNumber-1].Timestamp + uint64(ws.getSidechainBlockTimeIncrements())
|
||||
} else if t.clMock.CurrentPayloadNumber == ws.getSidechainSplitHeight() {
|
||||
pAttributes.Timestamp = t.clMock.latestHeader.Time + uint64(ws.getSidechainBlockTimeIncrements())
|
||||
} else {
|
||||
pAttributes.Timestamp = t.clMock.latestPayloadAttributes.Timestamp
|
||||
}
|
||||
if t.clMock.CurrentPayloadNumber >= ws.getSidechainWithdrawalsForkHeight() {
|
||||
var attr = PayloadAttributes(
|
||||
prevRandao: env.clMock.latestPayloadAttributes.prevRandao,
|
||||
suggestedFeeRecipient: env.clMock.latestPayloadAttributes.suggestedFeeRecipient,
|
||||
)
|
||||
|
||||
if env.clMock.currentPayloadNumber > ws.getSidechainSplitHeight().uint64:
|
||||
attr.timestamp = w3Qty(sidechain.sidechain[env.clMock.currentPayloadNumber-1].timestamp, ws.getSidechainBlockTimeIncrements())
|
||||
elif env.clMock.currentPayloadNumber == ws.getSidechainSplitHeight().uint64:
|
||||
attr.timestamp = w3Qty(env.clMock.latestHeader.timestamp, ws.getSidechainBlockTimeIncrements())
|
||||
else:
|
||||
attr.timestamp = env.clMock.latestPayloadAttributes.timestamp
|
||||
|
||||
if env.clMock.currentPayloadNumber >= ws.getSidechainwdForkHeight().uint64:
|
||||
# Withdrawals
|
||||
version = 2
|
||||
pAttributes.Withdrawals = sidechainwdHistory[t.clMock.CurrentPayloadNumber]
|
||||
} else {
|
||||
# No withdrawals
|
||||
version = 1
|
||||
}
|
||||
let rr = sidechain.wdHistory.get(env.clMock.currentPayloadNumber)
|
||||
testCond rr.isOk:
|
||||
error "sidechain wd", msg=rr.error
|
||||
|
||||
info "Requesting sidechain payload %d: %v", t.TestName, t.clMock.CurrentPayloadNumber, pAttributes)
|
||||
attr.withdrawals = some(w3Withdrawals rr.get)
|
||||
|
||||
r := secondaryEngineTest.forkchoiceUpdated(&fcU, &pAttributes, version)
|
||||
info "Requesting sidechain payload",
|
||||
number=env.clMock.currentPayloadNumber
|
||||
|
||||
sidechain.attr = some(attr)
|
||||
let r = sec.client.forkchoiceUpdated(fcState, attr)
|
||||
r.expectNoError()
|
||||
r.expectPayloadStatus(test.Valid)
|
||||
if r.Response.PayloadID == nil {
|
||||
error "Unable to get a payload ID on the sidechain", t.TestName)
|
||||
}
|
||||
sidechainPayloadId = r.Response.PayloadID
|
||||
}
|
||||
},
|
||||
OnGetPayload: proc(): bool =
|
||||
var (
|
||||
version int
|
||||
payload *typ.ExecutableData
|
||||
)
|
||||
if t.clMock.CurrentPayloadNumber >= ws.getSidechainWithdrawalsForkHeight() {
|
||||
version = 2
|
||||
} else {
|
||||
version = 1
|
||||
}
|
||||
if t.clMock.latestPayloadBuilt.Number >= ws.getSidechainSplitHeight() {
|
||||
r.testFCU(valid)
|
||||
testCond r.get().payloadID.isSome:
|
||||
error "Unable to get a payload ID on the sidechain"
|
||||
sidechain.payloadId = r.get().payloadID.get()
|
||||
|
||||
return true
|
||||
,
|
||||
onGetPayload: proc(): bool =
|
||||
var
|
||||
payload: ExecutionPayload
|
||||
|
||||
if env.clMock.latestPayloadBuilt.blockNumber.uint64 >= ws.getSidechainSplitHeight().uint64:
|
||||
# This payload is built by the secondary client, hence need to manually fetch it here
|
||||
r := secondaryEngineTest.getPayload(sidechainPayloadId, version)
|
||||
doAssert(sidechain.attr.isSome)
|
||||
let version = sidechain.attr.get().version
|
||||
let r = sec.client.getPayload(sidechain.payloadId, version)
|
||||
r.expectNoError()
|
||||
payload = &r.Payload
|
||||
sidechain[payload.Number] = payload
|
||||
} else {
|
||||
payload = r.get().executionPayload
|
||||
sidechain.sidechain[payload.blockNumber.uint64] = payload
|
||||
else:
|
||||
# This block is part of both chains, simply forward it to the secondary client
|
||||
payload = &t.clMock.latestPayloadBuilt
|
||||
}
|
||||
r := secondaryEngineTest.newPayload(payload, nil, nil, version)
|
||||
r.expectStatus(test.Valid)
|
||||
p := secondaryEngineTest.forkchoiceUpdated(
|
||||
&beacon.ForkchoiceStateV1{
|
||||
HeadBlockHash: payload.BlockHash,
|
||||
},
|
||||
nil,
|
||||
version,
|
||||
payload = env.clMock.latestPayloadBuilt
|
||||
|
||||
let r = sec.client.newPayload(payload, payload.version)
|
||||
r.expectStatus(valid)
|
||||
|
||||
let fcState = ForkchoiceStateV1(
|
||||
headBlockHash: payload.blockHash,
|
||||
)
|
||||
p.expectPayloadStatus(test.Valid)
|
||||
},
|
||||
})
|
||||
let p = sec.client.forkchoiceUpdated(payload.version, fcState)
|
||||
p.testFCU(valid)
|
||||
return true
|
||||
))
|
||||
testCond pbRes
|
||||
|
||||
sidechainHeight := t.clMock.latestExecutedPayload.Number
|
||||
sidechain.height = env.clMock.latestExecutedPayload.blockNumber.uint64
|
||||
|
||||
if ws.WithdrawalsForkHeight < ws.getSidechainWithdrawalsForkHeight() {
|
||||
if ws.wdForkHeight < ws.getSidechainwdForkHeight():
|
||||
# This means the canonical chain forked before the sidechain.
|
||||
# Therefore we need to produce more sidechain payloads to reach
|
||||
# at least`ws.WithdrawalsBlockCount` withdrawals payloads produced on
|
||||
# the sidechain.
|
||||
for i := uint64(0); i < ws.getSidechainWithdrawalsForkHeight()-ws.WithdrawalsForkHeight; i++ {
|
||||
sidechainwdHistory[sidechainHeight+1], sidechainNextIndex = ws.GenerateWithdrawalsForBlock(sidechainNextIndex, sidechainStartAccount)
|
||||
pAttributes := typ.PayloadAttributes{
|
||||
Timestamp: sidechain[sidechainHeight].Timestamp + ws.getSidechainBlockTimeIncrements(),
|
||||
Random: t.clMock.latestPayloadAttributes.Random,
|
||||
SuggestedFeeRecipient: t.clMock.latestPayloadAttributes.SuggestedFeeRecipient,
|
||||
Withdrawals: sidechainwdHistory[sidechainHeight+1],
|
||||
}
|
||||
r := secondaryEngineTest.forkchoiceUpdatedV2(&beacon.ForkchoiceStateV1{
|
||||
HeadBlockHash: sidechain[sidechainHeight].BlockHash,
|
||||
}, &pAttributes)
|
||||
r.expectPayloadStatus(test.Valid)
|
||||
time.Sleep(time.Second)
|
||||
p := secondaryEngineTest.getPayloadV2(r.Response.PayloadID)
|
||||
let height = ws.getSidechainwdForkHeight()-ws.wdForkHeight
|
||||
for i in 0..<height:
|
||||
let
|
||||
wfb = ws.generateWithdrawalsForBlock(sidechain.nextIndex, sidechain.startAccount)
|
||||
|
||||
sidechain.wdHistory.put(sidechain.height+1, wfb.wds)
|
||||
sidechain.nextIndex = wfb.nextIndex
|
||||
|
||||
let wds = sidechain.wdHistory.get(sidechain.height+1).valueOr:
|
||||
echo "get wd history error ", error
|
||||
return false
|
||||
|
||||
let
|
||||
attr = PayloadAttributes(
|
||||
timestamp: w3Qty(sidechain.sidechain[sidechain.height].timestamp, ws.getSidechainBlockTimeIncrements()),
|
||||
prevRandao: env.clMock.latestPayloadAttributes.prevRandao,
|
||||
suggestedFeeRecipient: env.clMock.latestPayloadAttributes.suggestedFeeRecipient,
|
||||
withdrawals: some(w3Withdrawals wds),
|
||||
)
|
||||
fcState = ForkchoiceStateV1(
|
||||
headBlockHash: sidechain.sidechain[sidechain.height].blockHash,
|
||||
)
|
||||
|
||||
let r = sec.client.forkchoiceUpdatedV2(fcState, some(attr))
|
||||
r.testFCU(valid)
|
||||
|
||||
let p = sec.client.getPayloadV2(r.get().payloadID.get)
|
||||
p.expectNoError()
|
||||
s := secondaryEngineTest.newPayloadV2(&p.Payload)
|
||||
s.expectStatus(test.Valid)
|
||||
q := secondaryEngineTest.forkchoiceUpdatedV2(
|
||||
&beacon.ForkchoiceStateV1{
|
||||
HeadBlockHash: p.Payload.BlockHash,
|
||||
},
|
||||
nil,
|
||||
)
|
||||
q.expectPayloadStatus(test.Valid)
|
||||
sidechainHeight++
|
||||
sidechain[sidechainHeight] = &p.Payload
|
||||
}
|
||||
}
|
||||
|
||||
let z = p.get()
|
||||
let s = sec.client.newPayloadV2(z.executionPayload)
|
||||
s.expectStatus(valid)
|
||||
|
||||
let fs = ForkchoiceStateV1(headBlockHash: z.executionPayload.blockHash)
|
||||
|
||||
let q = sec.client.forkchoiceUpdatedV2(fs)
|
||||
q.testFCU(valid)
|
||||
|
||||
inc sidechain.height
|
||||
sidechain.sidechain[sidechain.height] = executionPayload(z.executionPayload)
|
||||
|
||||
# Check the withdrawals on the latest
|
||||
ws.wdHistory.VerifyWithdrawals(
|
||||
sidechainHeight,
|
||||
nil,
|
||||
t.TestEngine,
|
||||
)
|
||||
let res = ws.wdHistory.verifyWithdrawals(sidechain.height, none(UInt256), env.client)
|
||||
testCond res.isOk
|
||||
|
||||
if ws.ReOrgViaSync {
|
||||
if ws.reOrgViaSync:
|
||||
# Send latest sidechain payload as NewPayload + FCU and wait for sync
|
||||
loop:
|
||||
for {
|
||||
r := t.rpcClient.newPayloadV2(sidechain[sidechainHeight])
|
||||
let
|
||||
payload = sidechain.sidechain[sidechain.height]
|
||||
sideHash = sidechain.sidechain[sidechain.height].blockHash
|
||||
sleep = DefaultSleep
|
||||
period = chronos.seconds(sleep)
|
||||
|
||||
var loop = 0
|
||||
if ws.timeoutSeconds == 0:
|
||||
ws.timeoutSeconds = DefaultTimeout
|
||||
|
||||
while loop < ws.timeoutSeconds:
|
||||
let r = env.client.newPayloadV2(payload.V2)
|
||||
r.expectNoError()
|
||||
p := t.rpcClient.forkchoiceUpdatedV2(
|
||||
&beacon.ForkchoiceStateV1{
|
||||
HeadBlockHash: sidechain[sidechainHeight].BlockHash,
|
||||
},
|
||||
nil,
|
||||
)
|
||||
let fcState = ForkchoiceStateV1(headBlockHash: sideHash)
|
||||
let p = env.client.forkchoiceUpdatedV2(fcState)
|
||||
p.expectNoError()
|
||||
if p.Response.PayloadStatus.Status == test.Invalid {
|
||||
error "Primary client invalidated side chain", t.TestName)
|
||||
}
|
||||
select {
|
||||
case <-t.TimeoutContext.Done():
|
||||
error "Timeout waiting for sync", t.TestName)
|
||||
case <-time.After(time.Second):
|
||||
b := t.rpcClient.BlockByNumber(nil)
|
||||
if b.Block.Hash() == sidechain[sidechainHeight].BlockHash {
|
||||
# sync successful
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
let status = p.get().payloadStatus.status
|
||||
if status == PayloadExecutionStatus.invalid:
|
||||
error "Primary client invalidated side chain"
|
||||
return false
|
||||
|
||||
var header: common.BlockHeader
|
||||
let b = env.client.latestHeader(header)
|
||||
testCond b.isOk
|
||||
if header.blockHash == ethHash(sidehash):
|
||||
# sync successful
|
||||
break
|
||||
|
||||
waitFor sleepAsync(period)
|
||||
loop += sleep
|
||||
else:
|
||||
# Send all payloads one by one to the primary client
|
||||
for payloadNumber := ws.getSidechainSplitHeight(); payloadNumber <= sidechainHeight; payloadNumber++ {
|
||||
payload, ok := sidechain[payloadNumber]
|
||||
if !ok {
|
||||
error "Invalid payload %d requested.", t.TestName, payloadNumber)
|
||||
}
|
||||
var version int
|
||||
if payloadNumber >= ws.getSidechainWithdrawalsForkHeight() {
|
||||
version = 2
|
||||
} else {
|
||||
version = 1
|
||||
}
|
||||
info "Sending sidechain payload %d, hash=%s, parent=%s", t.TestName, payloadNumber, payload.BlockHash, payload.ParentHash)
|
||||
r := t.rpcClient.newPayload(payload, nil, nil, version)
|
||||
r.expectStatusEither(test.Valid, test.Accepted)
|
||||
p := t.rpcClient.forkchoiceUpdated(
|
||||
&beacon.ForkchoiceStateV1{
|
||||
HeadBlockHash: payload.BlockHash,
|
||||
},
|
||||
nil,
|
||||
version,
|
||||
)
|
||||
p.expectPayloadStatus(test.Valid)
|
||||
}
|
||||
}
|
||||
var payloadNumber = ws.getSidechainSplitHeight()
|
||||
while payloadNumber.uint64 <= sidechain.height:
|
||||
let payload = sidechain.sidechain[payloadNumber.uint64]
|
||||
var version = Version.V1
|
||||
if payloadNumber >= ws.getSidechainwdForkHeight():
|
||||
version = Version.V2
|
||||
|
||||
info "Sending sidechain",
|
||||
payloadNumber,
|
||||
hash=payload.blockHash.short,
|
||||
parentHash=payload.parentHash.short
|
||||
|
||||
let r = env.client.newPayload(payload, version)
|
||||
r.expectStatusEither(valid, accepted)
|
||||
|
||||
let fcState = ForkchoiceStateV1(headBlockHash: payload.blockHash)
|
||||
let p = env.client.forkchoiceUpdated(version, fcState)
|
||||
p.testFCU(valid)
|
||||
inc payloadNumber
|
||||
|
||||
|
||||
# Verify withdrawals changed
|
||||
sidechainwdHistory.VerifyWithdrawals(
|
||||
sidechainHeight,
|
||||
nil,
|
||||
t.TestEngine,
|
||||
)
|
||||
let r2 = sidechain.wdHistory.verifyWithdrawals(sidechain.height, none(UInt256), env.client)
|
||||
testCond r2.isOk
|
||||
|
||||
# Verify all balances of accounts in the original chain didn't increase
|
||||
# after the fork.
|
||||
# We are using different accounts credited between the canonical chain
|
||||
# and the fork.
|
||||
# We check on `latest`.
|
||||
ws.wdHistory.VerifyWithdrawals(
|
||||
ws.WithdrawalsForkHeight-1,
|
||||
nil,
|
||||
t.TestEngine,
|
||||
)
|
||||
let r3 = ws.wdHistory.verifyWithdrawals(uint64(ws.wdForkHeight-1), none(UInt256), env.client)
|
||||
testCond r3.isOk
|
||||
|
||||
# Re-Org back to the canonical chain
|
||||
r := t.rpcClient.forkchoiceUpdatedV2(&beacon.ForkchoiceStateV1{
|
||||
HeadBlockHash: t.clMock.latestPayloadBuilt.BlockHash,
|
||||
}, nil)
|
||||
r.expectPayloadStatus(test.Valid)
|
||||
]#
|
||||
let fcState = ForkchoiceStateV1(headBlockHash: env.clMock.latestPayloadBuilt.blockHash)
|
||||
let r = env.client.forkchoiceUpdatedV2(fcState)
|
||||
r.testFCU(valid)
|
||||
|
|
|
@ -52,6 +52,11 @@ type
|
|||
Option[PayloadAttributesV2] |
|
||||
Option[PayloadAttributesV3]
|
||||
|
||||
GetPayloadResponse* = object
|
||||
executionPayload*: ExecutionPayload
|
||||
blockValue*: Option[UInt256]
|
||||
blobsBundle*: Option[BlobsBundleV1]
|
||||
|
||||
Version* {.pure.} = enum
|
||||
V1
|
||||
V2
|
||||
|
@ -59,21 +64,27 @@ type
|
|||
|
||||
func version*(payload: ExecutionPayload): Version =
|
||||
if payload.blobGasUsed.isSome and payload.excessBlobGas.isSome:
|
||||
return Version.V3
|
||||
|
||||
if payload.withdrawals.isSome:
|
||||
return Version.V2
|
||||
|
||||
Version.V1
|
||||
Version.V3
|
||||
elif payload.withdrawals.isSome:
|
||||
Version.V2
|
||||
else:
|
||||
Version.V1
|
||||
|
||||
func version*(attr: PayloadAttributes): Version =
|
||||
if attr.parentBeaconBlockRoot.isSome:
|
||||
return Version.V3
|
||||
Version.V3
|
||||
elif attr.withdrawals.isSome:
|
||||
Version.V2
|
||||
else:
|
||||
Version.V1
|
||||
|
||||
if attr.withdrawals.isSome:
|
||||
return Version.V2
|
||||
|
||||
Version.V1
|
||||
func version*(res: GetPayloadResponse): Version =
|
||||
if res.blobsBundle.isSome:
|
||||
Version.V3
|
||||
elif res.blockValue.isSome:
|
||||
Version.V2
|
||||
else:
|
||||
Version.V1
|
||||
|
||||
func V1V2*(attr: PayloadAttributes): PayloadAttributesV1OrV2 =
|
||||
PayloadAttributesV1OrV2(
|
||||
|
@ -107,6 +118,22 @@ func V3*(attr: PayloadAttributes): PayloadAttributesV3 =
|
|||
parentBeaconBlockRoot: attr.parentBeaconBlockRoot.get
|
||||
)
|
||||
|
||||
func V1*(attr: Option[PayloadAttributes]): Option[PayloadAttributesV1] =
|
||||
if attr.isNone:
|
||||
return none(PayloadAttributesV1)
|
||||
some(attr.get.V1)
|
||||
|
||||
when false:
|
||||
func V2*(attr: Option[PayloadAttributes]): Option[PayloadAttributesV2] =
|
||||
if attr.isNone:
|
||||
return none(PayloadAttributesV2)
|
||||
some(attr.get.V2)
|
||||
|
||||
func V3*(attr: Option[PayloadAttributes]): Option[PayloadAttributesV3] =
|
||||
if attr.isNone:
|
||||
return none(PayloadAttributesV3)
|
||||
some(attr.get.V3)
|
||||
|
||||
func payloadAttributes*(attr: PayloadAttributesV1): PayloadAttributes =
|
||||
PayloadAttributes(
|
||||
timestamp: attr.timestamp,
|
||||
|
@ -308,3 +335,19 @@ func executionPayload*(p: ExecutionPayloadV1OrV2): ExecutionPayload =
|
|||
transactions: p.transactions,
|
||||
withdrawals: p.withdrawals
|
||||
)
|
||||
|
||||
func V1*(res: GetPayloadResponse): ExecutionPayloadV1 =
|
||||
res.executionPayload.V1
|
||||
|
||||
func V2*(res: GetPayloadResponse): GetPayloadV2Response =
|
||||
GetPayloadV2Response(
|
||||
executionPayload: res.executionPayload.V1V2,
|
||||
blockValue: res.blockValue.get
|
||||
)
|
||||
|
||||
func V3*(res: GetPayloadResponse): GetPayloadV3Response =
|
||||
GetPayloadV3Response(
|
||||
executionPayload: res.executionPayload.V3,
|
||||
blockValue: res.blockValue.get,
|
||||
blobsBundle: res.blobsBundle.get
|
||||
)
|
||||
|
|
|
@ -21,7 +21,7 @@ const
|
|||
# maxTrackedHeaders is the maximum number of executed payloads the execution
|
||||
# engine tracks before evicting old ones. Ideally we should only ever track
|
||||
# the latest one; but have a slight wiggle room for non-ideal conditions.
|
||||
MaxTrackedHeaders = 10
|
||||
MaxTrackedHeaders = 96
|
||||
|
||||
type
|
||||
QueueItem[T] = object
|
||||
|
|
|
@ -56,7 +56,7 @@ proc `$`*(x: Web3Quantity): string =
|
|||
|
||||
proc `$`*(x: Web3Address): string =
|
||||
distinctBase(x).toHex
|
||||
|
||||
|
||||
proc short*(x: Web3Hash): string =
|
||||
let z = common.Hash256(data: distinctBase x)
|
||||
short(z)
|
||||
|
@ -168,6 +168,9 @@ func w3Qty*(x: common.EthTime): Web3Quantity =
|
|||
func w3Qty*(x: common.EthTime, y: int): Web3Quantity =
|
||||
Web3Quantity(x.toUnix + y.int64)
|
||||
|
||||
func w3Qty*(x: Web3Quantity, y: int): Web3Quantity =
|
||||
Web3Quantity(x.uint64 + y.uint64)
|
||||
|
||||
func w3Qty*(x: Option[uint64]): Option[Web3Quantity] =
|
||||
if x.isNone: none(Web3Quantity)
|
||||
else: some(Web3Quantity x.get)
|
||||
|
|
|
@ -114,12 +114,17 @@ proc txFeesCovered(xp: TxPoolRef; item: TxItemRef): bool =
|
|||
return false
|
||||
true
|
||||
|
||||
import stew/byteutils
|
||||
import ../../../utils/debug
|
||||
|
||||
proc txCostInBudget(xp: TxPoolRef; item: TxItemRef): bool =
|
||||
## Check whether the worst case expense is covered by the price budget,
|
||||
let
|
||||
balance = xp.chain.getBalance(item.sender)
|
||||
gasCost = item.tx.gasCost
|
||||
if balance < gasCost:
|
||||
debugEcho "nonce: ", item.tx.nonce, " ", balance, " ", gasCost, " ", item.sender.toHex
|
||||
debugEcho debug(item.tx)
|
||||
debug "invalid tx: not enough cash for gas",
|
||||
available = balance,
|
||||
require = gasCost
|
||||
|
|
|
@ -18,7 +18,6 @@ import
|
|||
".."/[protocol, sync_desc],
|
||||
./worker_desc,
|
||||
./skeleton_main,
|
||||
./skeleton_utils,
|
||||
./beacon_impl
|
||||
|
||||
logScope:
|
||||
|
|
Loading…
Reference in New Issue