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

Open
wants to merge 18 commits into
base: bold-review
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
)
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 {
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