Skip to content

Commit

Permalink
feat: send l1infotree data to the aggchain prover (#183)
Browse files Browse the repository at this point in the history
Co-authored-by: Arpit Temani <[email protected]>
  • Loading branch information
goran-ethernal and temaniarpit27 authored Feb 6, 2025
1 parent 8279ee6 commit 395838f
Show file tree
Hide file tree
Showing 28 changed files with 932 additions and 167 deletions.
6 changes: 4 additions & 2 deletions aggsender/aggsender.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ func New(
aggLayerClient agglayer.AgglayerClientInterface,
l1InfoTreeSyncer *l1infotreesync.L1InfoTreeSync,
l2Syncer types.L2BridgeSyncer,
epochNotifier types.EpochNotifier) (*AggSender, error) {
epochNotifier types.EpochNotifier,
l1Client types.EthClient) (*AggSender, error) {
storageConfig := db.AggSenderSQLStorageConfig{
DBPath: cfg.StoragePath,
KeepCertificatesHistory: cfg.KeepCertificatesHistory,
Expand Down Expand Up @@ -90,7 +91,8 @@ func New(
return nil, fmt.Errorf("error creating aggkit prover client: %w", err)
}

flowManager = newAggchainProverFlow(logger, cfg, aggchainProofClient, storage, l1InfoTreeSyncer, l2Syncer)
flowManager = newAggchainProverFlow(logger, cfg, aggchainProofClient, storage,
l1InfoTreeSyncer, l2Syncer, l1Client)
} else {
flowManager = newPPFlow(logger, cfg, storage, l1InfoTreeSyncer, l2Syncer)
}
Expand Down
4 changes: 2 additions & 2 deletions aggsender/aggsender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func TestAggSenderStart(t *testing.T) {
aggLayerMock,
nil,
bridgeL2SyncerMock,
epochNotifierMock)
epochNotifierMock, nil)
require.NoError(t, err)
require.NotNil(t, aggSender)
ch := make(chan aggsendertypes.EpochEvent)
Expand Down Expand Up @@ -114,7 +114,7 @@ func TestAggSenderSendCertificates(t *testing.T) {
AggLayerMock,
nil,
bridgeL2SyncerMock,
epochNotifierMock)
epochNotifierMock, nil)
require.NoError(t, err)
require.NotNil(t, aggSender)
ch := make(chan aggsendertypes.EpochEvent, 2)
Expand Down
130 changes: 99 additions & 31 deletions aggsender/flow_aggchain_prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@ package aggsender
import (
"context"
"fmt"
"math/big"

"github.com/agglayer/aggkit/agglayer"
"github.com/agglayer/aggkit/aggsender/db"
"github.com/agglayer/aggkit/aggsender/grpc"
"github.com/agglayer/aggkit/aggsender/types"
"github.com/agglayer/aggkit/etherman"
treeTypes "github.com/agglayer/aggkit/tree/types"
"github.com/ethereum/go-ethereum/common"
)

var finalizedBlockBigInt = big.NewInt(int64(etherman.Finalized))

// aggchainProverFlow is a struct that holds the logic for the AggchainProver prover type flow
type aggchainProverFlow struct {
*baseFlow

l1Client types.EthClient
aggchainProofClient grpc.AggchainProofClientInterface
}

