Commit c5daf6b7 authored by protolambda's avatar protolambda

op-e2e: add e2e geth test for pending block behavior, add TODOs on pending-block number usage

parent e00113e1
...@@ -100,6 +100,126 @@ func TestInvalidDepositInFCU(t *testing.T) { ...@@ -100,6 +100,126 @@ func TestInvalidDepositInFCU(t *testing.T) {
require.Equal(t, 0, balance.Cmp(common.Big0)) require.Equal(t, 0, balance.Cmp(common.Big0))
} }
// TestGethOnlyPendingBlockIsLatest walks through an engine-API block building job,
// and asserts that the pending block is set to match the latest block at every stage,
// for stability and tx-privacy.
func TestGethOnlyPendingBlockIsLatest(t *testing.T) {
InitParallel(t)
cfg := DefaultSystemConfig(t)
cfg.DeployConfig.FundDevAccounts = true
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
checkPending := func(stage string, number uint64) {
// TODO(CLI-4044): pending-block ID change
pendingBlock, err := opGeth.L2Client.BlockByNumber(ctx, big.NewInt(-1))
require.NoError(t, err, "failed to fetch pending block at stage "+stage)
require.Equal(t, number, pendingBlock.NumberU64(), "pending block must have expected number")
latestBlock, err := opGeth.L2Client.BlockByNumber(ctx, nil)
require.NoError(t, err, "failed to fetch latest block at stage "+stage)
require.Equal(t, pendingBlock.Hash(), latestBlock.Hash(), "pending and latest do not match at stage "+stage)
}
checkPending("genesis", 0)
amount := big.NewInt(42) // send 42 wei
aliceStartBalance, err := opGeth.L2Client.PendingBalanceAt(ctx, cfg.Secrets.Addresses().Alice)
require.NoError(t, err)
require.True(t, aliceStartBalance.Cmp(big.NewInt(0)) > 0, "alice must be funded")
checkPendingBalance := func() {
pendingBalance, err := opGeth.L2Client.PendingBalanceAt(ctx, cfg.Secrets.Addresses().Alice)
require.NoError(t, err)
require.Equal(t, pendingBalance, aliceStartBalance, "pending balance must still be the same")
}
startBlock, err := opGeth.L2Client.BlockByNumber(ctx, nil)
require.NoError(t, err)
signer := types.LatestSigner(opGeth.L2ChainConfig)
tip := big.NewInt(7_000_000_000) // 7 gwei tip
tx := types.MustSignNewTx(cfg.Secrets.Alice, signer, &types.DynamicFeeTx{
ChainID: big.NewInt(int64(cfg.DeployConfig.L2ChainID)),
Nonce: 0,
GasTipCap: tip,
GasFeeCap: new(big.Int).Add(startBlock.BaseFee(), tip),
Gas: 1_000_000,
To: &cfg.Secrets.Addresses().Bob,
Value: amount,
Data: nil,
})
require.NoError(t, opGeth.L2Client.SendTransaction(ctx, tx), "send tx to make pending work different")
checkPending("prepared", 0)
rpcClient, err := opGeth.node.Attach()
require.NoError(t, err)
defer rpcClient.Close()
// Wait for tx to be in tx-pool, for it to be picked up in block building
var txPoolStatus struct {
Pending hexutil.Uint64 `json:"pending"`
}
for i := 0; i < 5; i++ {
require.NoError(t, rpcClient.CallContext(ctx, &txPoolStatus, "txpool_status"))
if txPoolStatus.Pending == 0 {
time.Sleep(time.Second)
} else {
break
}
}
require.NotZero(t, txPoolStatus.Pending, "must have pending tx in pool")
checkPending("in-pool", 0)
checkPendingBalance()
// start building a block
attrs, err := opGeth.CreatePayloadAttributes()
require.NoError(t, err)
attrs.NoTxPool = false // we want to include a tx
fc := eth.ForkchoiceState{
HeadBlockHash: opGeth.L2Head.BlockHash,
SafeBlockHash: opGeth.L2Head.BlockHash,
}
res, err := opGeth.l2Engine.ForkchoiceUpdate(ctx, &fc, attrs)
require.NoError(t, err)
checkPending("building", 0)
checkPendingBalance()
// Now we have to wait until the block-building job picks up the tx from the tx-pool.
// See go routine that spins up in buildPayload() func in payload_building.go in miner package.
// We can't check it, we don't want to finish block-building prematurely, and so we have to wait.
time.Sleep(time.Second * 4) // conservatively wait 4 seconds, CI might lag during block building.
// retrieve the block
payload, err := opGeth.l2Engine.GetPayload(ctx, *res.PayloadID)
require.NoError(t, err)
checkPending("retrieved", 0)
require.Len(t, payload.Transactions, 2, "must include L1 info tx and tx from alice")
checkPendingBalance()
// process the block
status, err := opGeth.l2Engine.NewPayload(ctx, payload)
require.NoError(t, err)
require.Equal(t, eth.ExecutionValid, status.Status)
checkPending("processed", 0)
checkPendingBalance()
// make the block canonical
fc = eth.ForkchoiceState{
HeadBlockHash: payload.BlockHash,
SafeBlockHash: payload.BlockHash,
}
res, err = opGeth.l2Engine.ForkchoiceUpdate(ctx, &fc, nil)
require.NoError(t, err)
require.Equal(t, eth.ExecutionValid, res.PayloadStatus.Status)
checkPending("canonical", 1)
}
func TestPreregolith(t *testing.T) { func TestPreregolith(t *testing.T) {
InitParallel(t) InitParallel(t)
futureTimestamp := hexutil.Uint64(4) futureTimestamp := hexutil.Uint64(4)
......
...@@ -1394,6 +1394,7 @@ func TestPendingBlockIsLatest(t *testing.T) { ...@@ -1394,6 +1394,7 @@ func TestPendingBlockIsLatest(t *testing.T) {
t.Run("block", func(t *testing.T) { t.Run("block", func(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
// TODO(CLI-4044): pending-block ID change
pending, err := l2Seq.BlockByNumber(context.Background(), big.NewInt(-1)) pending, err := l2Seq.BlockByNumber(context.Background(), big.NewInt(-1))
require.NoError(t, err) require.NoError(t, err)
latest, err := l2Seq.BlockByNumber(context.Background(), nil) latest, err := l2Seq.BlockByNumber(context.Background(), nil)
...@@ -1408,6 +1409,7 @@ func TestPendingBlockIsLatest(t *testing.T) { ...@@ -1408,6 +1409,7 @@ func TestPendingBlockIsLatest(t *testing.T) {
}) })
t.Run("header", func(t *testing.T) { t.Run("header", func(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
// TODO(CLI-4044): pending-block ID change
pending, err := l2Seq.HeaderByNumber(context.Background(), big.NewInt(-1)) pending, err := l2Seq.HeaderByNumber(context.Background(), big.NewInt(-1))
require.NoError(t, err) require.NoError(t, err)
latest, err := l2Seq.HeaderByNumber(context.Background(), nil) latest, err := l2Seq.HeaderByNumber(context.Background(), nil)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment