Skip to content

Commit

Permalink
Merge branch 'master' into add-missing-precompiles-comments
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuacolvin0 authored Dec 12, 2024
2 parents eb1c31b + 918aaf4 commit 0ae2f59
Show file tree
Hide file tree
Showing 4 changed files with 322 additions and 45 deletions.
108 changes: 81 additions & 27 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 @@ -172,6 +174,7 @@ type BatchPosterConfig struct {
ReorgResistanceMargin time.Duration `koanf:"reorg-resistance-margin" reload:"hot"`
CheckBatchCorrectness bool `koanf:"check-batch-correctness"`
MaxEmptyBatchDelay time.Duration `koanf:"max-empty-batch-delay"`
DelayBufferThresholdMargin uint64 `koanf:"delay-buffer-threshold-margin"`

gasRefunder common.Address
l1BlockBound l1BlockBound
Expand Down Expand Up @@ -230,6 +233,7 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) {
f.Duration(prefix+".reorg-resistance-margin", DefaultBatchPosterConfig.ReorgResistanceMargin, "do not post batch if its within this duration from layer 1 minimum bounds. Requires l1-block-bound option not be set to \"ignore\"")
f.Bool(prefix+".check-batch-correctness", DefaultBatchPosterConfig.CheckBatchCorrectness, "setting this to true will run the batch against an inbox multiplexer and verifies that it produces the correct set of messages")
f.Duration(prefix+".max-empty-batch-delay", DefaultBatchPosterConfig.MaxEmptyBatchDelay, "maximum empty batch posting delay, batch poster will only be able to post an empty batch if this time period building a batch has passed")
f.Uint64(prefix+".delay-buffer-threshold-margin", DefaultBatchPosterConfig.DelayBufferThresholdMargin, "the number of blocks to post the batch before reaching the delay buffer threshold")
redislock.AddConfigOptions(prefix+".redis-lock", f)
dataposter.DataPosterConfigAddOptions(prefix+".data-poster", f, dataposter.DefaultDataPosterConfig)
genericconf.WalletConfigAddOptions(prefix+".parent-chain-wallet", f, DefaultBatchPosterConfig.ParentChainWallet.Pathname)
Expand Down Expand Up @@ -263,6 +267,7 @@ var DefaultBatchPosterConfig = BatchPosterConfig{
ReorgResistanceMargin: 10 * time.Minute,
CheckBatchCorrectness: true,
MaxEmptyBatchDelay: 3 * 24 * time.Hour,
DelayBufferThresholdMargin: 25, // 5 minutes considering 12-second blocks
}

var DefaultBatchPosterL1WalletConfig = genericconf.WalletConfig{
Expand Down Expand Up @@ -294,6 +299,7 @@ var TestBatchPosterConfig = BatchPosterConfig{
UseAccessLists: true,
GasEstimateBaseFeeMultipleBips: arbmath.OneInUBips * 3 / 2,
CheckBatchCorrectness: true,
DelayBufferThresholdMargin: 0,
}

type BatchPosterOpts struct {
Expand Down Expand Up @@ -725,6 +731,7 @@ type buildingBatch struct {
haveUsefulMessage bool
use4844 bool
muxBackend *simulatedMuxBackend
firstDelayedMsg *arbostypes.MessageWithMetadata
firstNonDelayedMsg *arbostypes.MessageWithMetadata
firstUsefulMsg *arbostypes.MessageWithMetadata
}
Expand Down Expand Up @@ -963,41 +970,45 @@ 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 args []any
var kzgBlobs []kzg4844.Blob
var err error
args = append(args, seqNum)
if use4844 {
kzgBlobs, err = blobs.EncodeBlobs(l2MessageData)
if err != nil {
return nil, nil, fmt.Errorf("failed to encode blobs: %w", err)
}
// EIP4844 transactions to the sequencer inbox will not use transaction calldata for L2 info.
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)),
)
} else {
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)),
)
// EIP4844 transactions to the sequencer inbox will not use transaction calldata for L2 info.
args = append(args, l2MessageData)
}
args = append(args, new(big.Int).SetUint64(delayedMsg))
args = append(args, b.config().gasRefunder)
args = append(args, new(big.Int).SetUint64(uint64(prevMsgNum)))
args = append(args, new(big.Int).SetUint64(uint64(newMsgNum)))
if delayProof != nil {
args = append(args, delayProof)
}
calldata, err := method.Inputs.Pack(args...)
if err != nil {
return nil, nil, err
}
Expand All @@ -1023,7 +1034,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 +1086,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 +1340,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 +1359,27 @@ 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
threasholdLimit := firstDelayedMsgBlock + delayBuffer.Threshold - b.config().DelayBufferThresholdMargin
if latestBlock >= threasholdLimit {
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 +1471,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 +1494,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

0 comments on commit 0ae2f59

Please sign in to comment.