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

fix: Various pessimistic proofs fixes and adaption to kurtosis-cdk pessimistic proof branch #165

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 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
25 changes: 25 additions & 0 deletions agglayer/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ func (c *AggLayerClient) WaitTxToBeMined(hash common.Hash, ctx context.Context)

// SendCertificate sends a certificate to the AggLayer
func (c *AggLayerClient) SendCertificate(certificate *SignedCertificate) (common.Hash, error) {
if err := defaultNilFieldsInCertificate(certificate); err != nil {
goran-ethernal marked this conversation as resolved.
Show resolved Hide resolved
return common.Hash{}, err
}

response, err := rpc.JSONRPCCall(c.url, "interop_sendCertificate", certificate)
if err != nil {
return common.Hash{}, err
Expand Down Expand Up @@ -128,3 +132,24 @@ func (c *AggLayerClient) GetCertificateHeader(certificateHash common.Hash) (*Cer

return result, nil
}

// defaultNilFieldsInCertificate sets the fields of the certificate to default values if they are nil
func defaultNilFieldsInCertificate(certificate *SignedCertificate) error {
if certificate == nil || certificate.Certificate == nil {
return errors.New("certificate is nil")
}

if certificate.Signature == nil {
certificate.Signature = &Signature{}
}

if certificate.Certificate.BridgeExits == nil {
certificate.Certificate.BridgeExits = []*BridgeExit{}
}

if certificate.Certificate.ImportedBridgeExits == nil {
certificate.Certificate.ImportedBridgeExits = []*ImportedBridgeExit{}
}

return nil
}
87 changes: 87 additions & 0 deletions agglayer/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package agglayer

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
)

func TestDefaultNilFieldsInCertificate(t *testing.T) {
t.Parallel()

tests := []struct {
name string
certificate *SignedCertificate
expectedErr error
}{
{
name: "Nil certificate",
certificate: nil,
expectedErr: errors.New("certificate is nil"),
},
{
name: "Nil inner certificate",
certificate: &SignedCertificate{
Certificate: nil,
},
expectedErr: errors.New("certificate is nil"),
},
{
name: "Nil signature",
certificate: &SignedCertificate{
Certificate: &Certificate{},
Signature: nil,
},
expectedErr: nil,
},
{
name: "Nil BridgeExits",
certificate: &SignedCertificate{
Certificate: &Certificate{
BridgeExits: nil,
},
Signature: &Signature{},
},
expectedErr: nil,
},
{
name: "Nil ImportedBridgeExits",
certificate: &SignedCertificate{
Certificate: &Certificate{
ImportedBridgeExits: nil,
},
Signature: &Signature{},
},
expectedErr: nil,
},
{
name: "All fields non-nil",
certificate: &SignedCertificate{
Certificate: &Certificate{
BridgeExits: []*BridgeExit{},
ImportedBridgeExits: []*ImportedBridgeExit{},
},
Signature: &Signature{},
},
expectedErr: nil,
},
}

for _, tt := range tests {
tt := tt

t.Run(tt.name, func(t *testing.T) {
t.Parallel()

err := defaultNilFieldsInCertificate(tt.certificate)
assert.Equal(t, tt.expectedErr, err)

if tt.certificate != nil && tt.certificate.Certificate != nil {
assert.NotNil(t, tt.certificate.Signature)
assert.NotNil(t, tt.certificate.Certificate.BridgeExits)
assert.NotNil(t, tt.certificate.Certificate.ImportedBridgeExits)
}
})
}
}
88 changes: 54 additions & 34 deletions aggsender/aggsender.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ var (
errNoBridgesAndClaims = errors.New("no bridges and claims to build certificate")
errInvalidSignatureSize = errors.New("invalid signature size")

zeroLER = common.HexToHash("0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757")
zeroLER = common.HexToHash("0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757")
nonSettledStatuses = []agglayer.CertificateStatus{agglayer.Pending, agglayer.Candidate, agglayer.Proven}
)

// AggSender is a component that will send certificates to the aggLayer
Expand Down Expand Up @@ -63,6 +64,8 @@ func New(
return nil, err
}

logger.Info(cfg.String())

return &AggSender{
cfg: cfg,
log: logger,
Expand All @@ -87,7 +90,7 @@ func (a *AggSender) sendCertificates(ctx context.Context) {
for {
select {
case <-ticker.C:
if err := a.sendCertificate(ctx); err != nil {
if _, err := a.sendCertificate(ctx); err != nil {
log.Error(err)
}
case <-ctx.Done():
Expand All @@ -98,27 +101,27 @@ func (a *AggSender) sendCertificates(ctx context.Context) {
}

// sendCertificate sends certificate for a network
func (a *AggSender) sendCertificate(ctx context.Context) error {
func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertificate, error) {
a.log.Infof("trying to send a new certificate...")

shouldSend, err := a.shouldSendCertificate(ctx)
shouldSend, err := a.shouldSendCertificate()
if err != nil {
return err
return nil, err
}

if !shouldSend {
a.log.Infof("waiting for pending certificates to be settled")
return nil
return nil, nil
}

lasL2BlockSynced, err := a.l2Syncer.GetLastProcessedBlock(ctx)
if err != nil {
return fmt.Errorf("error getting last processed block from l2: %w", err)
return nil, fmt.Errorf("error getting last processed block from l2: %w", err)
}

lastSentCertificateInfo, err := a.storage.GetLastSentCertificate(ctx)
lastSentCertificateInfo, err := a.storage.GetLastSentCertificate()
if err != nil {
return err
return nil, err
}

previousToBlock := lastSentCertificateInfo.ToBlock
Expand All @@ -131,44 +134,44 @@ func (a *AggSender) sendCertificate(ctx context.Context) error {
if previousToBlock >= lasL2BlockSynced {
a.log.Infof("no new blocks to send a certificate, last certificate block: %d, last L2 block: %d",
previousToBlock, lasL2BlockSynced)
return nil
return nil, nil
}

fromBlock := previousToBlock + 1
toBlock := lasL2BlockSynced

bridges, err := a.l2Syncer.GetBridgesPublished(ctx, fromBlock, toBlock)
if err != nil {
return fmt.Errorf("error getting bridges: %w", err)
return nil, fmt.Errorf("error getting bridges: %w", err)
}

if len(bridges) == 0 {
a.log.Infof("no bridges consumed, no need to send a certificate from block: %d to block: %d", fromBlock, toBlock)
return nil
return nil, nil
}

claims, err := a.l2Syncer.GetClaims(ctx, fromBlock, toBlock)
if err != nil {
return fmt.Errorf("error getting claims: %w", err)
return nil, fmt.Errorf("error getting claims: %w", err)
}

a.log.Infof("building certificate for block: %d to block: %d", fromBlock, toBlock)

certificate, err := a.buildCertificate(ctx, bridges, claims, lastSentCertificateInfo, toBlock)
if err != nil {
return fmt.Errorf("error building certificate: %w", err)
return nil, fmt.Errorf("error building certificate: %w", err)
}

signedCertificate, err := a.signCertificate(certificate)
if err != nil {
return fmt.Errorf("error signing certificate: %w", err)
return nil, fmt.Errorf("error signing certificate: %w", err)
}

a.saveCertificateToFile(signedCertificate)

certificateHash, err := a.aggLayerClient.SendCertificate(signedCertificate)
if err != nil {
return fmt.Errorf("error sending certificate: %w", err)
return nil, fmt.Errorf("error sending certificate: %w", err)
}
log.Infof("certificate send: Height: %d hash: %s", signedCertificate.Height, certificateHash.String())

Expand All @@ -179,24 +182,24 @@ func (a *AggSender) sendCertificate(ctx context.Context) error {
FromBlock: fromBlock,
ToBlock: toBlock,
}); err != nil {
return fmt.Errorf("error saving last sent certificate in db: %w", err)
return nil, fmt.Errorf("error saving last sent certificate in db: %w", err)
}

a.log.Infof("certificate: %s sent successfully for range of l2 blocks (from block: %d, to block: %d)",
certificateHash, fromBlock, toBlock)

return nil
return signedCertificate, nil
}

// saveCertificate saves the certificate to a tmp file
func (a *AggSender) saveCertificateToFile(signedCertificate *agglayer.SignedCertificate) {
if signedCertificate == nil || !a.cfg.SaveCertificatesToFiles {
if signedCertificate == nil || a.cfg.SaveCertificatesToFilesPath == "" {
return
}

fn := fmt.Sprintf("/tmp/certificate_%04d.json", signedCertificate.Height)
fn := fmt.Sprintf("%s/certificate_%04d-%07d.json",
a.cfg.SaveCertificatesToFilesPath, signedCertificate.Height, time.Now().Unix())
a.log.Infof("saving certificate to file: %s", fn)
jsonData, err := json.Marshal(signedCertificate)
jsonData, err := json.MarshalIndent(signedCertificate, "", " ")
if err != nil {
a.log.Errorf("error marshalling certificate: %w", err)
}
Expand All @@ -206,6 +209,27 @@ func (a *AggSender) saveCertificateToFile(signedCertificate *agglayer.SignedCert
}
}

// getNextHeightAndPreviousLER returns the height and previous LER for the new certificate
func (a *AggSender) getNextHeightAndPreviousLER(
lastSentCertificateInfo *aggsendertypes.CertificateInfo) (uint64, common.Hash) {
height := lastSentCertificateInfo.Height + 1
if lastSentCertificateInfo.Status == agglayer.InError {
// previous certificate was in error, so we need to resend it
a.log.Debugf("Last certificate %s failed so reusing height %d",
lastSentCertificateInfo.CertificateID, lastSentCertificateInfo.Height)
height = lastSentCertificateInfo.Height
}

previousLER := lastSentCertificateInfo.NewLocalExitRoot
if lastSentCertificateInfo.NewLocalExitRoot == (common.Hash{}) {
// meaning this is the first certificate
height = 0
previousLER = zeroLER
}

return height, previousLER
}

// buildCertificate builds a certificate from the bridge events
func (a *AggSender) buildCertificate(ctx context.Context,
bridges []bridgesync.Bridge,
Expand All @@ -223,6 +247,7 @@ func (a *AggSender) buildCertificate(ctx context.Context,
}

var depositCount uint32

if len(bridges) > 0 {
depositCount = bridges[len(bridges)-1].DepositCount
}
Expand All @@ -232,13 +257,7 @@ func (a *AggSender) buildCertificate(ctx context.Context,
return nil, fmt.Errorf("error getting exit root by index: %d. Error: %w", depositCount, err)
}

height := lastSentCertificateInfo.Height + 1
previousLER := lastSentCertificateInfo.NewLocalExitRoot
if lastSentCertificateInfo.NewLocalExitRoot == (common.Hash{}) {
// meaning this is the first certificate
height = 0
previousLER = zeroLER
}
height, previousLER := a.getNextHeightAndPreviousLER(&lastSentCertificateInfo)

return &agglayer.Certificate{
NetworkID: a.l2Syncer.OriginNetwork(),
Expand Down Expand Up @@ -312,7 +331,7 @@ func (a *AggSender) getImportedBridgeExits(
) ([]*agglayer.ImportedBridgeExit, error) {
if len(claims) == 0 {
// no claims to convert
return nil, nil
return []*agglayer.ImportedBridgeExit{}, nil
}

var (
Expand Down Expand Up @@ -459,9 +478,10 @@ func (a *AggSender) checkIfCertificatesAreSettled(ctx context.Context) {
// checkPendingCertificatesStatus checks the status of pending certificates
// and updates in the storage if it changed on agglayer
func (a *AggSender) checkPendingCertificatesStatus(ctx context.Context) {
pendingCertificates, err := a.storage.GetCertificatesByStatus(ctx, []agglayer.CertificateStatus{agglayer.Pending})
pendingCertificates, err := a.storage.GetCertificatesByStatus(nonSettledStatuses)
if err != nil {
a.log.Errorf("error getting pending certificates: %w", err)
return
}

for _, certificate := range pendingCertificates {
Expand All @@ -472,7 +492,7 @@ func (a *AggSender) checkPendingCertificatesStatus(ctx context.Context) {
continue
}

if certificateHeader.Status != agglayer.Pending {
if certificateHeader.Status != certificate.Status {
certificate.Status = certificateHeader.Status

a.log.Infof("certificate %s changed status to %s", certificateHeader.String(), certificate.Status)
Expand All @@ -487,8 +507,8 @@ func (a *AggSender) checkPendingCertificatesStatus(ctx context.Context) {

// shouldSendCertificate checks if a certificate should be sent at given time
// if we have pending certificates, then we wait until they are settled
func (a *AggSender) shouldSendCertificate(ctx context.Context) (bool, error) {
pendingCertificates, err := a.storage.GetCertificatesByStatus(ctx, []agglayer.CertificateStatus{agglayer.Pending})
func (a *AggSender) shouldSendCertificate() (bool, error) {
pendingCertificates, err := a.storage.GetCertificatesByStatus(nonSettledStatuses)
if err != nil {
return false, fmt.Errorf("error getting pending certificates: %w", err)
}
Expand Down
Loading
Loading