Expand All @@ -24,8 +30,10 @@ func newAggchainProverFlow(log types.Logger,
aggkitProverClient grpc.AggchainProofClientInterface,
storage db.AggSenderStorage,
l1InfoTreeSyncer types.L1InfoTreeSyncer,
l2Syncer types.L2BridgeSyncer) *aggchainProverFlow {
l2Syncer types.L2BridgeSyncer,
l1Client types.EthClient) *aggchainProverFlow {
return &aggchainProverFlow{
l1Client: l1Client,
aggchainProofClient: aggkitProverClient,
baseFlow: &baseFlow{
log: log,
Expand All @@ -49,7 +57,6 @@ func (a *aggchainProverFlow) GetCertificateBuildParams(ctx context.Context) (*ty

if lastSentCertificateInfo != nil && lastSentCertificateInfo.Status == agglayer.InError {
// if the last certificate was in error, we need to resend it

a.log.Infof("resending the same InError certificate: %s", lastSentCertificateInfo.String())

bridges, claims, err := a.getBridgesAndClaims(ctx, lastSentCertificateInfo.FromBlock, lastSentCertificateInfo.ToBlock)
Expand All @@ -65,12 +72,17 @@ func (a *aggchainProverFlow) GetCertificateBuildParams(ctx context.Context) (*ty
"but no bridges to resend the same certificate", lastSentCertificateInfo.String())
}

proof := lastSentCertificateInfo.AggchainProof
aggProof := lastSentCertificateInfo.AggchainProof
toBlock := lastSentCertificateInfo.ToBlock

if len(proof) == 0 {
if len(aggProof) == 0 {
proof, leaf, root, err := a.getFinalizedL1InfoTreeData(ctx)
if err != nil {
return nil, fmt.Errorf("aggchainProverFlow - error getting finalized L1 Info tree data: %w", err)
}

aggchainProof, err := a.aggchainProofClient.GenerateAggchainProof(lastSentCertificateInfo.FromBlock,
lastSentCertificateInfo.ToBlock, common.Hash{}, common.Hash{}, [32]common.Hash{})
lastSentCertificateInfo.ToBlock, root, leaf, proof)
if err != nil {
return nil, fmt.Errorf("aggchainProverFlow - error fetching aggchain proof for block range %d : %d : %w",
lastSentCertificateInfo.FromBlock, lastSentCertificateInfo.ToBlock, err)
Expand All @@ -81,7 +93,7 @@ func (a *aggchainProverFlow) GetCertificateBuildParams(ctx context.Context) (*ty
aggchainProof.StartBlock, aggchainProof.EndBlock, aggchainProof.Proof,
lastSentCertificateInfo.FromBlock, lastSentCertificateInfo.ToBlock)

proof = aggchainProof.Proof
aggProof = aggchainProof.Proof

if aggchainProof.EndBlock < lastSentCertificateInfo.ToBlock {
// aggchain prover can return a proof for a smaller range than requested
Expand All @@ -99,7 +111,7 @@ func (a *aggchainProverFlow) GetCertificateBuildParams(ctx context.Context) (*ty
Claims: claims,
LastSentCertificate: lastSentCertificateInfo,
CreatedAt: lastSentCertificateInfo.CreatedAt,
AggchainProof: proof,
AggchainProof: aggProof,
}

buildParams, err = adjustBlockRange(buildParams, lastSentCertificateInfo.ToBlock, toBlock)
Expand All @@ -116,8 +128,13 @@ func (a *aggchainProverFlow) GetCertificateBuildParams(ctx context.Context) (*ty
return nil, err
}

proof, leaf, root, err := a.getFinalizedL1InfoTreeData(ctx)
if err != nil {
return nil, fmt.Errorf("aggchainProverFlow - error getting finalized L1 Info tree data: %w", err)
}

aggchainProof, err := a.aggchainProofClient.GenerateAggchainProof(
buildParams.FromBlock, buildParams.ToBlock, common.Hash{}, common.Hash{}, [32]common.Hash{})
buildParams.FromBlock, buildParams.ToBlock, root, leaf, proof)
if err != nil {
return nil, fmt.Errorf("aggchainProverFlow - error fetching aggchain proof for block range %d : %d : %w",
buildParams.FromBlock, buildParams.ToBlock, err)
Expand All @@ -137,6 +154,80 @@ func (a *aggchainProverFlow) GetCertificateBuildParams(ctx context.Context) (*ty
return buildParams, nil
}

// getFinalizedL1InfoTreeData returns the L1 Info tree data for the last finalized processed block
// l1InfoTreeData is:
// - the leaf data of the highest index leaf on that block and root
// - merkle proof of given l1 info tree leaf
// - the root of the l1 info tree on that block
func (a *aggchainProverFlow) getFinalizedL1InfoTreeData(ctx context.Context,
) (treeTypes.Proof, common.Hash, common.Hash, error) {
lastFinalizedProcessedBlock, err := a.getLatestProcessedFinalizedBlock(ctx)
if err != nil {
return treeTypes.Proof{}, common.Hash{}, common.Hash{},
fmt.Errorf("aggchainProverFlow - error getting latest processed finalized block: %w", err)
}

root, err := a.l1InfoTreeSyncer.GetLastL1InfoTreeRootByBlockNum(ctx, lastFinalizedProcessedBlock)
if err != nil {
return treeTypes.Proof{}, common.Hash{}, common.Hash{},
fmt.Errorf("aggchainProverFlow - error getting last L1 Info tree root by block num %d: %w",
lastFinalizedProcessedBlock, err)
}

leaf, err := a.l1InfoTreeSyncer.GetInfoByIndex(ctx, root.Index)
if err != nil {
return treeTypes.Proof{}, common.Hash{}, common.Hash{},
fmt.Errorf("aggchainProverFlow - error getting L1 Info tree leaf by index %d: %w", root.Index, err)
}

proof, err := a.l1InfoTreeSyncer.GetL1InfoTreeMerkleProofFromIndexToRoot(ctx, root.Index, root.Hash)
if err != nil {
return treeTypes.Proof{}, common.Hash{}, common.Hash{},
fmt.Errorf("aggchainProverFlow - error getting L1 Info tree merkle proof from index %d to root %s: %w",
root.Index, root.Hash.String(), err)
}

return proof, leaf.Hash, root.Hash, nil
}

// getLatestProcessedFinalizedBlock returns the latest processed finalized block from the l1infotreesyncer
func (a *aggchainProverFlow) getLatestProcessedFinalizedBlock(ctx context.Context) (uint64, error) {
lastFinalizedL1Block, err := a.l1Client.HeaderByNumber(ctx, finalizedBlockBigInt)
if err != nil {
return 0, fmt.Errorf("aggchainProverFlow - error getting latest finalized L1 block: %w", err)
}

lastProcessedBlockNum, lastProcessedBlockHash, err := a.l1InfoTreeSyncer.GetProcessedBlockUntil(ctx,
lastFinalizedL1Block.Number.Uint64())
if err != nil {
return 0, fmt.Errorf("aggchainProverFlow - error getting latest processed block from l1infotreesyncer: %w", err)
}

if lastProcessedBlockNum == 0 {
return 0, fmt.Errorf("aggchainProverFlow - l1infotreesyncer did not process any block yet")
}

if lastFinalizedL1Block.Number.Uint64() > lastProcessedBlockNum {
// syncer has a lower block than the finalized block, so we need to get that block from the l1 node
lastFinalizedL1Block, err = a.l1Client.HeaderByNumber(ctx, new(big.Int).SetUint64(lastProcessedBlockNum))
if err != nil {
return 0, fmt.Errorf("aggchainProverFlow - error getting latest processed finalized block: %d: %w",
lastProcessedBlockNum, err)
}
}

if (lastProcessedBlockHash == common.Hash{}) || (lastProcessedBlockHash == lastFinalizedL1Block.Hash()) {
// if the hash is empty it means that this is an old block that was processed before this
// feature was added, so we will consider it finalized
return lastFinalizedL1Block.Number.Uint64(), nil
}

return 0, fmt.Errorf("aggchainProverFlow - l1infotreesyncer returned a different hash for "+
"the latest finalized block: %d. Might be that syncer did not process a reorg yet. "+
"Expected hash: %s, got: %s", lastProcessedBlockNum,
lastFinalizedL1Block.Hash().String(), lastProcessedBlockHash.String())
}

// adjustBlockRange adjusts the block range of the certificate to match the range returned by the aggchain prover
func adjustBlockRange(buildParams *types.CertificateBuildParams,
requestedToBlock, aggchainProverToBlock uint64) (*types.CertificateBuildParams, error) {
Expand All @@ -152,26 +243,3 @@ func adjustBlockRange(buildParams *types.CertificateBuildParams,

return buildParams, nil
}

// getLastSentBlockAndRetryCount returns the last sent block of the last sent certificate
// if there is no previosly sent certificate, it returns 0 and 0
func getLastSentBlockAndRetryCount(lastSentCertificateInfo *types.CertificateInfo) (uint64, int) {
if lastSentCertificateInfo == nil {
return 0, 0
}

retryCount := 0
lastSentBlock := lastSentCertificateInfo.ToBlock

if lastSentCertificateInfo.Status == agglayer.InError {
// if the last certificate was in error, we need to resend it
// from the block before the error
if lastSentCertificateInfo.FromBlock > 0 {
lastSentBlock = lastSentCertificateInfo.FromBlock - 1
}

retryCount = lastSentCertificateInfo.RetryCount + 1
}

return lastSentBlock, retryCount
}
Loading

0 comments on commit 395838f

Please sign in to comment.