Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add retryable expiry system tests #2799

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 183 additions & 2 deletions system_tests/retryable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/gasestimator"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"

"github.com/offchainlabs/nitro/arbnode"
"github.com/offchainlabs/nitro/arbos"
"github.com/offchainlabs/nitro/arbos/arbostypes"
"github.com/offchainlabs/nitro/arbos/l1pricing"
"github.com/offchainlabs/nitro/arbos/l2pricing"
"github.com/offchainlabs/nitro/arbos/retryables"
"github.com/offchainlabs/nitro/arbos/util"
Expand Down Expand Up @@ -445,6 +447,186 @@ func TestGetLifetime(t *testing.T) {
}
}

func warpL1Time(t *testing.T, builder *NodeBuilder, ctx context.Context, currentL1time, advanceTime uint64) uint64 {
t.Log("Warping L1 time...")
l1LatestHeader, err := builder.L1.Client.HeaderByNumber(ctx, big.NewInt(int64(rpc.LatestBlockNumber)))
Require(t, err)
if currentL1time == 0 {
currentL1time = l1LatestHeader.Time
}
newL1Timestamp := currentL1time + advanceTime
timeWarpHeader := &arbostypes.L1IncomingMessageHeader{
Kind: arbostypes.L1MessageType_L2Message,
Poster: l1pricing.BatchPosterAddress,
BlockNumber: l1LatestHeader.Number.Uint64(),
Timestamp: newL1Timestamp,
RequestId: nil,
L1BaseFee: nil,
}
hooks := arbos.NoopSequencingHooks()
tx := builder.L2Info.PrepareTx("Faucet", "User2", 300000, big.NewInt(1), nil)
_, err = builder.L2.ExecNode.ExecEngine.SequenceTransactions(timeWarpHeader, types.Transactions{tx}, hooks)
Require(t, err)
return newL1Timestamp
}

func TestRetryableExpiry(t *testing.T) {
t.Parallel()
builder, delayedInbox, lookupL2Tx, ctx, teardown := retryableSetup(t)
defer teardown()

ownerTxOpts := builder.L2Info.GetDefaultTransactOpts("Owner", ctx)
usertxopts := builder.L1Info.GetDefaultTransactOpts("Faucet", ctx)
usertxopts.Value = arbmath.BigMul(big.NewInt(1e12), big.NewInt(1e12))

simpleAddr, _ := builder.L2.DeploySimple(t, ownerTxOpts)
simpleABI, err := mocksgen.SimpleMetaData.GetAbi()
Require(t, err)

beneficiaryAddress := builder.L2Info.GetAddress("Beneficiary")
l1tx, err := delayedInbox.CreateRetryableTicket(
&usertxopts,
simpleAddr,
common.Big0,
big.NewInt(1e16),
beneficiaryAddress,
beneficiaryAddress,
// send enough L2 gas for intrinsic but not compute
big.NewInt(int64(params.TxGas+params.TxDataNonZeroGasEIP2028*4)),
big.NewInt(l2pricing.InitialBaseFeeWei*2),
simpleABI.Methods["incrementRedeem"].ID,
)
Require(t, err)

l1Receipt, err := builder.L1.EnsureTxSucceeded(l1tx)
Require(t, err)
if l1Receipt.Status != types.ReceiptStatusSuccessful {
Fatal(t, "l1Receipt indicated failure")
}

waitForL1DelayBlocks(t, builder)

receipt, err := builder.L2.EnsureTxSucceeded(lookupL2Tx(l1Receipt))
Require(t, err)
if len(receipt.Logs) != 2 {
Fatal(t, len(receipt.Logs))
}
ticketId := receipt.Logs[0].Topics[1]
firstRetryTxId := receipt.Logs[1].Topics[2]

// make sure it failed
receipt, err = WaitForTx(ctx, builder.L2.Client, firstRetryTxId, time.Second*5)
Require(t, err)
if receipt.Status != types.ReceiptStatusFailed {
Fatal(t, receipt.GasUsed)
}
Comment on lines +475 to +522
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior is being implemented several times already in this file, how about having another setup function to abstract that and avoid code repetition?


arbRetryableTx, err := precompilesgen.NewArbRetryableTx(common.HexToAddress("6e"), builder.L2.Client)
Require(t, err)

// check that the ticket exists
_, err = arbRetryableTx.GetTimeout(&bind.CallOpts{}, ticketId)
Require(t, err)

_ = warpL1Time(t, builder, ctx, 0, retryables.RetryableLifetimeSeconds)

// check that the ticket no longer exists
_, err = arbRetryableTx.GetTimeout(&bind.CallOpts{}, ticketId)
if (err == nil) || (err.Error() != "execution reverted: error NoTicketWithID(): NoTicketWithID()") {
Fatal(t, "didn't get expected NoTicketWithID error")
}
}

