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

feat: send l1infotree data to the aggchain prover #183

Merged
Merged
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
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))
joanestebanr marked this conversation as resolved.
Show resolved Hide resolved

// 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
joanestebanr marked this conversation as resolved.
Show resolved Hide resolved
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
Loading