From ecbe0946b845c97c5d962db5c41703e3271e7b51 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 18 Oct 2024 10:29:12 -0300 Subject: [PATCH 01/12] Add function to get delay buffer config --- arbnode/delay_buffer.go | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 arbnode/delay_buffer.go diff --git a/arbnode/delay_buffer.go b/arbnode/delay_buffer.go new file mode 100644 index 0000000000..5cdfb0edb7 --- /dev/null +++ b/arbnode/delay_buffer.go @@ -0,0 +1,57 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +// This file contains functions related to the delay buffer feature that are used mostly in the +// batch poster. + +package arbnode + +import ( + "context" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/offchainlabs/nitro/solgen/go/bridgegen" + "github.com/offchainlabs/nitro/util/headerreader" +) + +// DelayBufferConfig originates from the sequencer inbox contract. +type DelayBufferConfig struct { + Enabled bool + Threshold uint64 +} + +// GetBufferConfig gets the delay buffer config from the sequencer inbox contract. +// If the contract doesn't support the delay buffer, it returns a config with Enabled set to false. +func GetDelayBufferConfig(ctx context.Context, client *ethclient.Client, sequencerInboxAddress common.Address) ( + *DelayBufferConfig, error) { + + sequencerInbox, err := bridgegen.NewSequencerInbox(sequencerInboxAddress, client) + if err != nil { + return nil, fmt.Errorf("create sequencer inbox binding: %w", err) + } + callOpts := bind.CallOpts{ + Context: ctx, + } + enabled, err := sequencerInbox.IsDelayBufferable(&callOpts) + if err != nil { + if headerreader.ExecutionRevertedRegexp.MatchString(err.Error()) { + return &DelayBufferConfig{Enabled: false}, nil + } + return nil, fmt.Errorf("retrieve SequencerInbox.isDelayBufferable: %w", err) + } + if !enabled { + return &DelayBufferConfig{Enabled: false}, nil + } + bufferData, err := sequencerInbox.Buffer(&callOpts) + if err != nil { + return nil, fmt.Errorf("retrieve SequencerInbox.buffer: %w", err) + } + config := &DelayBufferConfig{ + Enabled: true, + Threshold: bufferData.Threshold, + } + return config, nil +} From ef66893d3e991ed42d4915b670f15df424683158 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 18 Oct 2024 11:24:07 -0300 Subject: [PATCH 02/12] Force batch to avoid consuming the delay buffer --- arbnode/batch_poster.go | 27 ++++++++++++++++++++++++++- arbnode/delay_buffer.go | 8 +------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 46a0160b71..6f546fa0b0 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -720,6 +720,7 @@ type buildingBatch struct { haveUsefulMessage bool use4844 bool muxBackend *simulatedMuxBackend + firstDelayedMsg *arbostypes.MessageWithMetadata firstNonDelayedMsg *arbostypes.MessageWithMetadata firstUsefulMsg *arbostypes.MessageWithMetadata } @@ -1314,7 +1315,11 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) b.building.firstUsefulMsg = msg } } - if !isDelayed && b.building.firstNonDelayedMsg == nil { + if isDelayed { + if b.building.firstDelayedMsg == nil { + b.building.firstDelayedMsg = msg + } + } else if b.building.firstNonDelayedMsg == nil { b.building.firstNonDelayedMsg = msg } b.building.msgCount++ @@ -1329,6 +1334,26 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) } } + delayBuffer, err := GetDelayBufferConfig(ctx, b.seqInbox) + if err != nil { + return false, err + } + if delayBuffer.Enabled && b.building.firstDelayedMsg != nil { + latestHeader, err := b.l1Reader.LastHeader(ctx) + if err != nil { + return false, err + } + latestBlock := latestHeader.Number.Uint64() + firstDelayedMsgBlock := b.building.firstDelayedMsg.Message.Header.BlockNumber + if firstDelayedMsgBlock+delayBuffer.Threshold >= latestBlock { + log.Info("force post batch because of the delay buffer", + "firstDelayedMsgBlock", firstDelayedMsgBlock, + "threshold", delayBuffer.Threshold, + "latestBlock", latestBlock) + forcePostBatch = true + } + } + if b.building.firstNonDelayedMsg != nil && hasL1Bound && config.ReorgResistanceMargin > 0 { firstMsgBlockNumber := b.building.firstNonDelayedMsg.Message.Header.BlockNumber firstMsgTimeStamp := b.building.firstNonDelayedMsg.Message.Header.Timestamp diff --git a/arbnode/delay_buffer.go b/arbnode/delay_buffer.go index 5cdfb0edb7..cc62cd716a 100644 --- a/arbnode/delay_buffer.go +++ b/arbnode/delay_buffer.go @@ -11,8 +11,6 @@ import ( "fmt" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethclient" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/util/headerreader" ) @@ -25,13 +23,9 @@ type DelayBufferConfig struct { // GetBufferConfig gets the delay buffer config from the sequencer inbox contract. // If the contract doesn't support the delay buffer, it returns a config with Enabled set to false. -func GetDelayBufferConfig(ctx context.Context, client *ethclient.Client, sequencerInboxAddress common.Address) ( +func GetDelayBufferConfig(ctx context.Context, sequencerInbox *bridgegen.SequencerInbox) ( *DelayBufferConfig, error) { - sequencerInbox, err := bridgegen.NewSequencerInbox(sequencerInboxAddress, client) - if err != nil { - return nil, fmt.Errorf("create sequencer inbox binding: %w", err) - } callOpts := bind.CallOpts{ Context: ctx, } From a05ee98f41f7ed5b111c946033ae145ac0f13219 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Mon, 21 Oct 2024 11:30:30 -0300 Subject: [PATCH 03/12] Add delay proof when posting a batch --- arbnode/batch_poster.go | 73 ++++++++++++++++++++++++++++++++++++----- arbnode/delay_buffer.go | 38 +++++++++++++++++++-- 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 6f546fa0b0..43b22c016e 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -80,8 +80,10 @@ var ( const ( batchPosterSimpleRedisLockKey = "node.batch-poster.redis-lock.simple-lock-key" - sequencerBatchPostMethodName = "addSequencerL2BatchFromOrigin0" - sequencerBatchPostWithBlobsMethodName = "addSequencerL2BatchFromBlobs" + sequencerBatchPostMethodName = "addSequencerL2BatchFromOrigin0" + sequencerBatchPostWithBlobsMethodName = "addSequencerL2BatchFromBlobs" + sequencerBatchPostDelayProofMethodName = "addSequencerL2BatchFromOriginDelayProof" + sequencerBatchPostWithBlobsDelayProofMethodName = "addSequencerL2BatchFromBlobsDelayProof" ) type batchPosterPosition struct { @@ -315,6 +317,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e if err = opts.Config().Validate(); err != nil { return nil, err } + // TODO(delaybuffer) use new bridgegen seqInboxABI, err := bridgegen.SequencerInboxMetaData.GetAbi() if err != nil { return nil, err @@ -959,15 +962,25 @@ func (b *BatchPoster) encodeAddBatch( l2MessageData []byte, delayedMsg uint64, use4844 bool, + delayProof *bridgegen.DelayProof, ) ([]byte, []kzg4844.Blob, error) { - methodName := sequencerBatchPostMethodName + var methodName string if use4844 { - methodName = sequencerBatchPostWithBlobsMethodName + if delayProof != nil { + methodName = sequencerBatchPostWithBlobsDelayProofMethodName + } else { + methodName = sequencerBatchPostWithBlobsMethodName + } + } else if delayProof != nil { + methodName = sequencerBatchPostDelayProofMethodName + } else { + methodName = sequencerBatchPostMethodName } method, ok := b.seqInboxABI.Methods[methodName] if !ok { return nil, nil, errors.New("failed to find add batch method") } + var calldata []byte var kzgBlobs []kzg4844.Blob var err error @@ -976,6 +989,9 @@ func (b *BatchPoster) encodeAddBatch( if err != nil { return nil, nil, fmt.Errorf("failed to encode blobs: %w", err) } + } + switch methodName { + case sequencerBatchPostWithBlobsMethodName: // EIP4844 transactions to the sequencer inbox will not use transaction calldata for L2 info. calldata, err = method.Inputs.Pack( seqNum, @@ -984,7 +1000,16 @@ func (b *BatchPoster) encodeAddBatch( new(big.Int).SetUint64(uint64(prevMsgNum)), new(big.Int).SetUint64(uint64(newMsgNum)), ) - } else { + case sequencerBatchPostWithBlobsDelayProofMethodName: + calldata, err = method.Inputs.Pack( + seqNum, + new(big.Int).SetUint64(delayedMsg), + b.config().gasRefunder, + new(big.Int).SetUint64(uint64(prevMsgNum)), + new(big.Int).SetUint64(uint64(newMsgNum)), + delayProof, + ) + case sequencerBatchPostMethodName: calldata, err = method.Inputs.Pack( seqNum, l2MessageData, @@ -993,6 +1018,18 @@ func (b *BatchPoster) encodeAddBatch( new(big.Int).SetUint64(uint64(prevMsgNum)), new(big.Int).SetUint64(uint64(newMsgNum)), ) + case sequencerBatchPostDelayProofMethodName: + calldata, err = method.Inputs.Pack( + seqNum, + l2MessageData, + new(big.Int).SetUint64(delayedMsg), + b.config().gasRefunder, + new(big.Int).SetUint64(uint64(prevMsgNum)), + new(big.Int).SetUint64(uint64(newMsgNum)), + delayProof, + ) + default: + panic("impossible") } if err != nil { return nil, nil, err @@ -1019,7 +1056,17 @@ func estimateGas(client rpc.ClientInterface, ctx context.Context, params estimat return uint64(gas), err } -func (b *BatchPoster) estimateGas(ctx context.Context, sequencerMessage []byte, delayedMessages uint64, realData []byte, realBlobs []kzg4844.Blob, realNonce uint64, realAccessList types.AccessList) (uint64, error) { +func (b *BatchPoster) estimateGas( + ctx context.Context, + sequencerMessage []byte, + delayedMessages uint64, + realData []byte, + realBlobs []kzg4844.Blob, + realNonce uint64, + realAccessList types.AccessList, + delayProof *bridgegen.DelayProof, +) (uint64, error) { + config := b.config() rpcClient := b.l1Reader.Client() rawRpcClient := rpcClient.Client() @@ -1061,7 +1108,7 @@ func (b *BatchPoster) estimateGas(ctx context.Context, sequencerMessage []byte, // However, we set nextMsgNum to 1 because it is necessary for a correct estimation for the final to be non-zero. // Because we're likely estimating against older state, this might not be the actual next message, // but the gas used should be the same. - data, kzgBlobs, err := b.encodeAddBatch(abi.MaxUint256, 0, 1, sequencerMessage, delayedMessages, len(realBlobs) > 0) + data, kzgBlobs, err := b.encodeAddBatch(abi.MaxUint256, 0, 1, sequencerMessage, delayedMessages, len(realBlobs) > 0, delayProof) if err != nil { return 0, err } @@ -1445,7 +1492,15 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) prevMessageCount = 0 } - data, kzgBlobs, err := b.encodeAddBatch(new(big.Int).SetUint64(batchPosition.NextSeqNum), prevMessageCount, b.building.msgCount, sequencerMsg, b.building.segments.delayedMsg, b.building.use4844) + var delayProof *bridgegen.DelayProof + if delayBuffer.Enabled && b.building.firstDelayedMsg != nil { + delayProof, err = GenDelayProof(ctx, b.building.firstDelayedMsg, b.inbox) + if err != nil { + return false, fmt.Errorf("failed to generate delay proof: %w", err) + } + } + + data, kzgBlobs, err := b.encodeAddBatch(new(big.Int).SetUint64(batchPosition.NextSeqNum), prevMessageCount, b.building.msgCount, sequencerMsg, b.building.segments.delayedMsg, b.building.use4844, delayProof) if err != nil { return false, err } @@ -1460,7 +1515,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) // In theory, this might reduce gas usage, but only by a factor that's already // accounted for in `config.ExtraBatchGas`, as that same factor can appear if a user // posts a new delayed message that we didn't see while gas estimating. - gasLimit, err := b.estimateGas(ctx, sequencerMsg, lastPotentialMsg.DelayedMessagesRead, data, kzgBlobs, nonce, accessList) + gasLimit, err := b.estimateGas(ctx, sequencerMsg, lastPotentialMsg.DelayedMessagesRead, data, kzgBlobs, nonce, accessList, delayProof) if err != nil { return false, err } diff --git a/arbnode/delay_buffer.go b/arbnode/delay_buffer.go index cc62cd716a..c0e317e27c 100644 --- a/arbnode/delay_buffer.go +++ b/arbnode/delay_buffer.go @@ -9,8 +9,12 @@ package arbnode import ( "context" "fmt" + "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/util/headerreader" ) @@ -26,9 +30,7 @@ type DelayBufferConfig struct { func GetDelayBufferConfig(ctx context.Context, sequencerInbox *bridgegen.SequencerInbox) ( *DelayBufferConfig, error) { - callOpts := bind.CallOpts{ - Context: ctx, - } + callOpts := bind.CallOpts{Context: ctx} enabled, err := sequencerInbox.IsDelayBufferable(&callOpts) if err != nil { if headerreader.ExecutionRevertedRegexp.MatchString(err.Error()) { @@ -49,3 +51,33 @@ func GetDelayBufferConfig(ctx context.Context, sequencerInbox *bridgegen.Sequenc } return config, nil } + +// GenDelayProof generates the delay proof based on batch's first delayed message and the delayed +// accumulater from the inbox. +func GenDelayProof(ctx context.Context, message *arbostypes.MessageWithMetadata, inbox *InboxTracker) ( + *bridgegen.DelayProof, error) { + + seqNum := message.DelayedMessagesRead + var beforeDelayedAcc common.Hash + if seqNum > 0 { + var err error + beforeDelayedAcc, err = inbox.GetDelayedAcc(seqNum) + if err != nil { + return nil, err + } + } + delayedMessage := bridgegen.MessagesMessage{ + Kind: message.Message.Header.Kind, + Sender: message.Message.Header.Poster, + BlockNumber: message.Message.Header.BlockNumber, + Timestamp: message.Message.Header.Timestamp, + InboxSeqNum: new(big.Int).SetUint64(seqNum), + BaseFeeL1: message.Message.Header.L1BaseFee, + MessageDataHash: crypto.Keccak256Hash(message.Message.L2msg), + } + delayProof := &bridgegen.DelayProof{ + BeforeDelayedAcc: beforeDelayedAcc, + DelayedMessage: delayedMessage, + } + return delayProof, nil +} From d388238dfcfef487a1942f2f56ecd4fb29450022 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Tue, 22 Oct 2024 15:52:20 -0300 Subject: [PATCH 04/12] Fix force-batch delay-buffer condition --- arbnode/batch_poster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 43b22c016e..0916aabf00 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1392,7 +1392,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) } latestBlock := latestHeader.Number.Uint64() firstDelayedMsgBlock := b.building.firstDelayedMsg.Message.Header.BlockNumber - if firstDelayedMsgBlock+delayBuffer.Threshold >= latestBlock { + if latestBlock > firstDelayedMsgBlock+delayBuffer.Threshold { log.Info("force post batch because of the delay buffer", "firstDelayedMsgBlock", firstDelayedMsgBlock, "threshold", delayBuffer.Threshold, From 25ef0dcddddf1831ac1ed42578423f9339e1c2a4 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Tue, 22 Oct 2024 16:47:08 -0300 Subject: [PATCH 05/12] Support the delay buffer in the deploy pkg --- arbnode/node.go | 11 +++++- cmd/deploy/deploy.go | 9 ++++- deploy/deploy.go | 43 +++++++++++++++--------- system_tests/common_test.go | 17 +++++++++- system_tests/full_challenge_impl_test.go | 1 + 5 files changed, 63 insertions(+), 18 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index 3613b986ac..d7d9465205 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -53,7 +53,15 @@ import ( "github.com/offchainlabs/nitro/wsbroadcastserver" ) -func GenerateRollupConfig(prod bool, wasmModuleRoot common.Hash, rollupOwner common.Address, chainConfig *params.ChainConfig, serializedChainConfig []byte, loserStakeEscrow common.Address) rollupgen.Config { +func DefaultBufferConfig() rollupgen.BufferConfig { + return rollupgen.BufferConfig{ + Threshold: 600, // 1 hour of blocks + Max: 14400, // 2 days of blocks + ReplenishRateInBasis: 500, // 5% + } +} + +func GenerateRollupConfig(prod bool, wasmModuleRoot common.Hash, rollupOwner common.Address, chainConfig *params.ChainConfig, serializedChainConfig []byte, loserStakeEscrow common.Address, bufferConfig rollupgen.BufferConfig) rollupgen.Config { var confirmPeriod uint64 if prod { confirmPeriod = 45818 @@ -77,6 +85,7 @@ func GenerateRollupConfig(prod bool, wasmModuleRoot common.Hash, rollupOwner com DelaySeconds: big.NewInt(60 * 60 * 24), FutureSeconds: big.NewInt(60 * 60), }, + BufferConfig: bufferConfig, } } diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index c70ceb1d94..6060c55586 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go @@ -17,6 +17,7 @@ import ( "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" + "github.com/offchainlabs/nitro/solgen/go/rollupgen" "github.com/offchainlabs/nitro/util/headerreader" "github.com/offchainlabs/nitro/validator/server_common" @@ -61,6 +62,7 @@ func main() { authorizevalidators := flag.Uint64("authorizevalidators", 0, "Number of validators to preemptively authorize") txTimeout := flag.Duration("txtimeout", 10*time.Minute, "Timeout when waiting for a transaction to be included in a block") prod := flag.Bool("prod", false, "Whether to configure the rollup for production or testing") + isDelayBufferable := flag.Bool("delayBufferable", false, "Whether the sequencer-inbox delay buffer is enabled") flag.Parse() l1ChainId := new(big.Int).SetUint64(*l1ChainIdUint) maxDataSize := new(big.Int).SetUint64(*maxDataSizeUint) @@ -170,6 +172,11 @@ func main() { panic(fmt.Errorf("failed to deserialize chain config: %w", err)) } + var bufferConfig rollupgen.BufferConfig + if *isDelayBufferable { + bufferConfig = arbnode.DefaultBufferConfig() + } + arbSys, _ := precompilesgen.NewArbSys(types.ArbSysAddress, l1client) l1Reader, err := headerreader.New(ctx, l1client, func() *headerreader.Config { return &headerReaderConfig }, arbSys) if err != nil { @@ -186,7 +193,7 @@ func main() { batchPosters, batchPosterManagerAddress, *authorizevalidators, - arbnode.GenerateRollupConfig(*prod, moduleRoot, ownerAddress, &chainConfig, chainConfigJson, loserEscrowAddress), + arbnode.GenerateRollupConfig(*prod, moduleRoot, ownerAddress, &chainConfig, chainConfigJson, loserEscrowAddress, bufferConfig), nativeToken, maxDataSize, true, diff --git a/deploy/deploy.go b/deploy/deploy.go index bb4b2e6594..858d06db89 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -49,16 +49,27 @@ func deployBridgeCreator(ctx context.Context, parentChainReader *headerreader.He return common.Address{}, fmt.Errorf("blob basefee reader deploy error: %w", err) } } - seqInboxTemplateEthBased, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize, reader4844, false) + seqInboxTemplateEthBased, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize, reader4844, false, false) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { return common.Address{}, fmt.Errorf("sequencer inbox eth based deploy error: %w", err) } - seqInboxTemplateERC20Based, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize, reader4844, true) + delayBufferableSeqInboxTemplateEthBased, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize, reader4844, false, true) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("delay bufferable sequencer inbox eth based deploy error: %w", err) + } + + seqInboxTemplateERC20Based, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize, reader4844, true, false) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { return common.Address{}, fmt.Errorf("sequencer inbox erc20 based deploy error: %w", err) } + delayBufferableSeqInboxTemplateERC20Based, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize, reader4844, true, true) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("delay bufferable sequencer inbox erc20 based deploy error: %w", err) + } inboxTemplate, tx, _, err := bridgegen.DeployInbox(auth, client, maxDataSize) err = andTxSucceeded(ctx, parentChainReader, tx, err) @@ -78,12 +89,13 @@ func deployBridgeCreator(ctx context.Context, parentChainReader *headerreader.He return common.Address{}, fmt.Errorf("outbox deploy error: %w", err) } - ethBasedTemplates := rollupgen.BridgeCreatorBridgeContracts{ - Bridge: bridgeTemplate, - SequencerInbox: seqInboxTemplateEthBased, - Inbox: inboxTemplate, - RollupEventInbox: rollupEventBridgeTemplate, - Outbox: outboxTemplate, + ethBasedTemplates := rollupgen.BridgeCreatorBridgeTemplates{ + Bridge: bridgeTemplate, + SequencerInbox: seqInboxTemplateEthBased, + DelayBufferableSequencerInbox: delayBufferableSeqInboxTemplateEthBased, + Inbox: inboxTemplate, + RollupEventInbox: rollupEventBridgeTemplate, + Outbox: outboxTemplate, } /// deploy ERC20 based templates @@ -111,12 +123,13 @@ func deployBridgeCreator(ctx context.Context, parentChainReader *headerreader.He return common.Address{}, fmt.Errorf("outbox deploy error: %w", err) } - erc20BasedTemplates := rollupgen.BridgeCreatorBridgeContracts{ - Bridge: erc20BridgeTemplate, - SequencerInbox: seqInboxTemplateERC20Based, - Inbox: erc20InboxTemplate, - RollupEventInbox: erc20RollupEventBridgeTemplate, - Outbox: erc20OutboxTemplate, + erc20BasedTemplates := rollupgen.BridgeCreatorBridgeTemplates{ + Bridge: erc20BridgeTemplate, + SequencerInbox: seqInboxTemplateERC20Based, + DelayBufferableSequencerInbox: delayBufferableSeqInboxTemplateERC20Based, + Inbox: erc20InboxTemplate, + RollupEventInbox: erc20RollupEventBridgeTemplate, + Outbox: erc20OutboxTemplate, } bridgeCreatorAddr, tx, _, err := rollupgen.DeployBridgeCreator(auth, client, ethBasedTemplates, erc20BasedTemplates) @@ -256,7 +269,6 @@ func DeployOnParentChain(ctx context.Context, parentChainReader *headerreader.He for i := uint64(1); i <= authorizeValidators; i++ { validatorAddrs = append(validatorAddrs, crypto.CreateAddress(validatorWalletCreator, i)) } - deployParams := rollupgen.RollupCreatorRollupDeploymentParams{ Config: config, Validators: validatorAddrs, @@ -272,6 +284,7 @@ func DeployOnParentChain(ctx context.Context, parentChainReader *headerreader.He deployAuth, deployParams, ) + if err != nil { return nil, fmt.Errorf("error submitting create rollup tx: %w", err) } diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 027a41d875..f4ea03a105 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -76,6 +76,7 @@ import ( "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/solgen/go/mocksgen" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" + "github.com/offchainlabs/nitro/solgen/go/rollupgen" "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/testhelpers" @@ -252,6 +253,7 @@ type NodeBuilder struct { l3InitMessage *arbostypes.ParsedInitMessage withProdConfirmPeriodBlocks bool wasmCacheTag uint32 + isDelayBufferable bool // Created nodes L1 *TestClient @@ -364,6 +366,11 @@ func (b *NodeBuilder) WithStylusLongTermCache(enabled bool) *NodeBuilder { return b } +func (b *NodeBuilder) WithDelayBuffer(enabled bool) *NodeBuilder { + b.isDelayBufferable = enabled + return b +} + func (b *NodeBuilder) Build(t *testing.T) func() { b.CheckConfig(t) if b.withL1 { @@ -413,6 +420,7 @@ func (b *NodeBuilder) BuildL1(t *testing.T) { locator.LatestWasmModuleRoot(), b.withProdConfirmPeriodBlocks, true, + b.isDelayBufferable, ) b.L1.cleanup = func() { requireClose(t, b.L1.Stack) } } @@ -516,6 +524,7 @@ func (b *NodeBuilder) BuildL3OnL2(t *testing.T) func() { locator.LatestWasmModuleRoot(), b.l3Config.withProdConfirmPeriodBlocks, false, + false, ) b.L3 = buildOnParentChain( @@ -1259,6 +1268,7 @@ func deployOnParentChain( wasmModuleRoot common.Hash, prodConfirmPeriodBlocks bool, chainSupportsBlobs bool, + isDelayBufferable bool, ) (*chaininfo.RollupAddresses, *arbostypes.ParsedInitMessage) { parentChainInfo.GenerateAccount("RollupOwner") parentChainInfo.GenerateAccount("Sequencer") @@ -1281,6 +1291,11 @@ func deployOnParentChain( parentChainReader.Start(ctx) defer parentChainReader.StopAndWait() + var bufferConfig rollupgen.BufferConfig + if isDelayBufferable { + bufferConfig = arbnode.DefaultBufferConfig() + } + nativeToken := common.Address{} maxDataSize := big.NewInt(117964) addresses, err := deploy.DeployOnParentChain( @@ -1290,7 +1305,7 @@ func deployOnParentChain( []common.Address{parentChainInfo.GetAddress("Sequencer")}, parentChainInfo.GetAddress("RollupOwner"), 0, - arbnode.GenerateRollupConfig(prodConfirmPeriodBlocks, wasmModuleRoot, parentChainInfo.GetAddress("RollupOwner"), chainConfig, serializedChainConfig, common.Address{}), + arbnode.GenerateRollupConfig(prodConfirmPeriodBlocks, wasmModuleRoot, parentChainInfo.GetAddress("RollupOwner"), chainConfig, serializedChainConfig, common.Address{}, bufferConfig), nativeToken, maxDataSize, chainSupportsBlobs, diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index 4d902f87ba..30b7352892 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -215,6 +215,7 @@ func setupSequencerInboxStub(ctx context.Context, t *testing.T, l1Info *Blockcha big.NewInt(117964), reader4844, false, + false, ) Require(t, err) _, err = EnsureTxSucceeded(ctx, l1Client, tx) From 4259e94f10d1d0bd1eb9fbcd841a3ea136fafe3a Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Tue, 22 Oct 2024 17:53:46 -0300 Subject: [PATCH 06/12] Fix delay proof Get the delay message accumulator from the previous message. --- arbnode/delay_buffer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/delay_buffer.go b/arbnode/delay_buffer.go index c0e317e27c..4b79455c64 100644 --- a/arbnode/delay_buffer.go +++ b/arbnode/delay_buffer.go @@ -61,7 +61,7 @@ func GenDelayProof(ctx context.Context, message *arbostypes.MessageWithMetadata, var beforeDelayedAcc common.Hash if seqNum > 0 { var err error - beforeDelayedAcc, err = inbox.GetDelayedAcc(seqNum) + beforeDelayedAcc, err = inbox.GetDelayedAcc(seqNum - 1) if err != nil { return nil, err } From a6c9b2a102a2c1c40896372e35add5d25934bbd9 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 23 Oct 2024 11:40:18 -0300 Subject: [PATCH 07/12] Fix delay buffer threshold check --- arbnode/batch_poster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 0916aabf00..fc1a789db9 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1392,7 +1392,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) } latestBlock := latestHeader.Number.Uint64() firstDelayedMsgBlock := b.building.firstDelayedMsg.Message.Header.BlockNumber - if latestBlock > firstDelayedMsgBlock+delayBuffer.Threshold { + if latestBlock >= firstDelayedMsgBlock+delayBuffer.Threshold { log.Info("force post batch because of the delay buffer", "firstDelayedMsgBlock", firstDelayedMsgBlock, "threshold", delayBuffer.Threshold, From ba1079d29aad47fcf9cafbba3546a3e7640929fd Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 23 Oct 2024 11:40:37 -0300 Subject: [PATCH 08/12] Fix off-by-one error in delay proof --- arbnode/delay_buffer.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arbnode/delay_buffer.go b/arbnode/delay_buffer.go index 4b79455c64..508fec1b35 100644 --- a/arbnode/delay_buffer.go +++ b/arbnode/delay_buffer.go @@ -57,7 +57,10 @@ func GetDelayBufferConfig(ctx context.Context, sequencerInbox *bridgegen.Sequenc func GenDelayProof(ctx context.Context, message *arbostypes.MessageWithMetadata, inbox *InboxTracker) ( *bridgegen.DelayProof, error) { - seqNum := message.DelayedMessagesRead + if message.DelayedMessagesRead == 0 { + return nil, fmt.Errorf("BUG: trying to generate delay proof without delayed message") + } + seqNum := message.DelayedMessagesRead - 1 var beforeDelayedAcc common.Hash if seqNum > 0 { var err error From 5cc8a44a27d394c983384c4cadbb8ba5c9eec522 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 23 Oct 2024 16:40:28 -0300 Subject: [PATCH 09/12] Test batch poster with delay buffer enabled --- system_tests/batch_poster_test.go | 51 +++++++++++++++++++++++++++ system_tests/common_test.go | 58 +++++++++++++++---------------- 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 0ec03e84c4..6b602d38a8 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -14,6 +14,7 @@ import ( "github.com/andybalholm/brotli" "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" @@ -363,3 +364,53 @@ func TestAllowPostingFirstBatchWhenSequencerMessageCountMismatchEnabled(t *testi func TestAllowPostingFirstBatchWhenSequencerMessageCountMismatchDisabled(t *testing.T) { testAllowPostingFirstBatchWhenSequencerMessageCountMismatch(t, false) } + +func TestBatchPosterDelayBuffer(t *testing.T) { + const messagesPerBatch = 10 + const threshold = 100 + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + builder := NewNodeBuilder(ctx).DefaultConfig(t, true).WithDelayBuffer(threshold) + builder.nodeConfig.BatchPoster.MaxDelay = time.Hour // set high max-delay so we can test the delay buffer + cleanup := builder.Build(t) + defer cleanup() + builder.L2Info.GenerateAccount("User2") + + testClientB, cleanupB := builder.Build2ndNode(t, &SecondNodeParams{}) + defer cleanupB() + + sequenceInbox, err := bridgegen.NewSequencerInbox(builder.L1Info.GetAddress("SequencerInbox"), builder.L1.Client) + Require(t, err) + getBatchCount := func() uint64 { + batchCount, err := sequenceInbox.BatchCount(&bind.CallOpts{Context: ctx}) + Require(t, err) + return batchCount.Uint64() + } + + t.Run("SendsDelayedMessages", func(t *testing.T) { + previousBatchCount := getBatchCount() + const numBatches = 3 + for batch := uint64(0); batch < numBatches; batch++ { + txs := make(types.Transactions, messagesPerBatch) + for i := range txs { + txs[i] = builder.L2Info.PrepareTx("Owner", "User2", builder.L2Info.TransferGas, common.Big1, nil) + } + SendSignedTxesInBatchViaL1(t, ctx, builder.L1Info, builder.L1.Client, builder.L2.Client, txs) + time.Sleep(time.Second) + if currBatchCount := getBatchCount(); currBatchCount != previousBatchCount+batch { + t.Fatalf("expected batch count %v; got %v", previousBatchCount+batch, currBatchCount) + } + // Advance L1 to force the delay buffer + AdvanceL1(t, ctx, builder.L1.Client, builder.L1Info, threshold) + if currBatchCount := getBatchCount(); currBatchCount != previousBatchCount+batch+1 { + t.Fatalf("expected batch count %v; got %v", previousBatchCount+batch+1, currBatchCount) + } + for _, tx := range txs { + _, err := testClientB.EnsureTxSucceeded(tx) + Require(t, err, "tx not found on second node") + } + } + }) +} diff --git a/system_tests/common_test.go b/system_tests/common_test.go index f4ea03a105..4bbf7c473c 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -76,7 +76,6 @@ import ( "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/solgen/go/mocksgen" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" - "github.com/offchainlabs/nitro/solgen/go/rollupgen" "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/testhelpers" @@ -253,7 +252,7 @@ type NodeBuilder struct { l3InitMessage *arbostypes.ParsedInitMessage withProdConfirmPeriodBlocks bool wasmCacheTag uint32 - isDelayBufferable bool + delayBufferThreshold uint64 // Created nodes L1 *TestClient @@ -366,8 +365,11 @@ func (b *NodeBuilder) WithStylusLongTermCache(enabled bool) *NodeBuilder { return b } -func (b *NodeBuilder) WithDelayBuffer(enabled bool) *NodeBuilder { - b.isDelayBufferable = enabled +// WithDelayBuffer sets the delay-buffer threshold, which is the number of blocks the batch-poster +// is allowed to delay a batch with a delayed message. +// Setting the threshold to zero disabled the delay buffer (default behaviour). +func (b *NodeBuilder) WithDelayBuffer(threshold uint64) *NodeBuilder { + b.delayBufferThreshold = threshold return b } @@ -420,7 +422,7 @@ func (b *NodeBuilder) BuildL1(t *testing.T) { locator.LatestWasmModuleRoot(), b.withProdConfirmPeriodBlocks, true, - b.isDelayBufferable, + b.delayBufferThreshold, ) b.L1.cleanup = func() { requireClose(t, b.L1.Stack) } } @@ -524,7 +526,7 @@ func (b *NodeBuilder) BuildL3OnL2(t *testing.T) func() { locator.LatestWasmModuleRoot(), b.l3Config.withProdConfirmPeriodBlocks, false, - false, + 0, ) b.L3 = buildOnParentChain( @@ -882,6 +884,21 @@ func BridgeBalance( return tx, res } +// AdvanceL1 sends dummy transactions to L1 to create blocks. +func AdvanceL1( + t *testing.T, + ctx context.Context, + l1client *ethclient.Client, + l1info *BlockchainTestInfo, + numBlocks int, +) { + for i := 0; i < numBlocks; i++ { + SendWaitTestTransactions(t, ctx, l1client, []*types.Transaction{ + l1info.PrepareTx("Faucet", "Faucet", 30000, big.NewInt(1e12), nil), + }) + } +} + func SendSignedTxesInBatchViaL1( t *testing.T, ctx context.Context, @@ -901,12 +918,7 @@ func SendSignedTxesInBatchViaL1( _, err = EnsureTxSucceeded(ctx, l1client, l1tx) Require(t, err) - // sending l1 messages creates l1 blocks.. make enough to get that delayed inbox message in - for i := 0; i < 30; i++ { - SendWaitTestTransactions(t, ctx, l1client, []*types.Transaction{ - l1info.PrepareTx("Faucet", "Faucet", 30000, big.NewInt(1e12), nil), - }) - } + AdvanceL1(t, ctx, l1client, l1info, 30) var receipts types.Receipts for _, tx := range delayedTxes { receipt, err := EnsureTxSucceeded(ctx, l2client, tx) @@ -953,12 +965,7 @@ func SendSignedTxViaL1( _, err = EnsureTxSucceeded(ctx, l1client, l1tx) Require(t, err) - // sending l1 messages creates l1 blocks.. make enough to get that delayed inbox message in - for i := 0; i < 30; i++ { - SendWaitTestTransactions(t, ctx, l1client, []*types.Transaction{ - l1info.PrepareTx("Faucet", "Faucet", 30000, big.NewInt(1e12), nil), - }) - } + AdvanceL1(t, ctx, l1client, l1info, 30) receipt, err := EnsureTxSucceeded(ctx, l2client, delayedTx) Require(t, err) return receipt @@ -1004,12 +1011,7 @@ func SendUnsignedTxViaL1( _, err = EnsureTxSucceeded(ctx, l1client, l1tx) Require(t, err) - // sending l1 messages creates l1 blocks.. make enough to get that delayed inbox message in - for i := 0; i < 30; i++ { - SendWaitTestTransactions(t, ctx, l1client, []*types.Transaction{ - l1info.PrepareTx("Faucet", "Faucet", 30000, big.NewInt(1e12), nil), - }) - } + AdvanceL1(t, ctx, l1client, l1info, 30) receipt, err := EnsureTxSucceeded(ctx, l2client, unsignedTx) Require(t, err) return receipt @@ -1268,7 +1270,7 @@ func deployOnParentChain( wasmModuleRoot common.Hash, prodConfirmPeriodBlocks bool, chainSupportsBlobs bool, - isDelayBufferable bool, + delayBufferThreshold uint64, ) (*chaininfo.RollupAddresses, *arbostypes.ParsedInitMessage) { parentChainInfo.GenerateAccount("RollupOwner") parentChainInfo.GenerateAccount("Sequencer") @@ -1291,10 +1293,8 @@ func deployOnParentChain( parentChainReader.Start(ctx) defer parentChainReader.StopAndWait() - var bufferConfig rollupgen.BufferConfig - if isDelayBufferable { - bufferConfig = arbnode.DefaultBufferConfig() - } + bufferConfig := arbnode.DefaultBufferConfig() + bufferConfig.Threshold = delayBufferThreshold nativeToken := common.Address{} maxDataSize := big.NewInt(117964) From 410a65df41a0ff9223cff118a50073c5227127c9 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Thu, 24 Oct 2024 15:54:05 -0300 Subject: [PATCH 10/12] Add two more delay-buffer tests * Test batch poster without delay buffer works * Test delay buffer don't force batch without delayed messages --- system_tests/batch_poster_test.go | 129 ++++++++++++++++++++++-------- 1 file changed, 95 insertions(+), 34 deletions(-) diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 6b602d38a8..2ba3da328e 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -6,6 +6,7 @@ package arbtest import ( "context" "crypto/rand" + "errors" "fmt" "math/big" "strings" @@ -365,52 +366,112 @@ func TestAllowPostingFirstBatchWhenSequencerMessageCountMismatchDisabled(t *test testAllowPostingFirstBatchWhenSequencerMessageCountMismatch(t, false) } -func TestBatchPosterDelayBuffer(t *testing.T) { - const messagesPerBatch = 10 - const threshold = 100 +func GetBatchCount(t *testing.T, builder *NodeBuilder) uint64 { + t.Helper() + sequenceInbox, err := bridgegen.NewSequencerInbox(builder.L1Info.GetAddress("SequencerInbox"), builder.L1.Client) + Require(t, err) + batchCount, err := sequenceInbox.BatchCount(&bind.CallOpts{Context: builder.ctx}) + Require(t, err) + return batchCount.Uint64() +} + +func CheckBatchCount(t *testing.T, builder *NodeBuilder, want uint64) { + if got := GetBatchCount(t, builder); got != want { + t.Fatalf("invalid batch count, want %v, got %v", want, got) + } +} + +func testBatchPosterDelayBuffer(t *testing.T, delayBufferEnabled bool) { + const messagesPerBatch = 3 + const numBatches = 3 + var threshold uint64 + if delayBufferEnabled { + threshold = 100 + } ctx, cancel := context.WithCancel(context.Background()) defer cancel() builder := NewNodeBuilder(ctx).DefaultConfig(t, true).WithDelayBuffer(threshold) + builder.L2Info.GenerateAccount("User2") builder.nodeConfig.BatchPoster.MaxDelay = time.Hour // set high max-delay so we can test the delay buffer cleanup := builder.Build(t) defer cleanup() - builder.L2Info.GenerateAccount("User2") + testClientB, cleanupB := builder.Build2ndNode(t, &SecondNodeParams{}) + defer cleanupB() + + initialBatchCount := GetBatchCount(t, builder) + for batch := uint64(0); batch < numBatches; batch++ { + txs := make(types.Transactions, messagesPerBatch) + for i := range txs { + txs[i] = builder.L2Info.PrepareTx("Owner", "User2", builder.L2Info.TransferGas, common.Big1, nil) + } + SendSignedTxesInBatchViaL1(t, ctx, builder.L1Info, builder.L1.Client, builder.L2.Client, txs) + + // Check batch wasn't sent + _, err := WaitForTx(ctx, testClientB.Client, txs[0].Hash(), 100*time.Millisecond) + if err == nil || !errors.Is(err, context.DeadlineExceeded) { + Fatal(t, "expected context-deadline exceeded error, but got:", err) + } + CheckBatchCount(t, builder, initialBatchCount+batch) + + // Advance L1 to force a batch given the delay buffer threshold + AdvanceL1(t, ctx, builder.L1.Client, builder.L1Info, int(threshold)) // #nosec G115 + if !delayBufferEnabled { + // If the delay buffer is disabled, set max delay to zero to force it + CheckBatchCount(t, builder, initialBatchCount+batch) + builder.nodeConfig.BatchPoster.MaxDelay = 0 + } + for _, tx := range txs { + _, err := testClientB.EnsureTxSucceeded(tx) + Require(t, err, "tx not found on second node") + } + CheckBatchCount(t, builder, initialBatchCount+batch+1) + if !delayBufferEnabled { + builder.nodeConfig.BatchPoster.MaxDelay = time.Hour + } + } +} +func TestBatchPosterDelayBufferEnabled(t *testing.T) { + testBatchPosterDelayBuffer(t, true) +} + +func TestBatchPosterDelayBufferDisabled(t *testing.T) { + testBatchPosterDelayBuffer(t, false) +} + +func TestBatchPosterDelayBufferDontForceNonDelayedMessages(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + const threshold = 100 + builder := NewNodeBuilder(ctx).DefaultConfig(t, true).WithDelayBuffer(threshold) + builder.L2Info.GenerateAccount("User2") + builder.nodeConfig.BatchPoster.MaxDelay = time.Hour // set high max-delay so we can test the delay buffer + cleanup := builder.Build(t) + defer cleanup() testClientB, cleanupB := builder.Build2ndNode(t, &SecondNodeParams{}) defer cleanupB() - sequenceInbox, err := bridgegen.NewSequencerInbox(builder.L1Info.GetAddress("SequencerInbox"), builder.L1.Client) - Require(t, err) - getBatchCount := func() uint64 { - batchCount, err := sequenceInbox.BatchCount(&bind.CallOpts{Context: ctx}) - Require(t, err) - return batchCount.Uint64() + // Send non-delayed message and advance L1 + initialBatchCount := GetBatchCount(t, builder) + const numTxs = 3 + txs := make(types.Transactions, numTxs) + for i := range txs { + txs[i] = builder.L2Info.PrepareTx("Owner", "User2", builder.L2Info.TransferGas, common.Big1, nil) } + builder.L2.SendWaitTestTransactions(t, txs) + AdvanceL1(t, ctx, builder.L1.Client, builder.L1Info, threshold) - t.Run("SendsDelayedMessages", func(t *testing.T) { - previousBatchCount := getBatchCount() - const numBatches = 3 - for batch := uint64(0); batch < numBatches; batch++ { - txs := make(types.Transactions, messagesPerBatch) - for i := range txs { - txs[i] = builder.L2Info.PrepareTx("Owner", "User2", builder.L2Info.TransferGas, common.Big1, nil) - } - SendSignedTxesInBatchViaL1(t, ctx, builder.L1Info, builder.L1.Client, builder.L2.Client, txs) - time.Sleep(time.Second) - if currBatchCount := getBatchCount(); currBatchCount != previousBatchCount+batch { - t.Fatalf("expected batch count %v; got %v", previousBatchCount+batch, currBatchCount) - } - // Advance L1 to force the delay buffer - AdvanceL1(t, ctx, builder.L1.Client, builder.L1Info, threshold) - if currBatchCount := getBatchCount(); currBatchCount != previousBatchCount+batch+1 { - t.Fatalf("expected batch count %v; got %v", previousBatchCount+batch+1, currBatchCount) - } - for _, tx := range txs { - _, err := testClientB.EnsureTxSucceeded(tx) - Require(t, err, "tx not found on second node") - } - } - }) + // Even advancing the L1, the batch won't be posted because it doesn't contain a delayed message + CheckBatchCount(t, builder, initialBatchCount) + + // Set delay to zero to force non-delayed messages + builder.nodeConfig.BatchPoster.MaxDelay = 0 + for _, tx := range txs { + _, err := testClientB.EnsureTxSucceeded(tx) + Require(t, err, "tx not found on second node") + } + CheckBatchCount(t, builder, initialBatchCount+1) } From fb86fad785bdbb0be40d72003a74661093b3ab31 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Mon, 28 Oct 2024 15:27:23 -0300 Subject: [PATCH 11/12] Fix gofmt --- staker/multi_protocol/multi_protocol_staker.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/staker/multi_protocol/multi_protocol_staker.go b/staker/multi_protocol/multi_protocol_staker.go index d4d4e1b54f..5f36b8df99 100644 --- a/staker/multi_protocol/multi_protocol_staker.go +++ b/staker/multi_protocol/multi_protocol_staker.go @@ -2,15 +2,16 @@ package multiprotocolstaker import ( "context" - "github.com/offchainlabs/nitro/staker" "time" - "github.com/offchainlabs/bold/solgen/go/bridgegen" - boldrollup "github.com/offchainlabs/bold/solgen/go/rollupgen" + "github.com/offchainlabs/nitro/staker" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/bold/solgen/go/bridgegen" + boldrollup "github.com/offchainlabs/bold/solgen/go/rollupgen" boldstaker "github.com/offchainlabs/nitro/staker/bold" legacystaker "github.com/offchainlabs/nitro/staker/legacy" From b0d586cb4dcc13d414de86eb35958f5bb04be54a Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Mon, 28 Oct 2024 17:31:15 -0300 Subject: [PATCH 12/12] Use BoLD contracts to test delay buffer Rebase the delay buffer PR on top of BoLD's little merge. Then, use the BoLD version of the contracts when appropriate instead of using the delay-buffer development branch. --- arbnode/batch_poster.go | 3 +- arbnode/delay_buffer.go | 2 +- arbnode/node.go | 37 --- cmd/deploy/deploy.go | 12 +- deploy/deploy.go | 117 +++++++-- deploy/legacy.go | 321 +++++++++++++++++++++++ system_tests/batch_poster_test.go | 10 +- system_tests/common_test.go | 103 ++++++-- system_tests/full_challenge_impl_test.go | 1 - 9 files changed, 509 insertions(+), 97 deletions(-) create mode 100644 deploy/legacy.go diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index fc1a789db9..eb0a98240c 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + "github.com/offchainlabs/bold/solgen/go/bridgegen" "github.com/offchainlabs/nitro/arbnode/dataposter" "github.com/offchainlabs/nitro/arbnode/dataposter/storage" "github.com/offchainlabs/nitro/arbnode/redislock" @@ -44,7 +45,6 @@ import ( "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/execution" - "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/util" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/blobs" @@ -317,7 +317,6 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e if err = opts.Config().Validate(); err != nil { return nil, err } - // TODO(delaybuffer) use new bridgegen seqInboxABI, err := bridgegen.SequencerInboxMetaData.GetAbi() if err != nil { return nil, err diff --git a/arbnode/delay_buffer.go b/arbnode/delay_buffer.go index 508fec1b35..ffef7ee828 100644 --- a/arbnode/delay_buffer.go +++ b/arbnode/delay_buffer.go @@ -14,8 +14,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/offchainlabs/bold/solgen/go/bridgegen" "github.com/offchainlabs/nitro/arbos/arbostypes" - "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/util/headerreader" ) diff --git a/arbnode/node.go b/arbnode/node.go index d7d9465205..7445999fcd 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -39,7 +39,6 @@ import ( "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" - "github.com/offchainlabs/nitro/solgen/go/rollupgen" "github.com/offchainlabs/nitro/staker" boldstaker "github.com/offchainlabs/nitro/staker/bold" legacystaker "github.com/offchainlabs/nitro/staker/legacy" @@ -53,42 +52,6 @@ import ( "github.com/offchainlabs/nitro/wsbroadcastserver" ) -func DefaultBufferConfig() rollupgen.BufferConfig { - return rollupgen.BufferConfig{ - Threshold: 600, // 1 hour of blocks - Max: 14400, // 2 days of blocks - ReplenishRateInBasis: 500, // 5% - } -} - -func GenerateRollupConfig(prod bool, wasmModuleRoot common.Hash, rollupOwner common.Address, chainConfig *params.ChainConfig, serializedChainConfig []byte, loserStakeEscrow common.Address, bufferConfig rollupgen.BufferConfig) rollupgen.Config { - var confirmPeriod uint64 - if prod { - confirmPeriod = 45818 - } else { - confirmPeriod = 20 - } - return rollupgen.Config{ - ConfirmPeriodBlocks: confirmPeriod, - ExtraChallengeTimeBlocks: 200, - StakeToken: common.Address{}, - BaseStake: big.NewInt(params.Ether), - WasmModuleRoot: wasmModuleRoot, - Owner: rollupOwner, - LoserStakeEscrow: loserStakeEscrow, - ChainId: chainConfig.ChainID, - // TODO could the ChainConfig be just []byte? - ChainConfig: string(serializedChainConfig), - SequencerInboxMaxTimeVariation: rollupgen.ISequencerInboxMaxTimeVariation{ - DelayBlocks: big.NewInt(60 * 60 * 24 / 15), - FutureBlocks: big.NewInt(12), - DelaySeconds: big.NewInt(60 * 60 * 24), - FutureSeconds: big.NewInt(60 * 60), - }, - BufferConfig: bufferConfig, - } -} - type Config struct { Sequencer bool `koanf:"sequencer"` ParentChainReader headerreader.Config `koanf:"parent-chain-reader" reload:"hot"` diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index 6060c55586..603539196f 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go @@ -17,7 +17,6 @@ import ( "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" - "github.com/offchainlabs/nitro/solgen/go/rollupgen" "github.com/offchainlabs/nitro/util/headerreader" "github.com/offchainlabs/nitro/validator/server_common" @@ -26,7 +25,6 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/cmd/util" deploycode "github.com/offchainlabs/nitro/deploy" ) @@ -62,7 +60,6 @@ func main() { authorizevalidators := flag.Uint64("authorizevalidators", 0, "Number of validators to preemptively authorize") txTimeout := flag.Duration("txtimeout", 10*time.Minute, "Timeout when waiting for a transaction to be included in a block") prod := flag.Bool("prod", false, "Whether to configure the rollup for production or testing") - isDelayBufferable := flag.Bool("delayBufferable", false, "Whether the sequencer-inbox delay buffer is enabled") flag.Parse() l1ChainId := new(big.Int).SetUint64(*l1ChainIdUint) maxDataSize := new(big.Int).SetUint64(*maxDataSizeUint) @@ -172,11 +169,6 @@ func main() { panic(fmt.Errorf("failed to deserialize chain config: %w", err)) } - var bufferConfig rollupgen.BufferConfig - if *isDelayBufferable { - bufferConfig = arbnode.DefaultBufferConfig() - } - arbSys, _ := precompilesgen.NewArbSys(types.ArbSysAddress, l1client) l1Reader, err := headerreader.New(ctx, l1client, func() *headerreader.Config { return &headerReaderConfig }, arbSys) if err != nil { @@ -186,14 +178,14 @@ func main() { defer l1Reader.StopAndWait() nativeToken := common.HexToAddress(*nativeTokenAddressString) - deployedAddresses, err := deploycode.DeployOnParentChain( + deployedAddresses, err := deploycode.DeployLegacyOnParentChain( ctx, l1Reader, l1TransactionOpts, batchPosters, batchPosterManagerAddress, *authorizevalidators, - arbnode.GenerateRollupConfig(*prod, moduleRoot, ownerAddress, &chainConfig, chainConfigJson, loserEscrowAddress, bufferConfig), + deploycode.GenerateLegacyRollupConfig(*prod, moduleRoot, ownerAddress, &chainConfig, chainConfigJson, loserEscrowAddress), nativeToken, maxDataSize, true, diff --git a/deploy/deploy.go b/deploy/deploy.go index 858d06db89..6a71dd9673 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -10,16 +10,89 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/offchainlabs/bold/solgen/go/bridgegen" + "github.com/offchainlabs/bold/solgen/go/challengeV2gen" + "github.com/offchainlabs/bold/solgen/go/ospgen" + "github.com/offchainlabs/bold/solgen/go/rollupgen" + "github.com/offchainlabs/bold/solgen/go/yulgen" "github.com/offchainlabs/nitro/cmd/chaininfo" - "github.com/offchainlabs/nitro/solgen/go/bridgegen" - "github.com/offchainlabs/nitro/solgen/go/challengegen" - "github.com/offchainlabs/nitro/solgen/go/ospgen" - "github.com/offchainlabs/nitro/solgen/go/rollupgen" "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" - "github.com/offchainlabs/nitro/solgen/go/yulgen" "github.com/offchainlabs/nitro/util/headerreader" ) +// lint:require-exhaustive-initialization +type RollupConfigOpts struct { + Prod bool + WasmModuleRoot common.Hash + RollupOwner common.Address + ChainConfig *params.ChainConfig + SerializedChainConfig []byte + LoserStakeEscrow common.Address + MiniStakeValues []*big.Int + StakeToken common.Address + GenesisExecutionState rollupgen.AssertionState + GenesisInboxCount *big.Int + AnyTrustFastConfirmer common.Address + LayerZeroBlockEdgeHeight uint64 + LayerZeroBigStepEdgeHeight uint64 + LayerZeroSmallStepEdgeHeight uint64 + NumBigStepLevel uint8 + BufferConfig rollupgen.BufferConfig +} + +func DefaultBufferConfig() rollupgen.BufferConfig { + return rollupgen.BufferConfig{ + Threshold: 600, // 1 hour of blocks + Max: 14400, // 2 days of blocks + ReplenishRateInBasis: 500, // 5% + } +} + +func GenerateRollupConfig(opts *RollupConfigOpts) rollupgen.Config { + var confirmPeriod uint64 + if opts.Prod { + confirmPeriod = 45818 + } else { + confirmPeriod = 25 + } + + var gracePeriod uint64 + if opts.Prod { + gracePeriod = 14400 + } else { + gracePeriod = 3 + } + + cfg := rollupgen.Config{ + ConfirmPeriodBlocks: confirmPeriod, + StakeToken: opts.StakeToken, + BaseStake: big.NewInt(1), + WasmModuleRoot: opts.WasmModuleRoot, + Owner: opts.RollupOwner, + LoserStakeEscrow: opts.LoserStakeEscrow, + ChainId: opts.ChainConfig.ChainID, + ChainConfig: string(opts.SerializedChainConfig), + MiniStakeValues: opts.MiniStakeValues, + SequencerInboxMaxTimeVariation: rollupgen.ISequencerInboxMaxTimeVariation{ + DelayBlocks: big.NewInt(60 * 60 * 24 / 15), + FutureBlocks: big.NewInt(12), + DelaySeconds: big.NewInt(60 * 60 * 24), + FutureSeconds: big.NewInt(60 * 60), + }, + LayerZeroBlockEdgeHeight: new(big.Int).SetUint64(opts.LayerZeroBlockEdgeHeight), + LayerZeroBigStepEdgeHeight: new(big.Int).SetUint64(opts.LayerZeroBigStepEdgeHeight), + LayerZeroSmallStepEdgeHeight: new(big.Int).SetUint64(opts.LayerZeroSmallStepEdgeHeight), + GenesisAssertionState: opts.GenesisExecutionState, + GenesisInboxCount: opts.GenesisInboxCount, + AnyTrustFastConfirmer: opts.AnyTrustFastConfirmer, + NumBigStepLevel: opts.NumBigStepLevel, + ChallengeGracePeriodBlocks: gracePeriod, + BufferConfig: opts.BufferConfig, + } + return cfg +} + func andTxSucceeded(ctx context.Context, parentChainReader *headerreader.HeaderReader, tx *types.Transaction, err error) error { if err != nil { return fmt.Errorf("error submitting tx: %w", err) @@ -167,7 +240,7 @@ func deployChallengeFactory(ctx context.Context, parentChainReader *headerreader return common.Address{}, common.Address{}, fmt.Errorf("ospHostIo deploy error: %w", err) } - challengeManagerAddr, tx, _, err := challengegen.DeployChallengeManager(auth, client) + challengeManagerAddr, tx, _, err := challengeV2gen.DeployEdgeChallengeManager(auth, client) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { return common.Address{}, common.Address{}, fmt.Errorf("challenge manager deploy error: %w", err) @@ -182,57 +255,51 @@ func deployChallengeFactory(ctx context.Context, parentChainReader *headerreader return ospEntryAddr, challengeManagerAddr, nil } -func deployRollupCreator(ctx context.Context, parentChainReader *headerreader.HeaderReader, auth *bind.TransactOpts, maxDataSize *big.Int, chainSupportsBlobs bool) (*rollupgen.RollupCreator, common.Address, common.Address, common.Address, error) { +func deployRollupCreator(ctx context.Context, parentChainReader *headerreader.HeaderReader, auth *bind.TransactOpts, maxDataSize *big.Int, chainSupportsBlobs bool) (*rollupgen.RollupCreator, common.Address, common.Address, error) { bridgeCreator, err := deployBridgeCreator(ctx, parentChainReader, auth, maxDataSize, chainSupportsBlobs) if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) + return nil, common.Address{}, common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) } ospEntryAddr, challengeManagerAddr, err := deployChallengeFactory(ctx, parentChainReader, auth) if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, err + return nil, common.Address{}, common.Address{}, err } rollupAdminLogic, tx, _, err := rollupgen.DeployRollupAdminLogic(auth, parentChainReader.Client()) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup admin logic deploy error: %w", err) + return nil, common.Address{}, common.Address{}, fmt.Errorf("rollup admin logic deploy error: %w", err) } rollupUserLogic, tx, _, err := rollupgen.DeployRollupUserLogic(auth, parentChainReader.Client()) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup user logic deploy error: %w", err) + return nil, common.Address{}, common.Address{}, fmt.Errorf("rollup user logic deploy error: %w", err) } rollupCreatorAddress, tx, rollupCreator, err := rollupgen.DeployRollupCreator(auth, parentChainReader.Client()) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup creator deploy error: %w", err) + return nil, common.Address{}, common.Address{}, fmt.Errorf("rollup creator deploy error: %w", err) } upgradeExecutor, tx, _, err := upgrade_executorgen.DeployUpgradeExecutor(auth, parentChainReader.Client()) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("upgrade executor deploy error: %w", err) - } - - validatorUtils, tx, _, err := rollupgen.DeployValidatorUtils(auth, parentChainReader.Client()) - err = andTxSucceeded(ctx, parentChainReader, tx, err) - if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator utils deploy error: %w", err) + return nil, common.Address{}, common.Address{}, fmt.Errorf("upgrade executor deploy error: %w", err) } validatorWalletCreator, tx, _, err := rollupgen.DeployValidatorWalletCreator(auth, parentChainReader.Client()) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator wallet creator deploy error: %w", err) + return nil, common.Address{}, common.Address{}, fmt.Errorf("validator wallet creator deploy error: %w", err) } l2FactoriesDeployHelper, tx, _, err := rollupgen.DeployDeployHelper(auth, parentChainReader.Client()) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("deploy helper creator deploy error: %w", err) + return nil, common.Address{}, common.Address{}, fmt.Errorf("deploy helper creator deploy error: %w", err) } tx, err = rollupCreator.SetTemplates( @@ -243,16 +310,15 @@ func deployRollupCreator(ctx context.Context, parentChainReader *headerreader.He rollupAdminLogic, rollupUserLogic, upgradeExecutor, - validatorUtils, validatorWalletCreator, l2FactoriesDeployHelper, ) err = andTxSucceeded(ctx, parentChainReader, tx, err) if err != nil { - return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup set template error: %w", err) + return nil, common.Address{}, common.Address{}, fmt.Errorf("rollup set template error: %w", err) } - return rollupCreator, rollupCreatorAddress, validatorUtils, validatorWalletCreator, nil + return rollupCreator, rollupCreatorAddress, validatorWalletCreator, nil } func DeployOnParentChain(ctx context.Context, parentChainReader *headerreader.HeaderReader, deployAuth *bind.TransactOpts, batchPosters []common.Address, batchPosterManager common.Address, authorizeValidators uint64, config rollupgen.Config, nativeToken common.Address, maxDataSize *big.Int, chainSupportsBlobs bool) (*chaininfo.RollupAddresses, error) { @@ -260,7 +326,7 @@ func DeployOnParentChain(ctx context.Context, parentChainReader *headerreader.He return nil, errors.New("no machine specified") } - rollupCreator, _, validatorUtils, validatorWalletCreator, err := deployRollupCreator(ctx, parentChainReader, deployAuth, maxDataSize, chainSupportsBlobs) + rollupCreator, _, validatorWalletCreator, err := deployRollupCreator(ctx, parentChainReader, deployAuth, maxDataSize, chainSupportsBlobs) if err != nil { return nil, fmt.Errorf("error deploying rollup creator: %w", err) } @@ -305,7 +371,6 @@ func DeployOnParentChain(ctx context.Context, parentChainReader *headerreader.He Rollup: info.RollupAddress, NativeToken: nativeToken, UpgradeExecutor: info.UpgradeExecutor, - ValidatorUtils: validatorUtils, ValidatorWalletCreator: validatorWalletCreator, }, nil } diff --git a/deploy/legacy.go b/deploy/legacy.go new file mode 100644 index 0000000000..908052ae08 --- /dev/null +++ b/deploy/legacy.go @@ -0,0 +1,321 @@ +package deploy + +import ( + "context" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/offchainlabs/nitro/cmd/chaininfo" + "github.com/offchainlabs/nitro/solgen/go/bridgegen" + "github.com/offchainlabs/nitro/solgen/go/challengegen" + "github.com/offchainlabs/nitro/solgen/go/ospgen" + "github.com/offchainlabs/nitro/solgen/go/rollupgen" + "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" + "github.com/offchainlabs/nitro/solgen/go/yulgen" + "github.com/offchainlabs/nitro/util/headerreader" +) + +func GenerateLegacyRollupConfig( + prod bool, + wasmModuleRoot common.Hash, + rollupOwner common.Address, + chainConfig *params.ChainConfig, + serializedChainConfig []byte, + loserStakeEscrow common.Address, +) rollupgen.Config { + var confirmPeriod uint64 + if prod { + confirmPeriod = 45818 + } else { + confirmPeriod = 20 + } + return rollupgen.Config{ + ConfirmPeriodBlocks: confirmPeriod, + ExtraChallengeTimeBlocks: 200, + StakeToken: common.Address{}, + BaseStake: big.NewInt(params.Ether), + WasmModuleRoot: wasmModuleRoot, + Owner: rollupOwner, + LoserStakeEscrow: loserStakeEscrow, + ChainId: chainConfig.ChainID, + // TODO could the ChainConfig be just []byte? + ChainConfig: string(serializedChainConfig), + SequencerInboxMaxTimeVariation: rollupgen.ISequencerInboxMaxTimeVariation{ + DelayBlocks: big.NewInt(60 * 60 * 24 / 15), + FutureBlocks: big.NewInt(12), + DelaySeconds: big.NewInt(60 * 60 * 24), + FutureSeconds: big.NewInt(60 * 60), + }, + } +} + +func deployLegacyBridgeCreator(ctx context.Context, parentChainReader *headerreader.HeaderReader, auth *bind.TransactOpts, maxDataSize *big.Int, chainSupportsBlobs bool) (common.Address, error) { + client := parentChainReader.Client() + + /// deploy eth based templates + bridgeTemplate, tx, _, err := bridgegen.DeployBridge(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("bridge deploy error: %w", err) + } + + var reader4844 common.Address + if chainSupportsBlobs { + reader4844, tx, _, err = yulgen.DeployReader4844(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("blob basefee reader deploy error: %w", err) + } + } + seqInboxTemplateEthBased, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize, reader4844, false) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("sequencer inbox eth based deploy error: %w", err) + } + seqInboxTemplateERC20Based, tx, _, err := bridgegen.DeploySequencerInbox(auth, client, maxDataSize, reader4844, true) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("sequencer inbox erc20 based deploy error: %w", err) + } + + inboxTemplate, tx, _, err := bridgegen.DeployInbox(auth, client, maxDataSize) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("inbox deploy error: %w", err) + } + + rollupEventBridgeTemplate, tx, _, err := rollupgen.DeployRollupEventInbox(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("rollup event bridge deploy error: %w", err) + } + + outboxTemplate, tx, _, err := bridgegen.DeployOutbox(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("outbox deploy error: %w", err) + } + + ethBasedTemplates := rollupgen.BridgeCreatorBridgeContracts{ + Bridge: bridgeTemplate, + SequencerInbox: seqInboxTemplateEthBased, + Inbox: inboxTemplate, + RollupEventInbox: rollupEventBridgeTemplate, + Outbox: outboxTemplate, + } + + /// deploy ERC20 based templates + erc20BridgeTemplate, tx, _, err := bridgegen.DeployERC20Bridge(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("bridge deploy error: %w", err) + } + + erc20InboxTemplate, tx, _, err := bridgegen.DeployERC20Inbox(auth, client, maxDataSize) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("inbox deploy error: %w", err) + } + + erc20RollupEventBridgeTemplate, tx, _, err := rollupgen.DeployERC20RollupEventInbox(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("rollup event bridge deploy error: %w", err) + } + + erc20OutboxTemplate, tx, _, err := bridgegen.DeployERC20Outbox(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("outbox deploy error: %w", err) + } + + erc20BasedTemplates := rollupgen.BridgeCreatorBridgeContracts{ + Bridge: erc20BridgeTemplate, + SequencerInbox: seqInboxTemplateERC20Based, + Inbox: erc20InboxTemplate, + RollupEventInbox: erc20RollupEventBridgeTemplate, + Outbox: erc20OutboxTemplate, + } + + bridgeCreatorAddr, tx, _, err := rollupgen.DeployBridgeCreator(auth, client, ethBasedTemplates, erc20BasedTemplates) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) + } + + return bridgeCreatorAddr, nil +} + +func deployLegacyChallengeFactory(ctx context.Context, parentChainReader *headerreader.HeaderReader, auth *bind.TransactOpts) (common.Address, common.Address, error) { + client := parentChainReader.Client() + osp0, tx, _, err := ospgen.DeployOneStepProver0(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("osp0 deploy error: %w", err) + } + + ospMem, tx, _, err := ospgen.DeployOneStepProverMemory(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("ospMemory deploy error: %w", err) + } + + ospMath, tx, _, err := ospgen.DeployOneStepProverMath(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("ospMath deploy error: %w", err) + } + + ospHostIo, tx, _, err := ospgen.DeployOneStepProverHostIo(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("ospHostIo deploy error: %w", err) + } + + challengeManagerAddr, tx, _, err := challengegen.DeployChallengeManager(auth, client) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("challenge manager deploy error: %w", err) + } + + ospEntryAddr, tx, _, err := ospgen.DeployOneStepProofEntry(auth, client, osp0, ospMem, ospMath, ospHostIo) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return common.Address{}, common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) + } + + return ospEntryAddr, challengeManagerAddr, nil +} + +func deployLegacyRollupCreator(ctx context.Context, parentChainReader *headerreader.HeaderReader, auth *bind.TransactOpts, maxDataSize *big.Int, chainSupportsBlobs bool) (*rollupgen.RollupCreator, common.Address, common.Address, common.Address, error) { + bridgeCreator, err := deployLegacyBridgeCreator(ctx, parentChainReader, auth, maxDataSize, chainSupportsBlobs) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("bridge creator deploy error: %w", err) + } + + ospEntryAddr, challengeManagerAddr, err := deployLegacyChallengeFactory(ctx, parentChainReader, auth) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, err + } + + rollupAdminLogic, tx, _, err := rollupgen.DeployRollupAdminLogic(auth, parentChainReader.Client()) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup admin logic deploy error: %w", err) + } + + rollupUserLogic, tx, _, err := rollupgen.DeployRollupUserLogic(auth, parentChainReader.Client()) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup user logic deploy error: %w", err) + } + + rollupCreatorAddress, tx, rollupCreator, err := rollupgen.DeployRollupCreator(auth, parentChainReader.Client()) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup creator deploy error: %w", err) + } + + upgradeExecutor, tx, _, err := upgrade_executorgen.DeployUpgradeExecutor(auth, parentChainReader.Client()) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("upgrade executor deploy error: %w", err) + } + + validatorUtils, tx, _, err := rollupgen.DeployValidatorUtils(auth, parentChainReader.Client()) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator utils deploy error: %w", err) + } + + validatorWalletCreator, tx, _, err := rollupgen.DeployValidatorWalletCreator(auth, parentChainReader.Client()) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("validator wallet creator deploy error: %w", err) + } + + l2FactoriesDeployHelper, tx, _, err := rollupgen.DeployDeployHelper(auth, parentChainReader.Client()) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("deploy helper creator deploy error: %w", err) + } + + tx, err = rollupCreator.SetTemplates( + auth, + bridgeCreator, + ospEntryAddr, + challengeManagerAddr, + rollupAdminLogic, + rollupUserLogic, + upgradeExecutor, + validatorUtils, + validatorWalletCreator, + l2FactoriesDeployHelper, + ) + err = andTxSucceeded(ctx, parentChainReader, tx, err) + if err != nil { + return nil, common.Address{}, common.Address{}, common.Address{}, fmt.Errorf("rollup set template error: %w", err) + } + + return rollupCreator, rollupCreatorAddress, validatorUtils, validatorWalletCreator, nil +} + +func DeployLegacyOnParentChain(ctx context.Context, parentChainReader *headerreader.HeaderReader, deployAuth *bind.TransactOpts, batchPosters []common.Address, batchPosterManager common.Address, authorizeValidators uint64, config rollupgen.Config, nativeToken common.Address, maxDataSize *big.Int, chainSupportsBlobs bool) (*chaininfo.RollupAddresses, error) { + if config.WasmModuleRoot == (common.Hash{}) { + return nil, errors.New("no machine specified") + } + + rollupCreator, _, validatorUtils, validatorWalletCreator, err := deployLegacyRollupCreator(ctx, parentChainReader, deployAuth, maxDataSize, chainSupportsBlobs) + if err != nil { + return nil, fmt.Errorf("error deploying rollup creator: %w", err) + } + + var validatorAddrs []common.Address + for i := uint64(1); i <= authorizeValidators; i++ { + validatorAddrs = append(validatorAddrs, crypto.CreateAddress(validatorWalletCreator, i)) + } + deployParams := rollupgen.RollupCreatorRollupDeploymentParams{ + Config: config, + Validators: validatorAddrs, + MaxDataSize: maxDataSize, + NativeToken: nativeToken, + DeployFactoriesToL2: false, + MaxFeePerGasForRetryables: big.NewInt(0), // needed when utility factories are deployed + BatchPosters: batchPosters, + BatchPosterManager: batchPosterManager, + } + + tx, err := rollupCreator.CreateRollup( + deployAuth, + deployParams, + ) + + if err != nil { + return nil, fmt.Errorf("error submitting create rollup tx: %w", err) + } + receipt, err := parentChainReader.WaitForTxApproval(ctx, tx) + if err != nil { + return nil, fmt.Errorf("error executing create rollup tx: %w", err) + } + info, err := rollupCreator.ParseRollupCreated(*receipt.Logs[len(receipt.Logs)-1]) + if err != nil { + return nil, fmt.Errorf("error parsing rollup created log: %w", err) + } + + return &chaininfo.RollupAddresses{ + Bridge: info.Bridge, + Inbox: info.InboxAddress, + SequencerInbox: info.SequencerInbox, + DeployedAt: receipt.BlockNumber.Uint64(), + Rollup: info.RollupAddress, + NativeToken: nativeToken, + UpgradeExecutor: info.UpgradeExecutor, + ValidatorUtils: validatorUtils, + ValidatorWalletCreator: validatorWalletCreator, + }, nil +} diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 2ba3da328e..ffcd6a02be 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -392,7 +392,10 @@ func testBatchPosterDelayBuffer(t *testing.T, delayBufferEnabled bool) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - builder := NewNodeBuilder(ctx).DefaultConfig(t, true).WithDelayBuffer(threshold) + builder := NewNodeBuilder(ctx). + DefaultConfig(t, true). + WithBoldContracts(). + WithDelayBufferThreshold(threshold) builder.L2Info.GenerateAccount("User2") builder.nodeConfig.BatchPoster.MaxDelay = time.Hour // set high max-delay so we can test the delay buffer cleanup := builder.Build(t) @@ -446,7 +449,10 @@ func TestBatchPosterDelayBufferDontForceNonDelayedMessages(t *testing.T) { defer cancel() const threshold = 100 - builder := NewNodeBuilder(ctx).DefaultConfig(t, true).WithDelayBuffer(threshold) + builder := NewNodeBuilder(ctx). + DefaultConfig(t, true). + WithBoldContracts(). + WithDelayBufferThreshold(threshold) builder.L2Info.GenerateAccount("User2") builder.nodeConfig.BatchPoster.MaxDelay = time.Hour // set high max-delay so we can test the delay buffer cleanup := builder.Build(t) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 4bbf7c473c..a08d60f470 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -71,10 +71,11 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/offchainlabs/bold/solgen/go/mocksgen" + "github.com/offchainlabs/bold/solgen/go/rollupgen" "github.com/offchainlabs/nitro/arbnode" _ "github.com/offchainlabs/nitro/execution/nodeInterface" "github.com/offchainlabs/nitro/solgen/go/bridgegen" - "github.com/offchainlabs/nitro/solgen/go/mocksgen" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" "github.com/offchainlabs/nitro/statetransfer" @@ -252,6 +253,7 @@ type NodeBuilder struct { l3InitMessage *arbostypes.ParsedInitMessage withProdConfirmPeriodBlocks bool wasmCacheTag uint32 + deployBoldContracts bool delayBufferThreshold uint64 // Created nodes @@ -365,10 +367,15 @@ func (b *NodeBuilder) WithStylusLongTermCache(enabled bool) *NodeBuilder { return b } -// WithDelayBuffer sets the delay-buffer threshold, which is the number of blocks the batch-poster +func (b *NodeBuilder) WithBoldContracts() *NodeBuilder { + b.deployBoldContracts = true + return b +} + +// WithDelayBufferThreshold sets the delay-buffer threshold, which is the number of blocks the batch-poster // is allowed to delay a batch with a delayed message. // Setting the threshold to zero disabled the delay buffer (default behaviour). -func (b *NodeBuilder) WithDelayBuffer(threshold uint64) *NodeBuilder { +func (b *NodeBuilder) WithDelayBufferThreshold(threshold uint64) *NodeBuilder { b.delayBufferThreshold = threshold return b } @@ -422,6 +429,7 @@ func (b *NodeBuilder) BuildL1(t *testing.T) { locator.LatestWasmModuleRoot(), b.withProdConfirmPeriodBlocks, true, + b.deployBoldContracts, b.delayBufferThreshold, ) b.L1.cleanup = func() { requireClose(t, b.L1.Stack) } @@ -526,6 +534,7 @@ func (b *NodeBuilder) BuildL3OnL2(t *testing.T) func() { locator.LatestWasmModuleRoot(), b.l3Config.withProdConfirmPeriodBlocks, false, + false, 0, ) @@ -1270,6 +1279,7 @@ func deployOnParentChain( wasmModuleRoot common.Hash, prodConfirmPeriodBlocks bool, chainSupportsBlobs bool, + deployBoldContracts bool, delayBufferThreshold uint64, ) (*chaininfo.RollupAddresses, *arbostypes.ParsedInitMessage) { parentChainInfo.GenerateAccount("RollupOwner") @@ -1293,24 +1303,62 @@ func deployOnParentChain( parentChainReader.Start(ctx) defer parentChainReader.StopAndWait() - bufferConfig := arbnode.DefaultBufferConfig() - bufferConfig.Threshold = delayBufferThreshold - nativeToken := common.Address{} maxDataSize := big.NewInt(117964) - addresses, err := deploy.DeployOnParentChain( - ctx, - parentChainReader, - &parentChainTransactionOpts, - []common.Address{parentChainInfo.GetAddress("Sequencer")}, - parentChainInfo.GetAddress("RollupOwner"), - 0, - arbnode.GenerateRollupConfig(prodConfirmPeriodBlocks, wasmModuleRoot, parentChainInfo.GetAddress("RollupOwner"), chainConfig, serializedChainConfig, common.Address{}, bufferConfig), - nativeToken, - maxDataSize, - chainSupportsBlobs, - ) + var addresses *chaininfo.RollupAddresses + if deployBoldContracts { + miniStakeValues := []*big.Int{big.NewInt(5), big.NewInt(4), big.NewInt(3), big.NewInt(2), big.NewInt(1)} + opts := deploy.RollupConfigOpts{ + Prod: prodConfirmPeriodBlocks, + WasmModuleRoot: wasmModuleRoot, + RollupOwner: parentChainInfo.GetAddress("RollupOwner"), + ChainConfig: chainConfig, + SerializedChainConfig: serializedChainConfig, + LoserStakeEscrow: parentChainInfo.GetAddress("RollupOwner"), + MiniStakeValues: miniStakeValues, + StakeToken: deployStakeToken(t, ctx, parentChainInfo, parentChainClient), + GenesisExecutionState: rollupgen.AssertionState{ + GlobalState: rollupgen.GlobalState{}, + MachineStatus: 1, + EndHistoryRoot: [32]byte{}, + }, + GenesisInboxCount: big.NewInt(0), + AnyTrustFastConfirmer: common.Address{}, + LayerZeroBlockEdgeHeight: 1 << 5, + LayerZeroBigStepEdgeHeight: 1 << 10, + LayerZeroSmallStepEdgeHeight: 1 << 10, + NumBigStepLevel: 3, + BufferConfig: deploy.DefaultBufferConfig(), + } + opts.BufferConfig.Threshold = delayBufferThreshold + addresses, err = deploy.DeployOnParentChain( + ctx, + parentChainReader, + &parentChainTransactionOpts, + []common.Address{parentChainInfo.GetAddress("Sequencer")}, + parentChainInfo.GetAddress("RollupOwner"), + 0, + deploy.GenerateRollupConfig(&opts), + nativeToken, + maxDataSize, + chainSupportsBlobs, + ) + } else { + addresses, err = deploy.DeployLegacyOnParentChain( + ctx, + parentChainReader, + &parentChainTransactionOpts, + []common.Address{parentChainInfo.GetAddress("Sequencer")}, + parentChainInfo.GetAddress("RollupOwner"), + 0, + deploy.GenerateLegacyRollupConfig(prodConfirmPeriodBlocks, wasmModuleRoot, parentChainInfo.GetAddress("RollupOwner"), chainConfig, serializedChainConfig, common.Address{}), + nativeToken, + maxDataSize, + chainSupportsBlobs, + ) + } Require(t, err) + parentChainInfo.SetContract("Bridge", addresses.Bridge) parentChainInfo.SetContract("SequencerInbox", addresses.SequencerInbox) parentChainInfo.SetContract("Inbox", addresses.Inbox) @@ -1617,6 +1665,25 @@ func getDeadlineTimeout(t *testing.T, defaultTimeout time.Duration) time.Duratio return timeout } +func deployStakeToken(t *testing.T, ctx context.Context, info *BlockchainTestInfo, client *ethclient.Client) common.Address { + transactionOpts := info.GetDefaultTransactOpts("RollupOwner", ctx) + stakeToken, tx, tokenBindings, err := mocksgen.DeployTestWETH9( + &transactionOpts, + client, + "Weth", + "WETH", + ) + Require(t, err) + _, err = EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + transactionOpts.Value = big.NewInt(10000) + tx, err = tokenBindings.Deposit(&transactionOpts) + Require(t, err) + _, err = EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + return stakeToken +} + func deploySimple( t *testing.T, ctx context.Context, auth bind.TransactOpts, client *ethclient.Client, ) (common.Address, *mocksgen.Simple) { diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index 30b7352892..4d902f87ba 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -215,7 +215,6 @@ func setupSequencerInboxStub(ctx context.Context, t *testing.T, l1Info *Blockcha big.NewInt(117964), reader4844, false, - false, ) Require(t, err) _, err = EnsureTxSucceeded(ctx, l1Client, tx)