Skip to content

Commit

Permalink
Merge branch 'eigenda-v3.2.1' of github.com:Layr-Labs/nitro into epoc…
Browse files Browse the repository at this point in the history
…iask--fix-update-legacy-docker-artifact
  • Loading branch information
ethenotethan committed Nov 20, 2024
2 parents 3ed0ad5 + 48843f8 commit 5992935
Show file tree
Hide file tree
Showing 7 changed files with 412 additions and 28 deletions.
13 changes: 6 additions & 7 deletions arbitrator/prover/src/kzgbn254.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,12 @@ pub fn prove_kzg_preimage_bn254(
let mut length_bytes = Vec::with_capacity(32);
append_left_padded_biguint_be(&mut length_bytes, &BigUint::from(length_usize));

out.write_all(&commitment_hash.to_vec())?; // hash [:32]
out.write_all(&*z)?; // evaluation point [32:64]
out.write_all(&*proven_y)?; // expected output [64:96]
out.write_all(&xminusz_encoded_bytes)?; // g2TauMinusG2z [96:224]
out.write_all(&*commitment_encoded_bytes)?; // kzg commitment [224:288]
out.write_all(&proof_encoded_bytes)?; // proof [288:352]
out.write_all(&*length_bytes)?; // length of preimage [352:384]
out.write_all(&*z)?; // evaluation point [:32]
out.write_all(&*proven_y)?; // expected output [32:64]
out.write_all(&xminusz_encoded_bytes)?; // g2TauMinusG2z [64:192]
out.write_all(&*commitment_encoded_bytes)?; // kzg commitment [192:256]
out.write_all(&proof_encoded_bytes)?; // proof [256:320]
out.write_all(&*length_bytes)?; // length of preimage [320:352]

Ok(())
}
93 changes: 75 additions & 18 deletions arbnode/batch_poster.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ var (
batchPosterDALastSuccessfulActionGauge = metrics.NewRegisteredGauge("arb/batchPoster/action/da_last_success", nil)
batchPosterDASuccessCounter = metrics.NewRegisteredCounter("arb/batchPoster/action/da_success", nil)
batchPosterDAFailureCounter = metrics.NewRegisteredCounter("arb/batchPoster/action/da_failure", nil)
batchPosterDAFailoverCount = metrics.NewRegisteredCounter("arb/batchPoster/action/da_failover", nil)

batchPosterFailureCounter = metrics.NewRegisteredCounter("arb/batchPoster/action/failure", nil)

Expand Down Expand Up @@ -109,7 +110,7 @@ type BatchPoster struct {
building *buildingBatch
dapWriter daprovider.Writer
// This deviates from the DA spec but is necessary for the batch poster to work efficiently
// since we need to an extended method on the SequencerInbox contract
// since we need to an extended method on the SequencerInbox contract for posting EigenDA certificates
eigenDAWriter eigenda.EigenDAWriter
dapReaders []daprovider.Reader
dataPoster *dataposter.DataPoster
Expand All @@ -122,9 +123,10 @@ type BatchPoster struct {
backlog atomic.Uint64
lastHitL1Bounds time.Time // The last time we wanted to post a message but hit the L1 bounds

batchReverted atomic.Bool // indicates whether data poster batch was reverted
nextRevertCheckBlock int64 // the last parent block scanned for reverting batches
postedFirstBatch bool // indicates if batch poster has posted the first batch
batchReverted atomic.Bool // indicates whether data poster batch was reverted
nextRevertCheckBlock int64 // the last parent block scanned for reverting batches
postedFirstBatch bool // indicates if batch poster has posted the first batch
eigenDAFailoverToETHDA bool // indicates if batch poster should failover to ETHDA

accessList func(SequencerInboxAccs, AfterDelayedMessagesRead uint64) types.AccessList
}
Expand All @@ -148,6 +150,8 @@ type BatchPosterDangerousConfig struct {
type BatchPosterConfig struct {
Enable bool `koanf:"enable"`
DisableDapFallbackStoreDataOnChain bool `koanf:"disable-dap-fallback-store-data-on-chain" reload:"hot"`
// Enable failover to AnyTrust (if enabled) or native ETH DA if EigenDA fails.
EnableEigenDAFailover bool `koanf:"enable-eigenda-failover" reload:"hot"`
// Max batch size.
MaxSize int `koanf:"max-size" reload:"hot"`
// Maximum 4844 blob enabled batch size.
Expand Down Expand Up @@ -214,6 +218,7 @@ type BatchPosterConfigFetcher func() *BatchPosterConfig
func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) {
f.Bool(prefix+".enable", DefaultBatchPosterConfig.Enable, "enable posting batches to l1")
f.Bool(prefix+".disable-dap-fallback-store-data-on-chain", DefaultBatchPosterConfig.DisableDapFallbackStoreDataOnChain, "If unable to batch to DA provider, disable fallback storing data on chain")
f.Bool(prefix+".enable-eigenda-failover", DefaultBatchPosterConfig.EnableEigenDAFailover, "If EigenDA fails, failover to AnyTrust (if enabled) or native ETH DA")
f.Int(prefix+".max-size", DefaultBatchPosterConfig.MaxSize, "maximum batch size")
f.Int(prefix+".max-4844-batch-size", DefaultBatchPosterConfig.Max4844BatchSize, "maximum 4844 blob enabled batch size")
f.Int(prefix+".max-eigenda-batch-size", DefaultBatchPosterConfig.MaxEigenDABatchSize, "maximum EigenDA blob enabled batch size")
Expand Down Expand Up @@ -244,8 +249,9 @@ var DefaultBatchPosterConfig = BatchPosterConfig{
Enable: false,
DisableDapFallbackStoreDataOnChain: false,
// This default is overridden for L3 chains in applyChainParameters in cmd/nitro/nitro.go
MaxSize: 100000,
MaxEigenDABatchSize: 16_777_216,
EnableEigenDAFailover: false,
MaxSize: 100000,
MaxEigenDABatchSize: 16_777_216,
// Try to fill 3 blobs per batch
Max4844BatchSize: blobs.BlobEncodableData*(params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)/2 - 2000,
PollInterval: time.Second * 10,
Expand Down Expand Up @@ -325,7 +331,7 @@ var EigenDABatchPosterConfig = BatchPosterConfig{
L1BlockBoundBypass: time.Hour,
UseAccessLists: true,
GasEstimateBaseFeeMultipleBips: arbmath.OneInUBips * 3 / 2,
CheckBatchCorrectness: false,
CheckBatchCorrectness: true,
}

type BatchPosterOpts struct {
Expand Down Expand Up @@ -571,6 +577,14 @@ func (b *BatchPoster) getTxsInfoByBlock(ctx context.Context, number int64) ([]tx
return blk.Transactions, nil
}

func (b *BatchPoster) SetEigenDAClientMock() {
b.eigenDAWriter = eigenda.NewMockEigenDA(true)
}

func (b *BatchPoster) SetEigenDAWriter(writer eigenda.EigenDAWriter) {
b.eigenDAWriter = writer
}

// checkRevert checks blocks with number in range [from, to] whether they
// contain reverted batch_poster transaction.
// It returns true if it finds batch posting needs to halt, which is true if a batch reverts
Expand Down Expand Up @@ -1241,7 +1255,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error)
}

var useEigenDA bool
if b.eigenDAWriter != nil {
if b.eigenDAWriter != nil && !b.eigenDAFailoverToETHDA {
useEigenDA = true
}

Expand Down Expand Up @@ -1442,7 +1456,11 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error)
return false, nil
}

if b.dapWriter != nil {
var eigenDaBlobInfo *eigenda.EigenDABlobInfo
eigenDADispersed := false
failOver := false

if b.eigenDAWriter != nil && !b.eigenDAFailoverToETHDA {
if !b.redisLock.AttemptLock(ctx) {
return false, errAttemptLockFailed
}
Expand All @@ -1456,19 +1474,52 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error)
batchPosterDAFailureCounter.Inc(1)
return false, fmt.Errorf("%w: nonce changed from %d to %d while creating batch", storage.ErrStorageRace, nonce, gotNonce)
}
// #nosec G115
sequencerMsg, err = b.dapWriter.Store(ctx, sequencerMsg, uint64(time.Now().Add(config.DASRetentionPeriod).Unix()), config.DisableDapFallbackStoreDataOnChain)
if err != nil {
eigenDaBlobInfo, err = b.eigenDAWriter.Store(ctx, sequencerMsg)

if err != nil && errors.Is(err, eigenda.SvcUnavailableErr) && b.config().EnableEigenDAFailover && b.dapWriter != nil { // Failover to anytrust commitee if enabled
log.Error("EigenDA service is unavailable, failing over to any trust mode")
b.building.useEigenDA = false
failOver = true
}

if err != nil && errors.Is(err, eigenda.SvcUnavailableErr) && b.config().EnableEigenDAFailover && b.dapWriter == nil { // Failover to ETH DA if enabled
// when failing over to ETHDA (i.e 4844, calldata), we may need to re-encode the batch. To do this in compliance with the existing code, it's easiest
// to update an internal field and retrigger the poster's event loop. Since the batch poster can be distributed across mulitple nodes, there could be
// degraded temporary performance as each batch poster will re-encode the batch on another event loop tick using the coordination lock which could worst case
// could require every batcher instance to fail dispersal to EigenDA.
// However, this is a rare event and the performance impact is minimal.

log.Error("EigenDA service is unavailable and anytrust is disabled, failing over to ETH DA")
b.eigenDAFailoverToETHDA = true

// // if the batch's size exceeds the native DA max size limit, we must re-encode the batch to accomodate the AnyTrust, calldata, and 4844 size limits
if (len(sequencerMsg) > b.config().MaxSize && !b.building.use4844) || (len(sequencerMsg) > b.config().Max4844BatchSize && b.building.use4844) {
batchPosterDAFailureCounter.Inc(1)
batchPosterDAFailoverCount.Inc(1)

b.building = nil
return false, nil
}

}

if err != nil && !failOver {
batchPosterDAFailureCounter.Inc(1)
return false, err
}

batchPosterDASuccessCounter.Inc(1)
batchPosterDALastSuccessfulActionGauge.Update(time.Now().Unix())
} else if failOver {
batchPosterDAFailoverCount.Inc(1)
} else {
batchPosterDASuccessCounter.Inc(1)
batchPosterDALastSuccessfulActionGauge.Update(time.Now().Unix())
eigenDADispersed = true
}
}

var eigenDaBlobInfo *eigenda.EigenDABlobInfo
if b.eigenDAWriter != nil {
if b.dapWriter != nil && !eigenDADispersed {
if !b.redisLock.AttemptLock(ctx) {
return false, errAttemptLockFailed
}

gotNonce, gotMeta, err := b.dataPoster.GetNextNonceAndMeta(ctx)
if err != nil {
Expand All @@ -1479,7 +1530,8 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error)
batchPosterDAFailureCounter.Inc(1)
return false, fmt.Errorf("%w: nonce changed from %d to %d while creating batch", storage.ErrStorageRace, nonce, gotNonce)
}
eigenDaBlobInfo, err = b.eigenDAWriter.Store(ctx, sequencerMsg)
// #nosec G115
sequencerMsg, err = b.dapWriter.Store(ctx, sequencerMsg, uint64(time.Now().Add(config.DASRetentionPeriod).Unix()), config.DisableDapFallbackStoreDataOnChain)
if err != nil {
batchPosterDAFailureCounter.Inc(1)
return false, err
Expand Down Expand Up @@ -1547,6 +1599,10 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error)
return false, err
}

if !b.building.useEigenDA && b.eigenDAFailoverToETHDA {
b.eigenDAFailoverToETHDA = false
}

if config.CheckBatchCorrectness {
dapReaders := b.dapReaders
if b.building.use4844 {
Expand Down Expand Up @@ -1598,6 +1654,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error)
log.Info(
"BatchPoster: batch sent",
"eigenDA", b.building.useEigenDA,
"4844", b.building.use4844,
"sequenceNumber", batchPosition.NextSeqNum,
"from", batchPosition.MessageCount,
"to", b.building.msgCount,
Expand Down
15 changes: 13 additions & 2 deletions arbnode/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ func ConfigDefaultL1NonSequencerTest() *Config {
return &config
}

func (cfg *Config) WithEigenDATestConfigParams() *Config {
cfg.EigenDA.Enable = true
cfg.EigenDA.Rpc = "http://localhost:4242"
return cfg
}
func ConfigDefaultL2Test() *Config {
config := ConfigDefault
config.Dangerous = TestDangerousConfig
Expand Down Expand Up @@ -576,8 +581,14 @@ func createNodeImpl(
}
} else if l2Config.ArbitrumChainParams.DataAvailabilityCommittee {
return nil, errors.New("a data availability service is required for this chain, but it was not configured")
} else if config.EigenDA.Enable {
log.Info("EigenDA enabled")
}

if config.EigenDA.Enable && config.DataAvailability.Enable && !config.BatchPoster.EnableEigenDAFailover {
return nil, errors.New("eigenDA and anytrust cannot both be enabled without EnableEigenDAFailover=true in batch poster config")
}

if config.EigenDA.Enable {
log.Info("EigenDA enabled", "failover", config.BatchPoster.EnableEigenDAFailover, "anytrust", config.DataAvailability.Enable)
eigenDAService, err := eigenda.NewEigenDA(&config.EigenDA)
if err != nil {
return nil, err
Expand Down
4 changes: 3 additions & 1 deletion cmd/replay/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,9 @@ func main() {
// DAS batch and keysets are all together in the same preimage binary.
dasReader = &PreimageDASReader{}
dasKeysetFetcher = &PreimageDASReader{}
} else if eigenDAEnabled {
}

if eigenDAEnabled {
eigenDAReader = &EigenDAPreimageReader{}
}
backend := WavmInbox{}
Expand Down
8 changes: 8 additions & 0 deletions eigenda/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)

var (
SvcUnavailableErr = fmt.Errorf("eigenda service is unavailable")
)

type EigenDAProxyClient struct {
client ProxyClient
}
Expand Down Expand Up @@ -123,6 +127,10 @@ func (c *client) GetData(ctx context.Context, comm []byte) ([]byte, error) {
return nil, fmt.Errorf("received unexpected response code: %d", resp.StatusCode)
}

if resp.StatusCode == http.StatusServiceUnavailable {
return nil, SvcUnavailableErr
}

return io.ReadAll(resp.Body)
}

Expand Down
110 changes: 110 additions & 0 deletions eigenda/proxy_mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package eigenda

import (
"context"
"errors"
"fmt"

"github.com/Layr-Labs/eigenda/api/grpc/disperser"
)

var (
mockCert = []byte{0x01, 0x02, 0x03, 0x04}
mockBlobData = []byte("mock data")
mockBlobInfo = disperser.BlobInfo{}
mockHealthErr = errors.New("service unavailable")
mockServiceErr = fmt.Errorf("mock error: failed to store data")
)

// MockEigenDA implements the EigenDA interface using the mock client.
type MockEigenDA struct {
client *MockEigenDAProxyClient
}

// NewMockEigenDA initializes the mock EigenDA instance.
func NewMockEigenDA(fallbackErr bool) *MockEigenDA {
client := NewMockEigenDAProxyClient(fallbackErr)
return &MockEigenDA{client: client}
}

// QueryBlob mocks the QueryBlob function.
func (e *MockEigenDA) QueryBlob(ctx context.Context, cert *EigenDABlobInfo, domainFilter string) ([]byte, error) {
return []byte("mockData"), nil
}

// Store mocks the Store function, returning mock EigenDABlobInfo.
func (e *MockEigenDA) Store(ctx context.Context, data []byte) (*EigenDABlobInfo, error) {
var blobInfo = &EigenDABlobInfo{}
cert, err := e.client.Put(ctx, data)
if err != nil {
return nil, err
}

blobInfo.LoadBlobInfo(cert)

return blobInfo, nil
}

// Serialize mocks the Serialize function, returning a simple byte slice.
func (e *MockEigenDA) Serialize(blobInfo *EigenDABlobInfo) ([]byte, error) {
return []byte("mockSerializedData"), nil
}

type MockProxyClient struct {
ShouldFail bool // Flag to toggle failure modes
ShouldReturn503 bool
}

func NewMockProxyClient(failover bool) ProxyClient {
return &MockProxyClient{
ShouldFail: failover,
}
}

func (m *MockProxyClient) Health() error {
if m.ShouldFail {
return mockHealthErr
}
return nil
}

func (m *MockProxyClient) GetData(ctx context.Context, cert []byte) ([]byte, error) {
if m.ShouldFail {
return nil, fmt.Errorf("mock error: failed to get data")
}
return mockBlobData, nil
}

func (m *MockProxyClient) SetData(ctx context.Context, data []byte) ([]byte, error) {
if m.ShouldFail {
return nil, mockServiceErr
}
return mockCert, nil
}

type MockEigenDAProxyClient struct {
client ProxyClient
}

func NewMockEigenDAProxyClient(shouldFail bool) *MockEigenDAProxyClient {
return &MockEigenDAProxyClient{client: NewMockProxyClient(shouldFail)}
}

func (c *MockEigenDAProxyClient) Put(ctx context.Context, data []byte) (*disperser.BlobInfo, error) {
if c.client.(*MockProxyClient).ShouldFail {
return nil, SvcUnavailableErr
}
return &mockBlobInfo, nil
}

func (c *MockEigenDAProxyClient) Get(ctx context.Context, blobInfo *disperser.BlobInfo) ([]byte, error) {
if c.client.(*MockProxyClient).ShouldFail {
return nil, fmt.Errorf("mock error: failed to get data")
}

if c.client.(*MockProxyClient).ShouldReturn503 {
return nil, SvcUnavailableErr
}

return mockBlobData, nil
}
Loading

0 comments on commit 5992935

Please sign in to comment.