func TestKeepaliveAndRetryableExpiry(t *testing.T) {
t.Parallel()
builder, delayedInbox, lookupL2Tx, ctx, teardown := retryableSetup(t)
defer teardown()

ownerTxOpts := builder.L2Info.GetDefaultTransactOpts("Owner", ctx)
usertxopts := builder.L1Info.GetDefaultTransactOpts("Faucet", ctx)
usertxopts.Value = arbmath.BigMul(big.NewInt(1e12), big.NewInt(1e12))

simpleAddr, _ := builder.L2.DeploySimple(t, ownerTxOpts)
simpleABI, err := mocksgen.SimpleMetaData.GetAbi()
Require(t, err)

beneficiaryAddress := builder.L2Info.GetAddress("Beneficiary")
l1tx, err := delayedInbox.CreateRetryableTicket(
&usertxopts,
simpleAddr,
common.Big0,
big.NewInt(1e16),
beneficiaryAddress,
beneficiaryAddress,
// send enough L2 gas for intrinsic but not compute
big.NewInt(int64(params.TxGas+params.TxDataNonZeroGasEIP2028*4)),
big.NewInt(l2pricing.InitialBaseFeeWei*2),
simpleABI.Methods["incrementRedeem"].ID,
)
Require(t, err)

l1Receipt, err := builder.L1.EnsureTxSucceeded(l1tx)
Require(t, err)
if l1Receipt.Status != types.ReceiptStatusSuccessful {
Fatal(t, "l1Receipt indicated failure")
}

waitForL1DelayBlocks(t, builder)

receipt, err := builder.L2.EnsureTxSucceeded(lookupL2Tx(l1Receipt))
Require(t, err)
if len(receipt.Logs) != 2 {
Fatal(t, len(receipt.Logs))
}
ticketId := receipt.Logs[0].Topics[1]
firstRetryTxId := receipt.Logs[1].Topics[2]

// make sure it failed
receipt, err = WaitForTx(ctx, builder.L2.Client, firstRetryTxId, time.Second*5)
Require(t, err)
if receipt.Status != types.ReceiptStatusFailed {
Fatal(t, receipt.GasUsed)
}

arbRetryableTx, err := precompilesgen.NewArbRetryableTx(common.HexToAddress("6e"), builder.L2.Client)
Require(t, err)

// checks that the ticket exists and gets current timeout
timeoutBeforeKeepalive, err := arbRetryableTx.GetTimeout(&bind.CallOpts{}, ticketId)
Require(t, err)

// checks beneficiary
retrievedBeneficiaryAddress, err := arbRetryableTx.GetBeneficiary(&bind.CallOpts{}, ticketId)
Require(t, err)
if retrievedBeneficiaryAddress != beneficiaryAddress {
Fatal(t, "expected beneficiary to be", beneficiaryAddress, "but got", retrievedBeneficiaryAddress)
}

// checks that keepalive increases the timeout as expected
_, err = arbRetryableTx.Keepalive(&ownerTxOpts, ticketId)
Require(t, err)
timeoutAfterKeepalive, err := arbRetryableTx.GetTimeout(&bind.CallOpts{}, ticketId)
Require(t, err)
expectedTimeoutAfterKeepAlive := arbmath.BigAdd(timeoutBeforeKeepalive, big.NewInt(retryables.RetryableLifetimeSeconds))
if timeoutAfterKeepalive.Cmp(expectedTimeoutAfterKeepAlive) != 0 {
Fatal(t, "expected timeout after keepalive to be", expectedTimeoutAfterKeepAlive, "but got", timeoutAfterKeepalive)
}

currentL1time := warpL1Time(t, builder, ctx, 0, retryables.RetryableLifetimeSeconds)

// check that the ticket still exists
_, err = arbRetryableTx.GetTimeout(&bind.CallOpts{}, ticketId)
Require(t, err)

_ = warpL1Time(t, builder, ctx, currentL1time, retryables.RetryableLifetimeSeconds)

// check that the ticket no longer exists
_, err = arbRetryableTx.GetTimeout(&bind.CallOpts{}, ticketId)
if (err == nil) || (err.Error() != "execution reverted: error NoTicketWithID(): NoTicketWithID()") {
Fatal(t, "didn't get expected NoTicketWithID error")
}
}

func TestKeepaliveAndCancelRetryable(t *testing.T) {
t.Parallel()
builder, delayedInbox, lookupL2Tx, ctx, teardown := retryableSetup(t)
Expand Down Expand Up @@ -515,8 +697,7 @@ func TestKeepaliveAndCancelRetryable(t *testing.T) {
Require(t, err)
timeoutAfterKeepalive, err := arbRetryableTx.GetTimeout(&bind.CallOpts{}, ticketId)
Require(t, err)
expectedTimeoutAfterKeepAlive := timeoutBeforeKeepalive
expectedTimeoutAfterKeepAlive.Add(expectedTimeoutAfterKeepAlive, big.NewInt(retryables.RetryableLifetimeSeconds))
expectedTimeoutAfterKeepAlive := arbmath.BigAdd(timeoutBeforeKeepalive, big.NewInt(retryables.RetryableLifetimeSeconds))
if timeoutAfterKeepalive.Cmp(expectedTimeoutAfterKeepAlive) != 0 {
Fatal(t, "expected timeout after keepalive to be", expectedTimeoutAfterKeepAlive, "but got", timeoutAfterKeepalive)
}
Expand Down
Loading