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

Modify the batch poster to support the delay buffer feature #2758

Merged
merged 27 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ecbe094
Add function to get delay buffer config
gligneul Oct 18, 2024
ef66893
Force batch to avoid consuming the delay buffer
gligneul Oct 18, 2024
a05ee98
Add delay proof when posting a batch
gligneul Oct 21, 2024
d388238
Fix force-batch delay-buffer condition
gligneul Oct 22, 2024
25ef0dc
Support the delay buffer in the deploy pkg
gligneul Oct 22, 2024
4259e94
Fix delay proof
gligneul Oct 22, 2024
a6c9b2a
Fix delay buffer threshold check
gligneul Oct 23, 2024
ba1079d
Fix off-by-one error in delay proof
gligneul Oct 23, 2024
5cc8a44
Test batch poster with delay buffer enabled
gligneul Oct 23, 2024
410a65d
Add two more delay-buffer tests
gligneul Oct 24, 2024
fb86fad
Fix gofmt
gligneul Oct 28, 2024
b0d586c
Use BoLD contracts to test delay buffer
gligneul Oct 28, 2024
9d64847
Merge remote-tracking branch 'origin/bold-review' into gligneul/delay…
gligneul Oct 29, 2024
b203a3d
Revert "Use BoLD contracts to test delay buffer"
gligneul Nov 26, 2024
5fc42c3
Merge remote-tracking branch 'origin/bold-review' into gligneul/delay…
gligneul Nov 26, 2024
2264d01
Remove completed TODO
gligneul Nov 26, 2024
fb34a9f
Fix comment
gligneul Nov 26, 2024
5cffbc3
Merge branch 'bold-review' into gligneul/delay-buffer-bold
gligneul Nov 28, 2024
f2bc708
Merge branch 'bold-review' into gligneul/delay-buffer-bold
gligneul Nov 29, 2024
f77b262
Merge branch 'bold-review' into gligneul/delay-buffer-bold
gligneul Dec 4, 2024
86300b5
Merge branch 'master' into gligneul/delay-buffer-bold
eljobe Dec 9, 2024
40de16a
Merge branch 'master' into gligneul/delay-buffer-bold
gligneul Dec 10, 2024
c2fbee7
Merge branch 'master' into gligneul/delay-buffer-bold
gligneul Dec 10, 2024
3aea97e
Merge branch 'master' into gligneul/delay-buffer-bold
gligneul Dec 12, 2024
6928d0b
Remove repeated code in encodeAddBatch function
gligneul Dec 12, 2024
708cc0c
Add margin to delay buffer threshold
gligneul Dec 12, 2024
2c79487
Merge branch 'master' into gligneul/delay-buffer-bold
PlasmaPower Dec 12, 2024
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
101 changes: 90 additions & 11 deletions arbnode/batch_poster.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -725,6 +727,7 @@ type buildingBatch struct {
haveUsefulMessage bool
use4844 bool
muxBackend *simulatedMuxBackend
firstDelayedMsg *arbostypes.MessageWithMetadata
firstNonDelayedMsg *arbostypes.MessageWithMetadata
firstUsefulMsg *arbostypes.MessageWithMetadata
}
Expand Down Expand Up @@ -963,15 +966,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
Expand All @@ -980,6 +993,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,
Expand All @@ -988,15 +1004,36 @@ 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,
new(big.Int).SetUint64(delayedMsg),
b.config().gasRefunder,
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,
gligneul marked this conversation as resolved.
Show resolved Hide resolved
)
default:
panic("impossible")
}
if err != nil {
return nil, nil, err
Expand All @@ -1023,7 +1060,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()
Expand Down Expand Up @@ -1065,7 +1112,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
}
Expand Down Expand Up @@ -1319,7 +1366,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++
Expand All @@ -1334,6 +1385,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 latestBlock >= firstDelayedMsgBlock+delayBuffer.Threshold {
gligneul marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down Expand Up @@ -1425,7 +1496,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
}
Expand All @@ -1440,7 +1519,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
}
Expand Down
87 changes: 87 additions & 0 deletions arbnode/delay_buffer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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"
"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/bold/solgen/go/bridgegen"
"github.com/offchainlabs/nitro/arbos/arbostypes"
"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, sequencerInbox *bridgegen.SequencerInbox) (
*DelayBufferConfig, error) {

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
}

// 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) {

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
beforeDelayedAcc, err = inbox.GetDelayedAcc(seqNum - 1)
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
}
Loading
Loading