From c26a5ac54a4aeeb7cec72ab0ac569d201f71afe4 Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:28:20 +0100 Subject: [PATCH 01/27] fix: certificate with no importedBridges set '[]' instead of 'null' --- agglayer/types.go | 75 ++++++++++++++++++++++++++++++++++++++++++ agglayer/types_test.go | 15 +++++++++ 2 files changed, 90 insertions(+) diff --git a/agglayer/types.go b/agglayer/types.go index 825c9db23..f5097bea1 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "math/big" + "reflect" "strings" "github.com/0xPolygon/cdk/bridgesync" @@ -125,12 +126,86 @@ func (c *Certificate) HashToSign() common.Hash { ) } +func duplicateToAnonymousStruct(src interface{}) interface{} { + srcVal := reflect.ValueOf(src) + srcType := srcVal.Type() + + var structFields []reflect.StructField + for i := 0; i < srcType.NumField(); i++ { + field := srcType.Field(i) + structFields = append(structFields, field) + } + + anonymousStructType := reflect.StructOf(structFields) + + anonymousStruct := reflect.New(anonymousStructType).Elem() + + for i := 0; i < srcVal.NumField(); i++ { + anonymousStruct.Field(i).Set(srcVal.Field(i)) + } + + return anonymousStruct.Interface() +} + +func (c *Certificate) MarshalJSON() ([]byte, error) { + if c.BridgeExits == nil { + c.BridgeExits = make([]*BridgeExit, 0) + } + if c.ImportedBridgeExits == nil { + c.ImportedBridgeExits = make([]*ImportedBridgeExit, 0) + } + return json.Marshal(&struct { + NetworkID uint32 `json:"network_id"` + Height uint64 `json:"height"` + PrevLocalExitRoot [32]byte `json:"prev_local_exit_root"` + NewLocalExitRoot [32]byte `json:"new_local_exit_root"` + BridgeExits []*BridgeExit `json:"bridge_exits"` + ImportedBridgeExits []*ImportedBridgeExit `json:"imported_bridge_exits"` + Metadata common.Hash `json:"metadata"` + }{ + NetworkID: c.NetworkID, + Height: c.Height, + PrevLocalExitRoot: c.PrevLocalExitRoot, + NewLocalExitRoot: c.NewLocalExitRoot, + BridgeExits: c.BridgeExits, + ImportedBridgeExits: c.ImportedBridgeExits, + Metadata: c.Metadata, + }) +} + // SignedCertificate is the struct that contains the certificate and the signature of the signer type SignedCertificate struct { *Certificate Signature *Signature `json:"signature"` } +func (c SignedCertificate) MarshalJSON() ([]byte, error) { + + if c.Signature == nil { + c.Signature = &Signature{} + } + + return json.Marshal(&struct { + NetworkID uint32 `json:"network_id"` + Height uint64 `json:"height"` + PrevLocalExitRoot [32]byte `json:"prev_local_exit_root"` + NewLocalExitRoot [32]byte `json:"new_local_exit_root"` + BridgeExits []*BridgeExit `json:"bridge_exits"` + ImportedBridgeExits []*ImportedBridgeExit `json:"imported_bridge_exits"` + Metadata common.Hash `json:"metadata"` + Signature *Signature `json:"signature"` + }{ + NetworkID: c.NetworkID, + Height: c.Height, + PrevLocalExitRoot: c.PrevLocalExitRoot, + NewLocalExitRoot: c.NewLocalExitRoot, + BridgeExits: c.BridgeExits, + ImportedBridgeExits: c.ImportedBridgeExits, + Metadata: c.Metadata, + Signature: c.Signature, + }) +} + // Signature is the data structure that will hold the signature of the given certificate type Signature struct { R common.Hash `json:"r"` diff --git a/agglayer/types_test.go b/agglayer/types_test.go index 325c0b882..2f4e2c628 100644 --- a/agglayer/types_test.go +++ b/agglayer/types_test.go @@ -13,8 +13,23 @@ import ( const ( expectedSignedCertificateEmptyMetadataJSON = `{"network_id":1,"height":1,"prev_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"new_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"bridge_exits":[{"leaf_type":"Transfer","token_info":null,"dest_network":0,"dest_address":"0x0000000000000000000000000000000000000000","amount":"1","metadata":[]}],"imported_bridge_exits":[{"bridge_exit":{"leaf_type":"Transfer","token_info":null,"dest_network":0,"dest_address":"0x0000000000000000000000000000000000000000","amount":"1","metadata":[]},"claim_data":null,"global_index":{"mainnet_flag":false,"rollup_index":1,"leaf_index":1}}],"metadata":"0x0000000000000000000000000000000000000000000000000000000000000000","signature":{"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000","odd_y_parity":false}}` expectedSignedCertificateyMetadataJSON = `{"network_id":1,"height":1,"prev_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"new_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"bridge_exits":[{"leaf_type":"Transfer","token_info":null,"dest_network":0,"dest_address":"0x0000000000000000000000000000000000000000","amount":"1","metadata":[1,2,3]}],"imported_bridge_exits":[{"bridge_exit":{"leaf_type":"Transfer","token_info":null,"dest_network":0,"dest_address":"0x0000000000000000000000000000000000000000","amount":"1","metadata":[]},"claim_data":null,"global_index":{"mainnet_flag":false,"rollup_index":1,"leaf_index":1}}],"metadata":"0x0000000000000000000000000000000000000000000000000000000000000000","signature":{"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000","odd_y_parity":false}}` + expectedCertificateJSONEmptyFields = `{"network_id":0,"height":0,"prev_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"new_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"bridge_exits":null,"imported_bridge_exits":null,"metadata":"0x0000000000000000000000000000000000000000000000000000000000000000","signature":{"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000","odd_y_parity":false}}` ) +func TestMarshalJSONEmptyFields(t *testing.T) { + cert := &SignedCertificate{ + Certificate: &Certificate{ + BridgeExits: nil, + ImportedBridgeExits: nil, + }, + Signature: &Signature{}, + } + data, err := json.Marshal(cert) + require.NoError(t, err) + log.Info(string(data)) + require.Equal(t, expectedCertificateJSONEmptyFields, string(data)) +} + func TestMarshalJSON(t *testing.T) { cert := SignedCertificate{ Certificate: &Certificate{ From 2e1e18f2b938a7b8611c9fd03af31d8e9758ee0a Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:42:16 +0100 Subject: [PATCH 02/27] fix: certificate with no importedBridges set '[]' instead of 'null' --- agglayer/types.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/agglayer/types.go b/agglayer/types.go index f5097bea1..25d8bc06b 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -147,7 +147,7 @@ func duplicateToAnonymousStruct(src interface{}) interface{} { return anonymousStruct.Interface() } -func (c *Certificate) MarshalJSON() ([]byte, error) { +func (c Certificate) MarshalJSON() ([]byte, error) { if c.BridgeExits == nil { c.BridgeExits = make([]*BridgeExit, 0) } @@ -184,6 +184,12 @@ func (c SignedCertificate) MarshalJSON() ([]byte, error) { if c.Signature == nil { c.Signature = &Signature{} } + if c.BridgeExits == nil { + c.BridgeExits = make([]*BridgeExit, 0) + } + if c.ImportedBridgeExits == nil { + c.ImportedBridgeExits = make([]*ImportedBridgeExit, 0) + } return json.Marshal(&struct { NetworkID uint32 `json:"network_id"` From 77d12ddb0a4bb9263e36a19649ffa87a05002b5d Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:50:35 +0100 Subject: [PATCH 03/27] feat: adapt to kurtosis-cdk pp --- scripts/local_config | 97 ++++++++++++++++++++++++++++++++++++-------- test/scripts/env.sh | 2 +- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/scripts/local_config b/scripts/local_config index d1a47b2c2..74608cb59 100755 --- a/scripts/local_config +++ b/scripts/local_config @@ -29,6 +29,10 @@ function get_value_from_toml_file(){ local _KEY="$3" local _LINE local _inside_section=0 + if [ $_SECTION == "." ]; then + _SECTION="" + _inside_section=1 + fi local _return_next_line=0 local _TMP_FILE=$(mktemp) cat $_FILE > $_TMP_FILE @@ -72,20 +76,45 @@ function get_value_from_toml_file(){ } ############################################################################### function export_key_from_toml_file_or_fatal(){ + export_key_from_toml_file "$1" "$2" "$3" "$4" + if [ $? -ne 0 ]; then + local _EXPORTED_VAR_NAME="$1" + local _FILE="$2" + local _SECTION="$3" + local _KEY="$4" + log_fatal "$FUNCNAME: key [$_KEY] not found in section [$_SECTION] in file [$_FILE]" + fi +} + +############################################################################### +function export_key_from_toml_file(){ local _EXPORTED_VAR_NAME="$1" local _FILE="$2" local _SECTION="$3" local _KEY="$4" local _VALUE=$(get_value_from_toml_file $_FILE $_SECTION $_KEY) if [ -z "$_VALUE" ]; then - log_fatal "$FUNCNAME: key $_KEY not found in section $_SECTION in file $_FILE" + log_debug "$FUNCNAME: key [$_KEY] not found in section [$_SECTION] in file [$_FILE]" + return 1 fi export $_EXPORTED_VAR_NAME="$_VALUE" log_debug "$_EXPORTED_VAR_NAME=${!_EXPORTED_VAR_NAME} \t\t\t# file:$_FILE section:$_SECTION key:$_KEY" + return 0 } - ############################################################################### function export_obj_key_from_toml_file_or_fatal(){ + export_obj_key_from_toml_file $* + if [ $? -ne 0 ]; then + local _EXPORTED_VAR_NAME="$1" + local _FILE="$2" + local _SECTION="$3" + local _KEY="$4" + log_fatal "$FUNCNAME: obj_key [$_KEY] not found in section [$_SECTION] in file [$_FILE]" + fi +} + +############################################################################### +function export_obj_key_from_toml_file(){ local _EXPORTED_VAR_NAME="$1" local _FILE="$2" local _SECTION="$3" @@ -94,7 +123,8 @@ function export_obj_key_from_toml_file_or_fatal(){ log_debug "export_obj_key_from_toml_file_or_fatal: $_EXPORTED_VAR_NAME $_FILE $_SECTION $_KEY $_OBJ_KEY" local _VALUE=$(get_value_from_toml_file $_FILE $_SECTION $_KEY) if [ -z "$_VALUE" ]; then - log_fatal "export_obj_key_from_toml_file_or_fatal: obj_key $_KEY not found in section [$_SECTION]" + log_error "export_obj_key_from_toml_file_or_fatal: obj_key $_KEY not found in section [$_SECTION]" + return 1 fi local _CLEAN_VALUE=$(echo $_VALUE | tr -d '{' | tr -d '}' | tr ',' '\n') while read -r _LINE; do @@ -113,7 +143,8 @@ function export_obj_key_from_toml_file_or_fatal(){ return 0 fi done <<< "$_CLEAN_VALUE" - log_fatal "export_obj_key_from_toml_file_or_fatal: obj_key $_OBJ_KEY not found in section $_SECTION/ $_KEY = $_VALUE" + log_error "export_obj_key_from_toml_file_or_fatal: obj_key $_OBJ_KEY not found in section $_SECTION/ $_KEY = $_VALUE" + return 1 } ############################################################################### @@ -133,23 +164,55 @@ function export_values_of_genesis(){ ############################################################################### function export_values_of_cdk_node_config(){ local _CDK_CONFIG_FILE=$1 - export_key_from_toml_file_or_fatal zkevm_l2_sequencer_address $_CDK_CONFIG_FILE SequenceSender L2Coinbase - export_obj_key_from_toml_file_or_fatal zkevm_l2_sequencer_keystore_password $_CDK_CONFIG_FILE SequenceSender PrivateKey Password - export_key_from_toml_file_or_fatal l1_chain_id $_CDK_CONFIG_FILE SequenceSender.EthTxManager.Etherman L1ChainID - export_key_from_toml_file_or_fatal zkevm_is_validium $_CDK_CONFIG_FILE Common IsValidiumMode - export_key_from_toml_file_or_fatal zkevm_contract_versions $_CDK_CONFIG_FILE Common ContractVersions - export_key_from_toml_file_or_fatal l2_chain_id $_CDK_CONFIG_FILE Aggregator ChainID + export_key_from_toml_file zkevm_l2_sequencer_address $_CDK_CONFIG_FILE SequenceSender L2Coinbase + if [ $? -ne 0 ]; then + export_key_from_toml_file_or_fatal zkevm_l2_sequencer_address $_CDK_CONFIG_FILE "." L2Coinbase + fi + export_obj_key_from_toml_file zkevm_l2_sequencer_keystore_password $_CDK_CONFIG_FILE SequenceSender PrivateKey Password + if [ $? -ne 0 ]; then + export_key_from_toml_file_or_fatal zkevm_l2_sequencer_keystore_password $_CDK_CONFIG_FILE "." SequencerPrivateKeyPassword + fi + export_key_from_toml_file l1_chain_id $_CDK_CONFIG_FILE SequenceSender.EthTxManager.Etherman L1ChainID + if [ $? -ne 0 ]; then + export_key_from_toml_file_or_fatal l1_chain_id $_CDK_CONFIG_FILE L1Config chainId + fi + export_key_from_toml_file zkevm_is_validium $_CDK_CONFIG_FILE Common IsValidiumMode + if [ $? -ne 0 ]; then + export_key_from_toml_file_or_fatal zkevm_is_validium $_CDK_CONFIG_FILE "." IsValidiumMode + fi + export_key_from_toml_file zkevm_contract_versions $_CDK_CONFIG_FILE Common ContractVersions + if [ $? -ne 0 ]; then + export_key_from_toml_file_or_fatal zkevm_contract_versions $_CDK_CONFIG_FILE "." ContractVersions + fi + export_key_from_toml_file l2_chain_id $_CDK_CONFIG_FILE Aggregator ChainID + if [ $? -ne 0 ]; then + log_debug "l2_chain_id not found in Aggregator section, using 0" + export l2_chain_id="0" + fi export_key_from_toml_file_or_fatal zkevm_aggregator_port $_CDK_CONFIG_FILE Aggregator Port - export_key_from_toml_file_or_fatal zkevm_l2_agglayer_address $_CDK_CONFIG_FILE Aggregator SenderAddress + export_key_from_toml_file zkevm_l2_agglayer_address $_CDK_CONFIG_FILE Aggregator SenderAddress + if [ $? -ne 0 ]; then + export_key_from_toml_file zkevm_l2_agglayer_address $_CDK_CONFIG_FILE "." SenderProofToL1Addr + fi export_key_from_toml_file_or_fatal aggregator_db_name $_CDK_CONFIG_FILE Aggregator.DB Name export_key_from_toml_file_or_fatal aggregator_db_user $_CDK_CONFIG_FILE Aggregator.DB User export_key_from_toml_file_or_fatal aggregator_db_password $_CDK_CONFIG_FILE Aggregator.DB Password - export_obj_key_from_toml_file_or_fatal zkevm_l2_aggregator_keystore_password $_CDK_CONFIG_FILE Aggregator.EthTxManager PrivateKeys Password - - export_key_from_toml_file_or_fatal zkevm_rollup_fork_id $_CDK_CONFIG_FILE Aggregator ForkId - export_key_from_toml_file_or_fatal zkevm_l2_agglayer_keystore_password $_CDK_CONFIG_FILE AggSender.SequencerPrivateKey Password - export_key_from_toml_file_or_fatal zkevm_bridge_address $_CDK_CONFIG_FILE BridgeL1Sync BridgeAddr - + export_obj_key_from_toml_file zkevm_l2_aggregator_keystore_password $_CDK_CONFIG_FILE Aggregator.EthTxManager PrivateKeys Password + if [ $? -ne 0 ]; then + export_key_from_toml_file zkevm_l2_aggregator_keystore_password $_CDK_CONFIG_FILE "." AggregatorPrivateKeyPassword + fi + export_key_from_toml_file zkevm_rollup_fork_id $_CDK_CONFIG_FILE Aggregator ForkId + if [ $? -ne 0 ]; then + export_key_from_toml_file_or_fatal zkevm_rollup_fork_id $_CDK_CONFIG_FILE "." ForkId + fi + export_key_from_toml_file zkevm_l2_agglayer_keystore_password $_CDK_CONFIG_FILE AggSender.SequencerPrivateKey Password + if [ $? -ne 0 ]; then + export_key_from_toml_file_or_fatal zkevm_l2_agglayer_keystore_password $_CDK_CONFIG_FILE "." SequencerPrivateKeyPassword + fi + export_key_from_toml_file zkevm_bridge_address $_CDK_CONFIG_FILE BridgeL1Sync BridgeAddr + if [ $? -ne 0 ]; then + export_key_from_toml_file_or_fatal zkevm_bridge_address $_CDK_CONFIG_FILE "." polygonBridgeAddr + fi export is_cdk_validium=$zkevm_is_validium export zkevm_rollup_chain_id=$l2_chain_id diff --git a/test/scripts/env.sh b/test/scripts/env.sh index 063b7d61a..298d4f73d 100644 --- a/test/scripts/env.sh +++ b/test/scripts/env.sh @@ -3,5 +3,5 @@ KURTOSIS_ENCLAVE=cdk TMP_CDK_FOLDER=tmp/cdk DEST_KURTOSIS_PARAMS_YML=../$TMP_CDK_FOLDER/e2e-params.yml -KURTOSIS_FOLDER=../kurtosis-cdk +KURTOSIS_FOLDER=${KURTOSIS_FOLDER:=../kurtosis-cdk} USE_L1_GAS_TOKEN_CONTRACT=true From 6d775da5dad55990f534e0190a89613ae64f5ccf Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:08:11 +0100 Subject: [PATCH 04/27] feat: change para SaveCertificatesToFiles to SaveCertificatesToFilesPath --- aggsender/aggsender.go | 27 +++++++++++++++++---------- aggsender/config.go | 4 ++-- scripts/local_config | 17 +++++++++++++++-- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index f1df20ffc..f3d7b73c3 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -190,13 +190,12 @@ func (a *AggSender) sendCertificate(ctx context.Context) error { // 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) } @@ -223,6 +222,7 @@ func (a *AggSender) buildCertificate(ctx context.Context, } var depositCount uint32 + if len(bridges) > 0 { depositCount = bridges[len(bridges)-1].DepositCount } @@ -231,8 +231,12 @@ func (a *AggSender) buildCertificate(ctx context.Context, if err != nil { return nil, fmt.Errorf("error getting exit root by index: %d. Error: %w", depositCount, err) } - height := lastSentCertificateInfo.Height + 1 + if lastSentCertificateInfo.Status == agglayer.InError { + a.log.Debugf("Last certificate %s fails 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 @@ -463,15 +467,18 @@ func (a *AggSender) checkPendingCertificatesStatus(ctx context.Context) { if err != nil { a.log.Errorf("error getting pending certificates: %w", err) } - - for _, certificate := range pendingCertificates { + totalPendiningCertificates := len(pendingCertificates) + for i, certificate := range pendingCertificates { + a.log.Debugf("checking status of certificate[%d/%d] height:%d id:%s ", + i, totalPendiningCertificates, certificate.Height, certificate.CertificateID) certificateHeader, err := a.aggLayerClient.GetCertificateHeader(certificate.CertificateID) if err != nil { - a.log.Errorf("error getting header of certificate %s with height: %d from agglayer: %w", - certificate.CertificateID, certificate.Height, err) + a.log.Errorf("error getting header of certificate [%d/%d] height:%d id:%s from agglayer: %w", + i, totalPendiningCertificates, certificate.Height, certificate.CertificateID, err) continue } - + a.log.Infof("checking status of certificate[%d/%d] height:%d id:%s = %s ", + i, totalPendiningCertificates, certificate.Height, certificate.CertificateID, certificateHeader.Status) if certificateHeader.Status != agglayer.Pending { certificate.Status = certificateHeader.Status diff --git a/aggsender/config.go b/aggsender/config.go index 506b4e9af..60aae7e81 100644 --- a/aggsender/config.go +++ b/aggsender/config.go @@ -18,6 +18,6 @@ type Config struct { AggsenderPrivateKey types.KeystoreFileConfig `mapstructure:"AggsenderPrivateKey"` // URLRPCL2 is the URL of the L2 RPC node URLRPCL2 string `mapstructure:"URLRPCL2"` - // SaveCertificatesToFiles is a flag which tells the AggSender to save the certificates to a file - SaveCertificatesToFiles bool `mapstructure:"SaveCertificatesToFiles"` + // SaveCertificatesToFilesPath if != "" tells the AggSender to save the certificates to a file in this path + SaveCertificatesToFilesPath string `mapstructure:"SaveCertificatesToFilesPath"` } diff --git a/scripts/local_config b/scripts/local_config index 74608cb59..a134220ec 100755 --- a/scripts/local_config +++ b/scripts/local_config @@ -344,6 +344,18 @@ function download_kurtosis_artifacts(){ } ############################################################################### +function add_translation_rules_for_validium(){ + if [ $is_cdk_validium != "true" ]; then + return + fi + log_debug " For Validium mode, we need to reach the DAC SERVER: adding translation rules" + + echo "[Aggregator.Synchronizer.Etherman.Validium.Translator]" + echo "FullMatchRules = [" + echo " {Old=\"http://zkevm-dac-001:8484\", New=\"http://127.0.0.1:${dac_port}\"}," + echo " ]" +} +############################################################################### function check_generated_config_file(){ grep "" $DEST_TEMPLATE_FILE > /dev/null if [ $? -ne 1 ]; then @@ -400,15 +412,16 @@ ok_or_fatal "Error generating template" check_generated_config_file +add_translation_rules_for_validium echo " " echo "file generated at:" $DEST/test.kurtosis.toml echo "- to restart kurtosis:" -echo " kurtosis clean --all; kurtosis run --enclave cdk-v1 --args-file params.yml --image-download always ." +echo " kurtosis clean --all; kurtosis run --enclave cdk --args-file params.yml --image-download always ." echo " " echo "- Stop cdk-node:" -echo " kurtosis service stop cdk-v1 cdk-node-001" +echo " kurtosis service stop cdk cdk-node-001" echo " " echo "- Add next configuration to vscode launch.json" echo " -----------------------------------------------------------" From 210ef4b0cd26e0b8b1a59ba78d61fa7f77e01570 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Wed, 6 Nov 2024 16:19:49 +0100 Subject: [PATCH 05/27] fix: get candidate and proven certificates as well --- agglayer/types.go | 81 ------------------------------------- aggsender/aggsender.go | 52 ++++++++++++++---------- aggsender/aggsender_test.go | 62 +++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 105 deletions(-) diff --git a/agglayer/types.go b/agglayer/types.go index 25d8bc06b..825c9db23 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "math/big" - "reflect" "strings" "github.com/0xPolygon/cdk/bridgesync" @@ -126,92 +125,12 @@ func (c *Certificate) HashToSign() common.Hash { ) } -func duplicateToAnonymousStruct(src interface{}) interface{} { - srcVal := reflect.ValueOf(src) - srcType := srcVal.Type() - - var structFields []reflect.StructField - for i := 0; i < srcType.NumField(); i++ { - field := srcType.Field(i) - structFields = append(structFields, field) - } - - anonymousStructType := reflect.StructOf(structFields) - - anonymousStruct := reflect.New(anonymousStructType).Elem() - - for i := 0; i < srcVal.NumField(); i++ { - anonymousStruct.Field(i).Set(srcVal.Field(i)) - } - - return anonymousStruct.Interface() -} - -func (c Certificate) MarshalJSON() ([]byte, error) { - if c.BridgeExits == nil { - c.BridgeExits = make([]*BridgeExit, 0) - } - if c.ImportedBridgeExits == nil { - c.ImportedBridgeExits = make([]*ImportedBridgeExit, 0) - } - return json.Marshal(&struct { - NetworkID uint32 `json:"network_id"` - Height uint64 `json:"height"` - PrevLocalExitRoot [32]byte `json:"prev_local_exit_root"` - NewLocalExitRoot [32]byte `json:"new_local_exit_root"` - BridgeExits []*BridgeExit `json:"bridge_exits"` - ImportedBridgeExits []*ImportedBridgeExit `json:"imported_bridge_exits"` - Metadata common.Hash `json:"metadata"` - }{ - NetworkID: c.NetworkID, - Height: c.Height, - PrevLocalExitRoot: c.PrevLocalExitRoot, - NewLocalExitRoot: c.NewLocalExitRoot, - BridgeExits: c.BridgeExits, - ImportedBridgeExits: c.ImportedBridgeExits, - Metadata: c.Metadata, - }) -} - // SignedCertificate is the struct that contains the certificate and the signature of the signer type SignedCertificate struct { *Certificate Signature *Signature `json:"signature"` } -func (c SignedCertificate) MarshalJSON() ([]byte, error) { - - if c.Signature == nil { - c.Signature = &Signature{} - } - if c.BridgeExits == nil { - c.BridgeExits = make([]*BridgeExit, 0) - } - if c.ImportedBridgeExits == nil { - c.ImportedBridgeExits = make([]*ImportedBridgeExit, 0) - } - - return json.Marshal(&struct { - NetworkID uint32 `json:"network_id"` - Height uint64 `json:"height"` - PrevLocalExitRoot [32]byte `json:"prev_local_exit_root"` - NewLocalExitRoot [32]byte `json:"new_local_exit_root"` - BridgeExits []*BridgeExit `json:"bridge_exits"` - ImportedBridgeExits []*ImportedBridgeExit `json:"imported_bridge_exits"` - Metadata common.Hash `json:"metadata"` - Signature *Signature `json:"signature"` - }{ - NetworkID: c.NetworkID, - Height: c.Height, - PrevLocalExitRoot: c.PrevLocalExitRoot, - NewLocalExitRoot: c.NewLocalExitRoot, - BridgeExits: c.BridgeExits, - ImportedBridgeExits: c.ImportedBridgeExits, - Metadata: c.Metadata, - Signature: c.Signature, - }) -} - // Signature is the data structure that will hold the signature of the given certificate type Signature struct { R common.Hash `json:"r"` diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index f3d7b73c3..3e3644459 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -205,6 +205,25 @@ 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, @@ -231,18 +250,8 @@ func (a *AggSender) buildCertificate(ctx context.Context, if err != nil { return nil, fmt.Errorf("error getting exit root by index: %d. Error: %w", depositCount, err) } - height := lastSentCertificateInfo.Height + 1 - if lastSentCertificateInfo.Status == agglayer.InError { - a.log.Debugf("Last certificate %s fails 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 - } + height, previousLER := a.getNextHeightAndPreviousLER(&lastSentCertificateInfo) return &agglayer.Certificate{ NetworkID: a.l2Syncer.OriginNetwork(), @@ -316,7 +325,7 @@ func (a *AggSender) getImportedBridgeExits( ) ([]*agglayer.ImportedBridgeExit, error) { if len(claims) == 0 { // no claims to convert - return nil, nil + return []*agglayer.ImportedBridgeExit{}, nil } var ( @@ -463,23 +472,22 @@ 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(ctx, []agglayer.CertificateStatus{ + agglayer.Pending, agglayer.Proven, agglayer.Candidate}) if err != nil { a.log.Errorf("error getting pending certificates: %w", err) + return } - totalPendiningCertificates := len(pendingCertificates) - for i, certificate := range pendingCertificates { - a.log.Debugf("checking status of certificate[%d/%d] height:%d id:%s ", - i, totalPendiningCertificates, certificate.Height, certificate.CertificateID) + + for _, certificate := range pendingCertificates { certificateHeader, err := a.aggLayerClient.GetCertificateHeader(certificate.CertificateID) if err != nil { - a.log.Errorf("error getting header of certificate [%d/%d] height:%d id:%s from agglayer: %w", - i, totalPendiningCertificates, certificate.Height, certificate.CertificateID, err) + a.log.Errorf("error getting header of certificate %s with height: %d from agglayer: %w", + certificate.CertificateID, certificate.Height, err) continue } - a.log.Infof("checking status of certificate[%d/%d] height:%d id:%s = %s ", - i, totalPendiningCertificates, certificate.Height, certificate.CertificateID, certificateHeader.Status) - 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) diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index 718786797..ef6ce9cde 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -456,7 +456,7 @@ func TestGetImportedBridgeExits(t *testing.T) { name: "No claims", claims: []bridgesync.Claim{}, expectedError: false, - expectedExits: nil, + expectedExits: []*agglayer.ImportedBridgeExit{}, }, } @@ -803,7 +803,9 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { mockAggLayerClient := agglayer.NewAgglayerClientMock(t) mockLogger := mocks.NewLoggerMock(t) - mockStorage.On("GetCertificatesByStatus", mock.Anything, []agglayer.CertificateStatus{agglayer.Pending}).Return(tt.pendingCertificates, tt.getFromDBError) + mockStorage.On("GetCertificatesByStatus", mock.Anything, []agglayer.CertificateStatus{ + agglayer.Pending, agglayer.Proven, agglayer.Candidate}).Return( + tt.pendingCertificates, tt.getFromDBError) for certID, header := range tt.certificateHeaders { mockAggLayerClient.On("GetCertificateHeader", certID).Return(header, tt.clientError) } @@ -1408,3 +1410,59 @@ func TestExploratoryGenerateCert(t *testing.T) { encoder.SetIndent("", " ") require.NoError(t, encoder.Encode(certificate)) } + +func TestGetNextHeightAndPreviousLER(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + lastSentCertificateInfo aggsendertypes.CertificateInfo + expectedHeight uint64 + expectedPreviousLER common.Hash + }{ + { + name: "Normal case", + lastSentCertificateInfo: aggsendertypes.CertificateInfo{ + Height: 10, + NewLocalExitRoot: common.HexToHash("0x123"), + Status: agglayer.Settled, + }, + expectedHeight: 11, + expectedPreviousLER: common.HexToHash("0x123"), + }, + { + name: "Previous certificate in error", + lastSentCertificateInfo: aggsendertypes.CertificateInfo{ + Height: 10, + NewLocalExitRoot: common.HexToHash("0x123"), + Status: agglayer.InError, + }, + expectedHeight: 10, + expectedPreviousLER: common.HexToHash("0x123"), + }, + { + name: "First certificate", + lastSentCertificateInfo: aggsendertypes.CertificateInfo{ + Height: 0, + NewLocalExitRoot: common.Hash{}, + Status: agglayer.Settled, + }, + expectedHeight: 0, + expectedPreviousLER: zeroLER, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + aggSender := &AggSender{log: log.WithFields("aggsender-test", "getNextHeightAndPreviousLER")} + height, previousLER := aggSender.getNextHeightAndPreviousLER(&tt.lastSentCertificateInfo) + + require.Equal(t, tt.expectedHeight, height) + require.Equal(t, tt.expectedPreviousLER, previousLER) + }) + } +} From 73dfe95814e82f877c2e8d944dc75eaaf763fb68 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Wed, 6 Nov 2024 17:16:47 +0100 Subject: [PATCH 06/27] fix: remove test --- agglayer/types_test.go | 15 --------------- aggsender/aggsender.go | 9 ++++++--- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/agglayer/types_test.go b/agglayer/types_test.go index 2f4e2c628..325c0b882 100644 --- a/agglayer/types_test.go +++ b/agglayer/types_test.go @@ -13,23 +13,8 @@ import ( const ( expectedSignedCertificateEmptyMetadataJSON = `{"network_id":1,"height":1,"prev_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"new_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"bridge_exits":[{"leaf_type":"Transfer","token_info":null,"dest_network":0,"dest_address":"0x0000000000000000000000000000000000000000","amount":"1","metadata":[]}],"imported_bridge_exits":[{"bridge_exit":{"leaf_type":"Transfer","token_info":null,"dest_network":0,"dest_address":"0x0000000000000000000000000000000000000000","amount":"1","metadata":[]},"claim_data":null,"global_index":{"mainnet_flag":false,"rollup_index":1,"leaf_index":1}}],"metadata":"0x0000000000000000000000000000000000000000000000000000000000000000","signature":{"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000","odd_y_parity":false}}` expectedSignedCertificateyMetadataJSON = `{"network_id":1,"height":1,"prev_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"new_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"bridge_exits":[{"leaf_type":"Transfer","token_info":null,"dest_network":0,"dest_address":"0x0000000000000000000000000000000000000000","amount":"1","metadata":[1,2,3]}],"imported_bridge_exits":[{"bridge_exit":{"leaf_type":"Transfer","token_info":null,"dest_network":0,"dest_address":"0x0000000000000000000000000000000000000000","amount":"1","metadata":[]},"claim_data":null,"global_index":{"mainnet_flag":false,"rollup_index":1,"leaf_index":1}}],"metadata":"0x0000000000000000000000000000000000000000000000000000000000000000","signature":{"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000","odd_y_parity":false}}` - expectedCertificateJSONEmptyFields = `{"network_id":0,"height":0,"prev_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"new_local_exit_root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"bridge_exits":null,"imported_bridge_exits":null,"metadata":"0x0000000000000000000000000000000000000000000000000000000000000000","signature":{"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000","odd_y_parity":false}}` ) -func TestMarshalJSONEmptyFields(t *testing.T) { - cert := &SignedCertificate{ - Certificate: &Certificate{ - BridgeExits: nil, - ImportedBridgeExits: nil, - }, - Signature: &Signature{}, - } - data, err := json.Marshal(cert) - require.NoError(t, err) - log.Info(string(data)) - require.Equal(t, expectedCertificateJSONEmptyFields, string(data)) -} - func TestMarshalJSON(t *testing.T) { cert := SignedCertificate{ Certificate: &Certificate{ diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index 3e3644459..620120752 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -193,7 +193,8 @@ func (a *AggSender) saveCertificateToFile(signedCertificate *agglayer.SignedCert if signedCertificate == nil || a.cfg.SaveCertificatesToFilesPath == "" { return } - fn := fmt.Sprintf("%s/certificate_%04d-%07d.json", a.cfg.SaveCertificatesToFilesPath, signedCertificate.Height, time.Now().Unix()) + 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.MarshalIndent(signedCertificate, "", " ") if err != nil { @@ -206,11 +207,13 @@ 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) { +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) + a.log.Debugf("Last certificate %s failed so reusing height %d", + lastSentCertificateInfo.CertificateID, lastSentCertificateInfo.Height) height = lastSentCertificateInfo.Height } From ef4fe29c9fbd754320598e2689058a5ebb5c5c26 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Thu, 7 Nov 2024 09:59:13 +0100 Subject: [PATCH 07/27] fix: small changes --- aggsender/aggsender.go | 10 ++++++---- aggsender/aggsender_test.go | 5 ++--- aggsender/config.go | 13 +++++++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index 620120752..5cb36c8c4 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -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 @@ -63,6 +64,8 @@ func New( return nil, err } + logger.Info(cfg.String()) + return &AggSender{ cfg: cfg, log: logger, @@ -475,8 +478,7 @@ 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, agglayer.Proven, agglayer.Candidate}) + pendingCertificates, err := a.storage.GetCertificatesByStatus(ctx, nonSettledStatuses) if err != nil { a.log.Errorf("error getting pending certificates: %w", err) return @@ -506,7 +508,7 @@ 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}) + pendingCertificates, err := a.storage.GetCertificatesByStatus(ctx, nonSettledStatuses) if err != nil { return false, fmt.Errorf("error getting pending certificates: %w", err) } diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index ef6ce9cde..7fcae39fa 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -803,8 +803,7 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { mockAggLayerClient := agglayer.NewAgglayerClientMock(t) mockLogger := mocks.NewLoggerMock(t) - mockStorage.On("GetCertificatesByStatus", mock.Anything, []agglayer.CertificateStatus{ - agglayer.Pending, agglayer.Proven, agglayer.Candidate}).Return( + mockStorage.On("GetCertificatesByStatus", mock.Anything, nonSettledStatuses).Return( tt.pendingCertificates, tt.getFromDBError) for certID, header := range tt.certificateHeaders { mockAggLayerClient.On("GetCertificateHeader", certID).Return(header, tt.clientError) @@ -895,7 +894,7 @@ func TestSendCertificate(t *testing.T) { if cfg.shouldSendCertificate != nil || cfg.getLastSentCertificate != nil || cfg.saveLastSentCertificate != nil { mockStorage = mocks.NewAggSenderStorageMock(t) - mockStorage.On("GetCertificatesByStatus", mock.Anything, []agglayer.CertificateStatus{agglayer.Pending}). + mockStorage.On("GetCertificatesByStatus", mock.Anything, nonSettledStatuses). Return(cfg.shouldSendCertificate...).Once() aggsender.storage = mockStorage diff --git a/aggsender/config.go b/aggsender/config.go index 60aae7e81..4ae07abb1 100644 --- a/aggsender/config.go +++ b/aggsender/config.go @@ -21,3 +21,16 @@ type Config struct { // SaveCertificatesToFilesPath if != "" tells the AggSender to save the certificates to a file in this path SaveCertificatesToFilesPath string `mapstructure:"SaveCertificatesToFilesPath"` } + +// String returns a string representation of the Config +func (c Config) String() string { + return "AggSender Config:\n" + + "StoragePath: " + c.StoragePath + "\n" + + "AggLayerURL: " + c.AggLayerURL + "\n" + + "BlockGetInterval: " + c.BlockGetInterval.String() + "\n" + + "CheckSettledInterval: " + c.CheckSettledInterval.String() + "\n" + + "AggsenderPrivateKeyPath: " + c.AggsenderPrivateKey.Path + "\n" + + "AggsenderPrivateKeyPassword: " + c.AggsenderPrivateKey.Password + "\n" + + "URLRPCL2: " + c.URLRPCL2 + "\n" + + "SaveCertificatesToFilesPath: " + c.SaveCertificatesToFilesPath + "\n" +} From 5e44136fef681bd47737c90a234017a7e088f9aa Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Thu, 7 Nov 2024 10:06:56 +0100 Subject: [PATCH 08/27] fix: db tx rollback --- aggsender/db/aggsender_db_storage.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/aggsender/db/aggsender_db_storage.go b/aggsender/db/aggsender_db_storage.go index 25b313928..400941a90 100644 --- a/aggsender/db/aggsender_db_storage.go +++ b/aggsender/db/aggsender_db_storage.go @@ -124,10 +124,10 @@ func (a *AggSenderSQLStorage) SaveLastSentCertificate(ctx context.Context, certi } }() - if err := meddler.Insert(tx, "certificate_info", &certificate); err != nil { + if err = meddler.Insert(tx, "certificate_info", &certificate); err != nil { return fmt.Errorf("error inserting certificate info: %w", err) } - if err := tx.Commit(); err != nil { + if err = tx.Commit(); err != nil { return err } @@ -150,10 +150,10 @@ func (a *AggSenderSQLStorage) DeleteCertificate(ctx context.Context, certificate } }() - if _, err := tx.Exec(`DELETE FROM certificate_info WHERE certificate_id = $1;`, certificateID); err != nil { + if _, err = tx.Exec(`DELETE FROM certificate_info WHERE certificate_id = $1;`, certificateID); err != nil { return fmt.Errorf("error deleting certificate info: %w", err) } - if err := tx.Commit(); err != nil { + if err = tx.Commit(); err != nil { return err } @@ -176,11 +176,11 @@ func (a *AggSenderSQLStorage) UpdateCertificateStatus(ctx context.Context, certi } }() - if _, err := tx.Exec(`UPDATE certificate_info SET status = $1 WHERE certificate_id = $2;`, + if _, err = tx.Exec(`UPDATE certificate_info SET status = $1 WHERE certificate_id = $2;`, certificate.Status, certificate.CertificateID); err != nil { return fmt.Errorf("error updating certificate info: %w", err) } - if err := tx.Commit(); err != nil { + if err = tx.Commit(); err != nil { return err } From 70aaaafae05d0b3611997cdfe4eb1348d0a81923 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Thu, 7 Nov 2024 11:19:42 +0100 Subject: [PATCH 09/27] fix: replace existing certificate --- aggsender/aggsender.go | 6 +- aggsender/aggsender_test.go | 6 +- aggsender/db/aggsender_db_storage.go | 47 ++++++++-- aggsender/db/aggsender_db_storage_test.go | 107 +++++++++++++++++++--- aggsender/mocks/mock_aggsender_storage.go | 87 +++++++++--------- 5 files changed, 181 insertions(+), 72 deletions(-) diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index 5cb36c8c4..b9f5f146b 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -119,7 +119,7 @@ func (a *AggSender) sendCertificate(ctx context.Context) error { return 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 } @@ -478,7 +478,7 @@ 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, nonSettledStatuses) + pendingCertificates, err := a.storage.GetCertificatesByStatus(nonSettledStatuses) if err != nil { a.log.Errorf("error getting pending certificates: %w", err) return @@ -508,7 +508,7 @@ 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, nonSettledStatuses) + pendingCertificates, err := a.storage.GetCertificatesByStatus(nonSettledStatuses) if err != nil { return false, fmt.Errorf("error getting pending certificates: %w", err) } diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index 7fcae39fa..d51969d00 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -803,7 +803,7 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { mockAggLayerClient := agglayer.NewAgglayerClientMock(t) mockLogger := mocks.NewLoggerMock(t) - mockStorage.On("GetCertificatesByStatus", mock.Anything, nonSettledStatuses).Return( + mockStorage.On("GetCertificatesByStatus", nonSettledStatuses).Return( tt.pendingCertificates, tt.getFromDBError) for certID, header := range tt.certificateHeaders { mockAggLayerClient.On("GetCertificateHeader", certID).Return(header, tt.clientError) @@ -894,13 +894,13 @@ func TestSendCertificate(t *testing.T) { if cfg.shouldSendCertificate != nil || cfg.getLastSentCertificate != nil || cfg.saveLastSentCertificate != nil { mockStorage = mocks.NewAggSenderStorageMock(t) - mockStorage.On("GetCertificatesByStatus", mock.Anything, nonSettledStatuses). + mockStorage.On("GetCertificatesByStatus", nonSettledStatuses). Return(cfg.shouldSendCertificate...).Once() aggsender.storage = mockStorage if cfg.getLastSentCertificate != nil { - mockStorage.On("GetLastSentCertificate", mock.Anything).Return(cfg.getLastSentCertificate...).Once() + mockStorage.On("GetLastSentCertificate").Return(cfg.getLastSentCertificate...).Once() } if cfg.saveLastSentCertificate != nil { diff --git a/aggsender/db/aggsender_db_storage.go b/aggsender/db/aggsender_db_storage.go index 400941a90..dc1f8b104 100644 --- a/aggsender/db/aggsender_db_storage.go +++ b/aggsender/db/aggsender_db_storage.go @@ -21,15 +21,15 @@ const errWhileRollbackFormat = "error while rolling back tx: %w" // AggSenderStorage is the interface that defines the methods to interact with the storage type AggSenderStorage interface { // GetCertificateByHeight returns a certificate by its height - GetCertificateByHeight(ctx context.Context, height uint64) (types.CertificateInfo, error) + GetCertificateByHeight(height uint64) (types.CertificateInfo, error) // GetLastSentCertificate returns the last certificate sent to the aggLayer - GetLastSentCertificate(ctx context.Context) (types.CertificateInfo, error) + GetLastSentCertificate() (types.CertificateInfo, error) // SaveLastSentCertificate saves the last certificate sent to the aggLayer SaveLastSentCertificate(ctx context.Context, certificate types.CertificateInfo) error // DeleteCertificate deletes a certificate from the storage DeleteCertificate(ctx context.Context, certificateID common.Hash) error // GetCertificatesByStatus returns a list of certificates by their status - GetCertificatesByStatus(ctx context.Context, status []agglayer.CertificateStatus) ([]*types.CertificateInfo, error) + GetCertificatesByStatus(status []agglayer.CertificateStatus) ([]*types.CertificateInfo, error) // UpdateCertificateStatus updates the status of a certificate UpdateCertificateStatus(ctx context.Context, certificate types.CertificateInfo) error } @@ -59,7 +59,7 @@ func NewAggSenderSQLStorage(logger *log.Logger, dbPath string) (*AggSenderSQLSto }, nil } -func (a *AggSenderSQLStorage) GetCertificatesByStatus(ctx context.Context, +func (a *AggSenderSQLStorage) GetCertificatesByStatus( statuses []agglayer.CertificateStatus) ([]*types.CertificateInfo, error) { query := "SELECT * FROM certificate_info" args := make([]interface{}, len(statuses)) @@ -88,10 +88,15 @@ func (a *AggSenderSQLStorage) GetCertificatesByStatus(ctx context.Context, } // GetCertificateByHeight returns a certificate by its height -func (a *AggSenderSQLStorage) GetCertificateByHeight(ctx context.Context, +func (a *AggSenderSQLStorage) GetCertificateByHeight(height uint64) (types.CertificateInfo, error) { + return getCertificateByHeight(a.db, height) +} + +// getCertificateByHeight returns a certificate by its height using the provided db +func getCertificateByHeight(db meddler.DB, height uint64) (types.CertificateInfo, error) { var certificateInfo types.CertificateInfo - if err := meddler.QueryRow(a.db, &certificateInfo, + if err := meddler.QueryRow(db, &certificateInfo, "SELECT * FROM certificate_info WHERE height = $1;", height); err != nil { return types.CertificateInfo{}, getSelectQueryError(height, err) } @@ -100,7 +105,7 @@ func (a *AggSenderSQLStorage) GetCertificateByHeight(ctx context.Context, } // GetLastSentCertificate returns the last certificate sent to the aggLayer -func (a *AggSenderSQLStorage) GetLastSentCertificate(ctx context.Context) (types.CertificateInfo, error) { +func (a *AggSenderSQLStorage) GetLastSentCertificate() (types.CertificateInfo, error) { var certificateInfo types.CertificateInfo if err := meddler.QueryRow(a.db, &certificateInfo, "SELECT * FROM certificate_info ORDER BY height DESC LIMIT 1;"); err != nil { @@ -124,9 +129,23 @@ func (a *AggSenderSQLStorage) SaveLastSentCertificate(ctx context.Context, certi } }() + cert, err := getCertificateByHeight(tx, certificate.Height) + if err != nil && !errors.Is(err, db.ErrNotFound) { + return err + } + + if cert.CertificateID != (common.Hash{}) { + // we already have a certificate with this height + // we need to delete it before inserting the new one + if err = deleteCertificate(tx, cert.CertificateID); err != nil { + return err + } + } + if err = meddler.Insert(tx, "certificate_info", &certificate); err != nil { return fmt.Errorf("error inserting certificate info: %w", err) } + if err = tx.Commit(); err != nil { return err } @@ -150,9 +169,10 @@ func (a *AggSenderSQLStorage) DeleteCertificate(ctx context.Context, certificate } }() - if _, err = tx.Exec(`DELETE FROM certificate_info WHERE certificate_id = $1;`, certificateID); err != nil { - return fmt.Errorf("error deleting certificate info: %w", err) + if err = deleteCertificate(a.db, certificateID); err != nil { + return err } + if err = tx.Commit(); err != nil { return err } @@ -162,6 +182,15 @@ func (a *AggSenderSQLStorage) DeleteCertificate(ctx context.Context, certificate return nil } +// deleteCertificate deletes a certificate from the storage using the provided db +func deleteCertificate(db meddler.DB, certificateID common.Hash) error { + if _, err := db.Exec(`DELETE FROM certificate_info WHERE certificate_id = $1;`, certificateID); err != nil { + return fmt.Errorf("error deleting certificate info: %w", err) + } + + return nil +} + // UpdateCertificateStatus updates the status of a certificate func (a *AggSenderSQLStorage) UpdateCertificateStatus(ctx context.Context, certificate types.CertificateInfo) error { tx, err := db.NewTx(ctx, a.db) diff --git a/aggsender/db/aggsender_db_storage_test.go b/aggsender/db/aggsender_db_storage_test.go index cfb7af7c2..6a656a95a 100644 --- a/aggsender/db/aggsender_db_storage_test.go +++ b/aggsender/db/aggsender_db_storage_test.go @@ -35,7 +35,7 @@ func Test_Storage(t *testing.T) { } require.NoError(t, storage.SaveLastSentCertificate(ctx, certificate)) - certificateFromDB, err := storage.GetCertificateByHeight(ctx, certificate.Height) + certificateFromDB, err := storage.GetCertificateByHeight(certificate.Height) require.NoError(t, err) require.Equal(t, certificate, certificateFromDB) @@ -55,7 +55,7 @@ func Test_Storage(t *testing.T) { require.NoError(t, storage.DeleteCertificate(ctx, certificate.CertificateID)) - certificateFromDB, err := storage.GetCertificateByHeight(ctx, certificate.Height) + certificateFromDB, err := storage.GetCertificateByHeight(certificate.Height) require.ErrorIs(t, err, db.ErrNotFound) require.Equal(t, types.CertificateInfo{}, certificateFromDB) require.NoError(t, storage.clean()) @@ -63,7 +63,7 @@ func Test_Storage(t *testing.T) { t.Run("GetLastSentCertificate", func(t *testing.T) { // try getting a certificate that doesn't exist - certificateFromDB, err := storage.GetLastSentCertificate(ctx) + certificateFromDB, err := storage.GetLastSentCertificate() require.NoError(t, err) require.Equal(t, types.CertificateInfo{}, certificateFromDB) @@ -78,7 +78,7 @@ func Test_Storage(t *testing.T) { } require.NoError(t, storage.SaveLastSentCertificate(ctx, certificate)) - certificateFromDB, err = storage.GetLastSentCertificate(ctx) + certificateFromDB, err = storage.GetLastSentCertificate() require.NoError(t, err) require.Equal(t, certificate, certificateFromDB) @@ -87,12 +87,12 @@ func Test_Storage(t *testing.T) { t.Run("GetCertificateByHeight", func(t *testing.T) { // try getting height 0 - certificateFromDB, err := storage.GetCertificateByHeight(ctx, 0) + certificateFromDB, err := storage.GetCertificateByHeight(0) require.NoError(t, err) require.Equal(t, types.CertificateInfo{}, certificateFromDB) // try getting a certificate that doesn't exist - certificateFromDB, err = storage.GetCertificateByHeight(ctx, 4) + certificateFromDB, err = storage.GetCertificateByHeight(4) require.ErrorIs(t, err, db.ErrNotFound) require.Equal(t, types.CertificateInfo{}, certificateFromDB) @@ -107,7 +107,7 @@ func Test_Storage(t *testing.T) { } require.NoError(t, storage.SaveLastSentCertificate(ctx, certificate)) - certificateFromDB, err = storage.GetCertificateByHeight(ctx, certificate.Height) + certificateFromDB, err = storage.GetCertificateByHeight(certificate.Height) require.NoError(t, err) require.Equal(t, certificate, certificateFromDB) @@ -149,28 +149,28 @@ func Test_Storage(t *testing.T) { // Test fetching certificates with status Settled statuses := []agglayer.CertificateStatus{agglayer.Settled} - certificatesFromDB, err := storage.GetCertificatesByStatus(ctx, statuses) + certificatesFromDB, err := storage.GetCertificatesByStatus(statuses) require.NoError(t, err) require.Len(t, certificatesFromDB, 1) require.ElementsMatch(t, []*types.CertificateInfo{certificates[0]}, certificatesFromDB) // Test fetching certificates with status Pending statuses = []agglayer.CertificateStatus{agglayer.Pending} - certificatesFromDB, err = storage.GetCertificatesByStatus(ctx, statuses) + certificatesFromDB, err = storage.GetCertificatesByStatus(statuses) require.NoError(t, err) require.Len(t, certificatesFromDB, 1) require.ElementsMatch(t, []*types.CertificateInfo{certificates[1]}, certificatesFromDB) // Test fetching certificates with status InError statuses = []agglayer.CertificateStatus{agglayer.InError} - certificatesFromDB, err = storage.GetCertificatesByStatus(ctx, statuses) + certificatesFromDB, err = storage.GetCertificatesByStatus(statuses) require.NoError(t, err) require.Len(t, certificatesFromDB, 1) require.ElementsMatch(t, []*types.CertificateInfo{certificates[2]}, certificatesFromDB) // Test fetching certificates with status InError and Pending statuses = []agglayer.CertificateStatus{agglayer.InError, agglayer.Pending} - certificatesFromDB, err = storage.GetCertificatesByStatus(ctx, statuses) + certificatesFromDB, err = storage.GetCertificatesByStatus(statuses) require.NoError(t, err) require.Len(t, certificatesFromDB, 2) require.ElementsMatch(t, []*types.CertificateInfo{certificates[1], certificates[2]}, certificatesFromDB) @@ -195,10 +195,93 @@ func Test_Storage(t *testing.T) { require.NoError(t, storage.UpdateCertificateStatus(ctx, certificate)) // Fetch the certificate and verify the status has been updated - certificateFromDB, err := storage.GetCertificateByHeight(ctx, certificate.Height) + certificateFromDB, err := storage.GetCertificateByHeight(certificate.Height) require.NoError(t, err) require.Equal(t, certificate.Status, certificateFromDB.Status) require.NoError(t, storage.clean()) }) } + +func Test_SaveLastSentCertificate(t *testing.T) { + ctx := context.Background() + + path := path.Join(t.TempDir(), "file::memory:?cache=shared") + log.Debugf("sqlite path: %s", path) + require.NoError(t, migrations.RunMigrations(path)) + + storage, err := NewAggSenderSQLStorage(log.WithFields("aggsender-db"), path) + require.NoError(t, err) + + t.Run("SaveNewCertificate", func(t *testing.T) { + certificate := types.CertificateInfo{ + Height: 1, + CertificateID: common.HexToHash("0x1"), + NewLocalExitRoot: common.HexToHash("0x2"), + FromBlock: 1, + ToBlock: 2, + Status: agglayer.Settled, + } + require.NoError(t, storage.SaveLastSentCertificate(ctx, certificate)) + + certificateFromDB, err := storage.GetCertificateByHeight(certificate.Height) + require.NoError(t, err) + require.Equal(t, certificate, certificateFromDB) + require.NoError(t, storage.clean()) + }) + + t.Run("UpdateExistingCertificate", func(t *testing.T) { + certificate := types.CertificateInfo{ + Height: 2, + CertificateID: common.HexToHash("0x3"), + NewLocalExitRoot: common.HexToHash("0x4"), + FromBlock: 3, + ToBlock: 4, + Status: agglayer.InError, + } + require.NoError(t, storage.SaveLastSentCertificate(ctx, certificate)) + + // Update the certificate with the same height + updatedCertificate := types.CertificateInfo{ + Height: 2, + CertificateID: common.HexToHash("0x5"), + NewLocalExitRoot: common.HexToHash("0x6"), + FromBlock: 3, + ToBlock: 6, + Status: agglayer.Pending, + } + require.NoError(t, storage.SaveLastSentCertificate(ctx, updatedCertificate)) + + certificateFromDB, err := storage.GetCertificateByHeight(updatedCertificate.Height) + require.NoError(t, err) + require.Equal(t, updatedCertificate, certificateFromDB) + require.NoError(t, storage.clean()) + }) + + t.Run("SaveCertificateWithRollback", func(t *testing.T) { + // Simulate an error during the transaction to trigger a rollback + certificate := types.CertificateInfo{ + Height: 3, + CertificateID: common.HexToHash("0x7"), + NewLocalExitRoot: common.HexToHash("0x8"), + FromBlock: 7, + ToBlock: 8, + Status: agglayer.Settled, + } + + // Close the database to force an error + require.NoError(t, storage.db.Close()) + + err := storage.SaveLastSentCertificate(ctx, certificate) + require.Error(t, err) + + // Reopen the database and check that the certificate was not saved + storage.db, err = db.NewSQLiteDB(path) + require.NoError(t, err) + + certificateFromDB, err := storage.GetCertificateByHeight(certificate.Height) + require.ErrorIs(t, err, db.ErrNotFound) + require.Equal(t, types.CertificateInfo{}, certificateFromDB) + require.NoError(t, storage.clean()) + }) +} diff --git a/aggsender/mocks/mock_aggsender_storage.go b/aggsender/mocks/mock_aggsender_storage.go index a5f193fc4..17f8d2272 100644 --- a/aggsender/mocks/mock_aggsender_storage.go +++ b/aggsender/mocks/mock_aggsender_storage.go @@ -73,9 +73,9 @@ func (_c *AggSenderStorageMock_DeleteCertificate_Call) RunAndReturn(run func(con return _c } -// GetCertificateByHeight provides a mock function with given fields: ctx, height -func (_m *AggSenderStorageMock) GetCertificateByHeight(ctx context.Context, height uint64) (types.CertificateInfo, error) { - ret := _m.Called(ctx, height) +// GetCertificateByHeight provides a mock function with given fields: height +func (_m *AggSenderStorageMock) GetCertificateByHeight(height uint64) (types.CertificateInfo, error) { + ret := _m.Called(height) if len(ret) == 0 { panic("no return value specified for GetCertificateByHeight") @@ -83,17 +83,17 @@ func (_m *AggSenderStorageMock) GetCertificateByHeight(ctx context.Context, heig var r0 types.CertificateInfo var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64) (types.CertificateInfo, error)); ok { - return rf(ctx, height) + if rf, ok := ret.Get(0).(func(uint64) (types.CertificateInfo, error)); ok { + return rf(height) } - if rf, ok := ret.Get(0).(func(context.Context, uint64) types.CertificateInfo); ok { - r0 = rf(ctx, height) + if rf, ok := ret.Get(0).(func(uint64) types.CertificateInfo); ok { + r0 = rf(height) } else { r0 = ret.Get(0).(types.CertificateInfo) } - if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok { - r1 = rf(ctx, height) + if rf, ok := ret.Get(1).(func(uint64) error); ok { + r1 = rf(height) } else { r1 = ret.Error(1) } @@ -107,15 +107,14 @@ type AggSenderStorageMock_GetCertificateByHeight_Call struct { } // GetCertificateByHeight is a helper method to define mock.On call -// - ctx context.Context // - height uint64 -func (_e *AggSenderStorageMock_Expecter) GetCertificateByHeight(ctx interface{}, height interface{}) *AggSenderStorageMock_GetCertificateByHeight_Call { - return &AggSenderStorageMock_GetCertificateByHeight_Call{Call: _e.mock.On("GetCertificateByHeight", ctx, height)} +func (_e *AggSenderStorageMock_Expecter) GetCertificateByHeight(height interface{}) *AggSenderStorageMock_GetCertificateByHeight_Call { + return &AggSenderStorageMock_GetCertificateByHeight_Call{Call: _e.mock.On("GetCertificateByHeight", height)} } -func (_c *AggSenderStorageMock_GetCertificateByHeight_Call) Run(run func(ctx context.Context, height uint64)) *AggSenderStorageMock_GetCertificateByHeight_Call { +func (_c *AggSenderStorageMock_GetCertificateByHeight_Call) Run(run func(height uint64)) *AggSenderStorageMock_GetCertificateByHeight_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64)) + run(args[0].(uint64)) }) return _c } @@ -125,14 +124,14 @@ func (_c *AggSenderStorageMock_GetCertificateByHeight_Call) Return(_a0 types.Cer return _c } -func (_c *AggSenderStorageMock_GetCertificateByHeight_Call) RunAndReturn(run func(context.Context, uint64) (types.CertificateInfo, error)) *AggSenderStorageMock_GetCertificateByHeight_Call { +func (_c *AggSenderStorageMock_GetCertificateByHeight_Call) RunAndReturn(run func(uint64) (types.CertificateInfo, error)) *AggSenderStorageMock_GetCertificateByHeight_Call { _c.Call.Return(run) return _c } -// GetCertificatesByStatus provides a mock function with given fields: ctx, status -func (_m *AggSenderStorageMock) GetCertificatesByStatus(ctx context.Context, status []agglayer.CertificateStatus) ([]*types.CertificateInfo, error) { - ret := _m.Called(ctx, status) +// GetCertificatesByStatus provides a mock function with given fields: status +func (_m *AggSenderStorageMock) GetCertificatesByStatus(status []agglayer.CertificateStatus) ([]*types.CertificateInfo, error) { + ret := _m.Called(status) if len(ret) == 0 { panic("no return value specified for GetCertificatesByStatus") @@ -140,19 +139,19 @@ func (_m *AggSenderStorageMock) GetCertificatesByStatus(ctx context.Context, sta var r0 []*types.CertificateInfo var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []agglayer.CertificateStatus) ([]*types.CertificateInfo, error)); ok { - return rf(ctx, status) + if rf, ok := ret.Get(0).(func([]agglayer.CertificateStatus) ([]*types.CertificateInfo, error)); ok { + return rf(status) } - if rf, ok := ret.Get(0).(func(context.Context, []agglayer.CertificateStatus) []*types.CertificateInfo); ok { - r0 = rf(ctx, status) + if rf, ok := ret.Get(0).(func([]agglayer.CertificateStatus) []*types.CertificateInfo); ok { + r0 = rf(status) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*types.CertificateInfo) } } - if rf, ok := ret.Get(1).(func(context.Context, []agglayer.CertificateStatus) error); ok { - r1 = rf(ctx, status) + if rf, ok := ret.Get(1).(func([]agglayer.CertificateStatus) error); ok { + r1 = rf(status) } else { r1 = ret.Error(1) } @@ -166,15 +165,14 @@ type AggSenderStorageMock_GetCertificatesByStatus_Call struct { } // GetCertificatesByStatus is a helper method to define mock.On call -// - ctx context.Context // - status []agglayer.CertificateStatus -func (_e *AggSenderStorageMock_Expecter) GetCertificatesByStatus(ctx interface{}, status interface{}) *AggSenderStorageMock_GetCertificatesByStatus_Call { - return &AggSenderStorageMock_GetCertificatesByStatus_Call{Call: _e.mock.On("GetCertificatesByStatus", ctx, status)} +func (_e *AggSenderStorageMock_Expecter) GetCertificatesByStatus(status interface{}) *AggSenderStorageMock_GetCertificatesByStatus_Call { + return &AggSenderStorageMock_GetCertificatesByStatus_Call{Call: _e.mock.On("GetCertificatesByStatus", status)} } -func (_c *AggSenderStorageMock_GetCertificatesByStatus_Call) Run(run func(ctx context.Context, status []agglayer.CertificateStatus)) *AggSenderStorageMock_GetCertificatesByStatus_Call { +func (_c *AggSenderStorageMock_GetCertificatesByStatus_Call) Run(run func(status []agglayer.CertificateStatus)) *AggSenderStorageMock_GetCertificatesByStatus_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]agglayer.CertificateStatus)) + run(args[0].([]agglayer.CertificateStatus)) }) return _c } @@ -184,14 +182,14 @@ func (_c *AggSenderStorageMock_GetCertificatesByStatus_Call) Return(_a0 []*types return _c } -func (_c *AggSenderStorageMock_GetCertificatesByStatus_Call) RunAndReturn(run func(context.Context, []agglayer.CertificateStatus) ([]*types.CertificateInfo, error)) *AggSenderStorageMock_GetCertificatesByStatus_Call { +func (_c *AggSenderStorageMock_GetCertificatesByStatus_Call) RunAndReturn(run func([]agglayer.CertificateStatus) ([]*types.CertificateInfo, error)) *AggSenderStorageMock_GetCertificatesByStatus_Call { _c.Call.Return(run) return _c } -// GetLastSentCertificate provides a mock function with given fields: ctx -func (_m *AggSenderStorageMock) GetLastSentCertificate(ctx context.Context) (types.CertificateInfo, error) { - ret := _m.Called(ctx) +// GetLastSentCertificate provides a mock function with given fields: +func (_m *AggSenderStorageMock) GetLastSentCertificate() (types.CertificateInfo, error) { + ret := _m.Called() if len(ret) == 0 { panic("no return value specified for GetLastSentCertificate") @@ -199,17 +197,17 @@ func (_m *AggSenderStorageMock) GetLastSentCertificate(ctx context.Context) (typ var r0 types.CertificateInfo var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (types.CertificateInfo, error)); ok { - return rf(ctx) + if rf, ok := ret.Get(0).(func() (types.CertificateInfo, error)); ok { + return rf() } - if rf, ok := ret.Get(0).(func(context.Context) types.CertificateInfo); ok { - r0 = rf(ctx) + if rf, ok := ret.Get(0).(func() types.CertificateInfo); ok { + r0 = rf() } else { r0 = ret.Get(0).(types.CertificateInfo) } - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() } else { r1 = ret.Error(1) } @@ -223,14 +221,13 @@ type AggSenderStorageMock_GetLastSentCertificate_Call struct { } // GetLastSentCertificate is a helper method to define mock.On call -// - ctx context.Context -func (_e *AggSenderStorageMock_Expecter) GetLastSentCertificate(ctx interface{}) *AggSenderStorageMock_GetLastSentCertificate_Call { - return &AggSenderStorageMock_GetLastSentCertificate_Call{Call: _e.mock.On("GetLastSentCertificate", ctx)} +func (_e *AggSenderStorageMock_Expecter) GetLastSentCertificate() *AggSenderStorageMock_GetLastSentCertificate_Call { + return &AggSenderStorageMock_GetLastSentCertificate_Call{Call: _e.mock.On("GetLastSentCertificate")} } -func (_c *AggSenderStorageMock_GetLastSentCertificate_Call) Run(run func(ctx context.Context)) *AggSenderStorageMock_GetLastSentCertificate_Call { +func (_c *AggSenderStorageMock_GetLastSentCertificate_Call) Run(run func()) *AggSenderStorageMock_GetLastSentCertificate_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) + run() }) return _c } @@ -240,7 +237,7 @@ func (_c *AggSenderStorageMock_GetLastSentCertificate_Call) Return(_a0 types.Cer return _c } -func (_c *AggSenderStorageMock_GetLastSentCertificate_Call) RunAndReturn(run func(context.Context) (types.CertificateInfo, error)) *AggSenderStorageMock_GetLastSentCertificate_Call { +func (_c *AggSenderStorageMock_GetLastSentCertificate_Call) RunAndReturn(run func() (types.CertificateInfo, error)) *AggSenderStorageMock_GetLastSentCertificate_Call { _c.Call.Return(run) return _c } From 4c225589e52997c9fc8ab377a030d12bff38d7ab Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Thu, 7 Nov 2024 12:35:52 +0100 Subject: [PATCH 10/27] fix: lint and coverage --- aggsender/aggsender.go | 4 ++-- aggsender/aggsender_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index b9f5f146b..5c0bb88a8 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -104,7 +104,7 @@ func (a *AggSender) sendCertificates(ctx context.Context) { func (a *AggSender) sendCertificate(ctx context.Context) error { a.log.Infof("trying to send a new certificate...") - shouldSend, err := a.shouldSendCertificate(ctx) + shouldSend, err := a.shouldSendCertificate() if err != nil { return err } @@ -507,7 +507,7 @@ 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) { +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) diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index d51969d00..028d22ccc 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -34,6 +34,30 @@ func TestExploratoryGetCertificateHeader(t *testing.T) { fmt.Print(certificateHeader) } +func TestConfigString(t *testing.T) { + config := Config{ + StoragePath: "/path/to/storage", + AggLayerURL: "http://agglayer.url", + BlockGetInterval: types.Duration{Duration: 10 * time.Second}, + CheckSettledInterval: types.Duration{Duration: 20 * time.Second}, + AggsenderPrivateKey: types.KeystoreFileConfig{Path: "/path/to/key", Password: "password"}, + URLRPCL2: "http://l2.rpc.url", + SaveCertificatesToFilesPath: "/path/to/certificates", + } + + expected := "AggSender Config:\n" + + "StoragePath: /path/to/storage\n" + + "AggLayerURL: http://agglayer.url\n" + + "BlockGetInterval: 10s\n" + + "CheckSettledInterval: 20s\n" + + "AggsenderPrivateKeyPath: /path/to/key\n" + + "AggsenderPrivateKeyPassword: password\n" + + "URLRPCL2: http://l2.rpc.url\n" + + "SaveCertificatesToFilesPath: /path/to/certificates\n" + + require.Equal(t, expected, config.String()) +} + func TestConvertClaimToImportedBridgeExit(t *testing.T) { t.Parallel() From f623931d906fcb8d5d2e4de034d9c238350af2ed Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Thu, 7 Nov 2024 12:50:06 +0100 Subject: [PATCH 11/27] feat: check for nil fields in certificate --- agglayer/client.go | 25 ++++++++++++ agglayer/client_test.go | 87 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 agglayer/client_test.go diff --git a/agglayer/client.go b/agglayer/client.go index 132c27166..c2389a12c 100644 --- a/agglayer/client.go +++ b/agglayer/client.go @@ -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 { + return common.Hash{}, err + } + response, err := rpc.JSONRPCCall(c.url, "interop_sendCertificate", certificate) if err != nil { return common.Hash{}, err @@ -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 +} diff --git a/agglayer/client_test.go b/agglayer/client_test.go new file mode 100644 index 000000000..41f494001 --- /dev/null +++ b/agglayer/client_test.go @@ -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) + } + }) + } +} From 76233168ae3cd8dbec18a73d3d606aecedc372be Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Thu, 7 Nov 2024 13:08:24 +0100 Subject: [PATCH 12/27] feat: no claims test --- aggsender/aggsender.go | 30 ++++++++-------- aggsender/aggsender_test.go | 69 ++++++++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index 5c0bb88a8..b0b5e1cbe 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -90,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(): @@ -101,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() 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() if err != nil { - return err + return nil, err } previousToBlock := lastSentCertificateInfo.ToBlock @@ -134,7 +134,7 @@ 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 @@ -142,36 +142,36 @@ func (a *AggSender) sendCertificate(ctx context.Context) error { 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()) @@ -182,13 +182,13 @@ 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 diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index 028d22ccc..468af11a7 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -1260,7 +1260,7 @@ func TestSendCertificate(t *testing.T) { aggsender, mockStorage, mockL2Syncer, mockAggLayerClient, mockL1InfoTreeSyncer := setupTest(tt) - err := aggsender.sendCertificate(context.Background()) + _, err := aggsender.sendCertificate(context.Background()) if tt.expectedError != "" { require.ErrorContains(t, err, tt.expectedError) @@ -1489,3 +1489,70 @@ func TestGetNextHeightAndPreviousLER(t *testing.T) { }) } } + +func TestSendCertificate_NoClaims(t *testing.T) { + t.Parallel() + + privateKey, err := crypto.GenerateKey() + require.NoError(t, err) + + ctx := context.Background() + mockStorage := mocks.NewAggSenderStorageMock(t) + mockL2Syncer := mocks.NewL2BridgeSyncerMock(t) + mockAggLayerClient := agglayer.NewAgglayerClientMock(t) + mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncerMock(t) + + aggSender := &AggSender{ + log: log.WithFields("aggsender-test", "no claims test"), + storage: mockStorage, + l2Syncer: mockL2Syncer, + aggLayerClient: mockAggLayerClient, + l1infoTreeSyncer: mockL1InfoTreeSyncer, + sequencerKey: privateKey, + cfg: Config{ + BlockGetInterval: types.Duration{Duration: time.Second}, + CheckSettledInterval: types.Duration{Duration: time.Second}, + }, + } + + mockStorage.On("GetCertificatesByStatus", nonSettledStatuses).Return([]*aggsendertypes.CertificateInfo{}, nil).Once() + mockStorage.On("GetLastSentCertificate").Return(aggsendertypes.CertificateInfo{ + NewLocalExitRoot: common.HexToHash("0x123"), + Height: 1, + FromBlock: 0, + ToBlock: 10, + }, nil).Once() + mockStorage.On("SaveLastSentCertificate", mock.Anything, mock.Anything).Return(nil).Once() + mockL2Syncer.On("GetLastProcessedBlock", mock.Anything).Return(uint64(50), nil) + mockL2Syncer.On("GetBridgesPublished", mock.Anything, uint64(11), uint64(50)).Return([]bridgesync.Bridge{ + { + BlockNum: 30, + BlockPos: 0, + LeafType: agglayer.LeafTypeAsset.Uint8(), + OriginNetwork: 1, + OriginAddress: common.HexToAddress("0x1"), + DestinationNetwork: 2, + DestinationAddress: common.HexToAddress("0x2"), + Amount: big.NewInt(100), + Metadata: []byte("metadata"), + DepositCount: 1, + }, + }, nil).Once() + mockL2Syncer.On("GetClaims", mock.Anything, uint64(11), uint64(50)).Return([]bridgesync.Claim{}, nil).Once() + mockL2Syncer.On("GetExitRootByIndex", mock.Anything, uint32(1)).Return(treeTypes.Root{}, nil).Once() + mockL2Syncer.On("OriginNetwork").Return(uint32(1), nil).Once() + mockAggLayerClient.On("SendCertificate", mock.Anything).Return(common.Hash{}, nil).Once() + + signedCertificate, err := aggSender.sendCertificate(ctx) + require.NoError(t, err) + require.NotNil(t, signedCertificate) + require.NotNil(t, signedCertificate.Signature) + require.NotNil(t, signedCertificate.Certificate) + require.NotNil(t, signedCertificate.Certificate.ImportedBridgeExits) + require.Len(t, signedCertificate.Certificate.BridgeExits, 1) + + mockStorage.AssertExpectations(t) + mockL2Syncer.AssertExpectations(t) + mockAggLayerClient.AssertExpectations(t) + mockL1InfoTreeSyncer.AssertExpectations(t) +} From e2ff575c631a3a678617fe0172022ad631dae1f3 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Thu, 7 Nov 2024 14:56:34 +0100 Subject: [PATCH 13/27] fix: comments --- agglayer/client.go | 27 +------------ agglayer/client_test.go | 87 ---------------------------------------- agglayer/types.go | 26 ++++++++++++ agglayer/types_test.go | 88 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 112 deletions(-) delete mode 100644 agglayer/client_test.go diff --git a/agglayer/client.go b/agglayer/client.go index c2389a12c..54f1c4c56 100644 --- a/agglayer/client.go +++ b/agglayer/client.go @@ -91,11 +91,9 @@ 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 { - return common.Hash{}, err - } + certificateToSend := certificate.Copy() - response, err := rpc.JSONRPCCall(c.url, "interop_sendCertificate", certificate) + response, err := rpc.JSONRPCCall(c.url, "interop_sendCertificate", certificateToSend) if err != nil { return common.Hash{}, err } @@ -132,24 +130,3 @@ 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 -} diff --git a/agglayer/client_test.go b/agglayer/client_test.go deleted file mode 100644 index 41f494001..000000000 --- a/agglayer/client_test.go +++ /dev/null @@ -1,87 +0,0 @@ -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) - } - }) - } -} diff --git a/agglayer/types.go b/agglayer/types.go index 825c9db23..69f88a897 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -131,6 +131,32 @@ type SignedCertificate struct { Signature *Signature `json:"signature"` } +// Copy returns a deep copy of the signed certificate +func (s *SignedCertificate) Copy() *SignedCertificate { + certificateCopy := &Certificate{ + NetworkID: s.NetworkID, + Height: s.Height, + PrevLocalExitRoot: s.PrevLocalExitRoot, + NewLocalExitRoot: s.NewLocalExitRoot, + BridgeExits: make([]*BridgeExit, len(s.BridgeExits)), + ImportedBridgeExits: make([]*ImportedBridgeExit, len(s.ImportedBridgeExits)), + Metadata: s.Metadata, + } + + copy(certificateCopy.BridgeExits, s.BridgeExits) + copy(certificateCopy.ImportedBridgeExits, s.ImportedBridgeExits) + + signature := s.Signature + if signature == nil { + signature = &Signature{} + } + + return &SignedCertificate{ + Certificate: certificateCopy, + Signature: signature, + } +} + // Signature is the data structure that will hold the signature of the given certificate type Signature struct { R common.Hash `json:"r"` diff --git a/agglayer/types_test.go b/agglayer/types_test.go index 325c0b882..734c83c70 100644 --- a/agglayer/types_test.go +++ b/agglayer/types_test.go @@ -64,3 +64,91 @@ func TestMarshalJSON(t *testing.T) { log.Info(string(data)) require.Equal(t, expectedSignedCertificateyMetadataJSON, string(data)) } + +func TestSignedCertificate_Copy(t *testing.T) { + t.Parallel() + + t.Run("copy with non-nil fields", func(t *testing.T) { + t.Parallel() + + original := &SignedCertificate{ + Certificate: &Certificate{ + NetworkID: 1, + Height: 100, + PrevLocalExitRoot: [32]byte{0x01}, + NewLocalExitRoot: [32]byte{0x02}, + BridgeExits: []*BridgeExit{ + { + LeafType: LeafTypeAsset, + TokenInfo: &TokenInfo{OriginNetwork: 1, OriginTokenAddress: common.HexToAddress("0x123")}, + DestinationNetwork: 2, + DestinationAddress: common.HexToAddress("0x456"), + Amount: big.NewInt(1000), + Metadata: []byte{0x01, 0x02}, + }, + }, + ImportedBridgeExits: []*ImportedBridgeExit{ + { + BridgeExit: &BridgeExit{ + LeafType: LeafTypeMessage, + TokenInfo: &TokenInfo{OriginNetwork: 1, OriginTokenAddress: common.HexToAddress("0x789")}, + DestinationNetwork: 3, + DestinationAddress: common.HexToAddress("0xabc"), + Amount: big.NewInt(2000), + Metadata: []byte{0x03, 0x04}, + }, + ClaimData: &ClaimFromMainnnet{}, + GlobalIndex: &GlobalIndex{MainnetFlag: true, RollupIndex: 1, LeafIndex: 2}, + }, + }, + Metadata: common.HexToHash("0xdef"), + }, + Signature: &Signature{ + R: common.HexToHash("0x111"), + S: common.HexToHash("0x222"), + OddParity: true, + }, + } + + copy := original.Copy() + + require.NotNil(t, copy) + require.NotSame(t, original, copy) + require.NotSame(t, original.Certificate, copy.Certificate) + require.Same(t, original.Signature, copy.Signature) + require.Equal(t, original, copy) + }) + + t.Run("copy with nil BridgeExits, ImportedBridgeExits and Signature", func(t *testing.T) { + t.Parallel() + + original := &SignedCertificate{ + Certificate: &Certificate{ + NetworkID: 1, + Height: 100, + PrevLocalExitRoot: [32]byte{0x01}, + NewLocalExitRoot: [32]byte{0x02}, + BridgeExits: nil, + ImportedBridgeExits: nil, + Metadata: common.HexToHash("0xdef"), + }, + Signature: nil, + } + + copy := original.Copy() + + require.NotNil(t, copy) + require.NotSame(t, original, copy) + require.NotSame(t, original.Certificate, copy.Certificate) + require.NotNil(t, copy.Signature) + require.Equal(t, original.NetworkID, copy.NetworkID) + require.Equal(t, original.Height, copy.Height) + require.Equal(t, original.PrevLocalExitRoot, copy.PrevLocalExitRoot) + require.Equal(t, original.NewLocalExitRoot, copy.NewLocalExitRoot) + require.Equal(t, original.Metadata, copy.Metadata) + require.NotNil(t, copy.BridgeExits) + require.NotNil(t, copy.ImportedBridgeExits) + require.Empty(t, copy.BridgeExits) + require.Empty(t, copy.ImportedBridgeExits) + }) +} From a6e07788889f5d04c49584fa8cacf55f113af46b Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Thu, 7 Nov 2024 15:02:35 +0100 Subject: [PATCH 14/27] fix: lint --- agglayer/types_test.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/agglayer/types_test.go b/agglayer/types_test.go index 734c83c70..56e6064cc 100644 --- a/agglayer/types_test.go +++ b/agglayer/types_test.go @@ -110,13 +110,13 @@ func TestSignedCertificate_Copy(t *testing.T) { }, } - copy := original.Copy() + certificateCopy := original.Copy() - require.NotNil(t, copy) - require.NotSame(t, original, copy) - require.NotSame(t, original.Certificate, copy.Certificate) - require.Same(t, original.Signature, copy.Signature) - require.Equal(t, original, copy) + require.NotNil(t, certificateCopy) + require.NotSame(t, original, certificateCopy) + require.NotSame(t, original.Certificate, certificateCopy.Certificate) + require.Same(t, original.Signature, certificateCopy.Signature) + require.Equal(t, original, certificateCopy) }) t.Run("copy with nil BridgeExits, ImportedBridgeExits and Signature", func(t *testing.T) { @@ -135,20 +135,20 @@ func TestSignedCertificate_Copy(t *testing.T) { Signature: nil, } - copy := original.Copy() + certificateCopy := original.Copy() - require.NotNil(t, copy) - require.NotSame(t, original, copy) - require.NotSame(t, original.Certificate, copy.Certificate) - require.NotNil(t, copy.Signature) - require.Equal(t, original.NetworkID, copy.NetworkID) - require.Equal(t, original.Height, copy.Height) - require.Equal(t, original.PrevLocalExitRoot, copy.PrevLocalExitRoot) - require.Equal(t, original.NewLocalExitRoot, copy.NewLocalExitRoot) - require.Equal(t, original.Metadata, copy.Metadata) - require.NotNil(t, copy.BridgeExits) - require.NotNil(t, copy.ImportedBridgeExits) - require.Empty(t, copy.BridgeExits) - require.Empty(t, copy.ImportedBridgeExits) + require.NotNil(t, certificateCopy) + require.NotSame(t, original, certificateCopy) + require.NotSame(t, original.Certificate, certificateCopy.Certificate) + require.NotNil(t, certificateCopy.Signature) + require.Equal(t, original.NetworkID, certificateCopy.NetworkID) + require.Equal(t, original.Height, certificateCopy.Height) + require.Equal(t, original.PrevLocalExitRoot, certificateCopy.PrevLocalExitRoot) + require.Equal(t, original.NewLocalExitRoot, certificateCopy.NewLocalExitRoot) + require.Equal(t, original.Metadata, certificateCopy.Metadata) + require.NotNil(t, certificateCopy.BridgeExits) + require.NotNil(t, certificateCopy.ImportedBridgeExits) + require.Empty(t, certificateCopy.BridgeExits) + require.Empty(t, certificateCopy.ImportedBridgeExits) }) } From 0d5f454975d1ebc395f755a1fd60bca2a6ee2201 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Thu, 7 Nov 2024 15:12:54 +0100 Subject: [PATCH 15/27] fix: shallow copy imported bridge exits and bridge exits --- agglayer/client.go | 2 +- agglayer/types.go | 23 ++++++++++------------- agglayer/types_test.go | 4 ++-- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/agglayer/client.go b/agglayer/client.go index 54f1c4c56..e60c1c7c9 100644 --- a/agglayer/client.go +++ b/agglayer/client.go @@ -91,7 +91,7 @@ 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) { - certificateToSend := certificate.Copy() + certificateToSend := certificate.CopyWithDefaulting() response, err := rpc.JSONRPCCall(c.url, "interop_sendCertificate", certificateToSend) if err != nil { diff --git a/agglayer/types.go b/agglayer/types.go index 69f88a897..ef15be105 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -131,20 +131,17 @@ type SignedCertificate struct { Signature *Signature `json:"signature"` } -// Copy returns a deep copy of the signed certificate -func (s *SignedCertificate) Copy() *SignedCertificate { - certificateCopy := &Certificate{ - NetworkID: s.NetworkID, - Height: s.Height, - PrevLocalExitRoot: s.PrevLocalExitRoot, - NewLocalExitRoot: s.NewLocalExitRoot, - BridgeExits: make([]*BridgeExit, len(s.BridgeExits)), - ImportedBridgeExits: make([]*ImportedBridgeExit, len(s.ImportedBridgeExits)), - Metadata: s.Metadata, +// CopyWithDefaulting returns a shallow copy of the signed certificate +func (s *SignedCertificate) CopyWithDefaulting() *SignedCertificate { + certificateCopy := *s.Certificate + + if certificateCopy.BridgeExits == nil { + certificateCopy.BridgeExits = make([]*BridgeExit, 0) } - copy(certificateCopy.BridgeExits, s.BridgeExits) - copy(certificateCopy.ImportedBridgeExits, s.ImportedBridgeExits) + if certificateCopy.ImportedBridgeExits == nil { + certificateCopy.ImportedBridgeExits = make([]*ImportedBridgeExit, 0) + } signature := s.Signature if signature == nil { @@ -152,7 +149,7 @@ func (s *SignedCertificate) Copy() *SignedCertificate { } return &SignedCertificate{ - Certificate: certificateCopy, + Certificate: &certificateCopy, Signature: signature, } } diff --git a/agglayer/types_test.go b/agglayer/types_test.go index 56e6064cc..950331419 100644 --- a/agglayer/types_test.go +++ b/agglayer/types_test.go @@ -110,7 +110,7 @@ func TestSignedCertificate_Copy(t *testing.T) { }, } - certificateCopy := original.Copy() + certificateCopy := original.CopyWithDefaulting() require.NotNil(t, certificateCopy) require.NotSame(t, original, certificateCopy) @@ -135,7 +135,7 @@ func TestSignedCertificate_Copy(t *testing.T) { Signature: nil, } - certificateCopy := original.Copy() + certificateCopy := original.CopyWithDefaulting() require.NotNil(t, certificateCopy) require.NotSame(t, original, certificateCopy) From c432bab43d7a8b5ea4adf37e5101d8eb48555526 Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:50:57 +0100 Subject: [PATCH 16/27] fix: local_config for debug --- scripts/local_config | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/local_config b/scripts/local_config index a134220ec..0a3b94730 100755 --- a/scripts/local_config +++ b/scripts/local_config @@ -120,10 +120,10 @@ function export_obj_key_from_toml_file(){ local _SECTION="$3" local _KEY="$4" local _OBJ_KEY="$5" - log_debug "export_obj_key_from_toml_file_or_fatal: $_EXPORTED_VAR_NAME $_FILE $_SECTION $_KEY $_OBJ_KEY" + log_debug "export_obj_key_from_toml_file: $_EXPORTED_VAR_NAME $_FILE $_SECTION $_KEY $_OBJ_KEY" local _VALUE=$(get_value_from_toml_file $_FILE $_SECTION $_KEY) if [ -z "$_VALUE" ]; then - log_error "export_obj_key_from_toml_file_or_fatal: obj_key $_KEY not found in section [$_SECTION]" + log_debug "export_obj_key_from_toml_file: obj_key $_KEY not found in section [$_SECTION]" return 1 fi local _CLEAN_VALUE=$(echo $_VALUE | tr -d '{' | tr -d '}' | tr ',' '\n') @@ -143,7 +143,7 @@ function export_obj_key_from_toml_file(){ return 0 fi done <<< "$_CLEAN_VALUE" - log_error "export_obj_key_from_toml_file_or_fatal: obj_key $_OBJ_KEY not found in section $_SECTION/ $_KEY = $_VALUE" + log_debug "export_obj_key_from_toml_file: obj_key $_OBJ_KEY not found in section $_SECTION/ $_KEY = $_VALUE" return 1 } @@ -269,7 +269,7 @@ function export_portnum_from_kurtosis_or_fail(){ ############################################################################### function export_ports_from_kurtosis(){ export_portnum_from_kurtosis_or_fail l1_rpc_port el-1-geth-lighthouse rpc - export_portnum_from_kurtosis_or_fail zkevm_rpc_http_port cdk-erigon-node-001 http-rpc rpc + export_portnum_from_kurtosis_or_fail zkevm_rpc_http_port cdk-erigon-rpc-001 http-rpc rpc export_portnum_from_kurtosis_or_fail zkevm_data_streamer_port cdk-erigon-sequencer-001 data-streamer export_portnum_from_kurtosis_or_fail aggregator_db_port postgres-001 postgres export_portnum_from_kurtosis_or_fail agglayer_port agglayer agglayer From eb4bc3a4db44d84426206c5554a1eb45233af87d Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:57:11 +0100 Subject: [PATCH 17/27] fix: cdk-erigon-node-001 rename to cdk-erigon-rpc-001 --- test/helpers/common-setup.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helpers/common-setup.bash b/test/helpers/common-setup.bash index dac81bebf..5f53cbf83 100644 --- a/test/helpers/common-setup.bash +++ b/test/helpers/common-setup.bash @@ -21,6 +21,6 @@ _common_setup() { readonly enclave=${KURTOSIS_ENCLAVE:-cdk} readonly contracts_container=${KURTOSIS_CONTRACTS:-contracts-001} readonly contracts_service_wrapper=${KURTOSIS_CONTRACTS_WRAPPER:-"kurtosis service exec $enclave $contracts_container"} - readonly erigon_rpc_node=${KURTOSIS_ERIGON_RPC:-cdk-erigon-node-001} + readonly erigon_rpc_node=${KURTOSIS_ERIGON_RPC:-cdk-erigon-rpc-001} readonly l2_rpc_url=${L2_ETH_RPC_URL:-"$(kurtosis port print $enclave $erigon_rpc_node rpc)"} } From 82ff40ae1a8b9b512d6f66c734e0a4127aced53d Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Fri, 8 Nov 2024 08:16:31 +0100 Subject: [PATCH 18/27] feat: add logs to check cert --- aggsender/aggsender.go | 16 ++++++++++------ aggsender/aggsender_test.go | 17 +---------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index b0b5e1cbe..1cd023963 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -483,22 +483,26 @@ func (a *AggSender) checkPendingCertificatesStatus(ctx context.Context) { a.log.Errorf("error getting pending certificates: %w", err) return } - + a.log.Debugf("checkPendingCertificatesStatus num of pendingCertificates: %d", len(pendingCertificates)) for _, certificate := range pendingCertificates { certificateHeader, err := a.aggLayerClient.GetCertificateHeader(certificate.CertificateID) if err != nil { - a.log.Errorf("error getting header of certificate %s with height: %d from agglayer: %w", - certificate.CertificateID, certificate.Height, err) + a.log.Errorf("error getting certificate header of %s from agglayer: %w", + certificate.String(), err) continue } + a.log.Debugf("aggLayerClient.GetCertificateHeader status [%s] of certificate %s ", + certificateHeader.Status, + certificateHeader.String()) if certificateHeader.Status != certificate.Status { - certificate.Status = certificateHeader.Status + a.log.Infof("certificate %s changed status from [%s] to [%s]", + certificateHeader.String(), certificate.Status, certificateHeader.Status) - a.log.Infof("certificate %s changed status to %s", certificateHeader.String(), certificate.Status) + certificate.Status = certificateHeader.Status if err := a.storage.UpdateCertificateStatus(ctx, *certificate); err != nil { - a.log.Errorf("error updating certificate status in storage: %w", err) + a.log.Errorf("error updating certificate %s status in storage: %w", certificateHeader.String(), err) continue } } diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index 468af11a7..3cce51dd7 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -825,7 +825,7 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { mockStorage := mocks.NewAggSenderStorageMock(t) mockAggLayerClient := agglayer.NewAgglayerClientMock(t) - mockLogger := mocks.NewLoggerMock(t) + mockLogger := log.WithFields("test", "unittest") mockStorage.On("GetCertificatesByStatus", nonSettledStatuses).Return( tt.pendingCertificates, tt.getFromDBError) @@ -838,20 +838,6 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { mockStorage.On("UpdateCertificateStatus", mock.Anything, mock.Anything).Return(nil) } - if tt.clientError != nil { - for _, msg := range tt.expectedErrorLogMessages { - mockLogger.On("Errorf", msg, mock.Anything, mock.Anything, mock.Anything).Return() - } - } else { - for _, msg := range tt.expectedErrorLogMessages { - mockLogger.On("Errorf", msg, mock.Anything).Return() - } - - for _, msg := range tt.expectedInfoMessages { - mockLogger.On("Infof", msg, mock.Anything, mock.Anything).Return() - } - } - aggSender := &AggSender{ log: mockLogger, storage: mockStorage, @@ -870,7 +856,6 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { time.Sleep(2 * time.Second) cancel() - mockLogger.AssertExpectations(t) mockAggLayerClient.AssertExpectations(t) mockStorage.AssertExpectations(t) }) From c221101135499a9d6e3a157a377152dd5c9c57af Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Fri, 8 Nov 2024 09:57:45 +0100 Subject: [PATCH 19/27] feat: store hash as text, add logs --- agglayer/types.go | 60 ++++++++++++++++++++++++++++ aggsender/aggsender.go | 17 ++++---- aggsender/db/aggsender_db_storage.go | 4 +- aggsender/types/types.go | 4 +- 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/agglayer/types.go b/agglayer/types.go index ef15be105..5d66e71dc 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -86,6 +86,27 @@ type Certificate struct { Metadata common.Hash `json:"metadata"` } +func (c *Certificate) String() string { + res := fmt.Sprintf("NetworkID: %d, Height: %d, PrevLocalExitRoot: %s, NewLocalExitRoot: %s, Metadata: %s\n", + c.NetworkID, c.Height, common.Bytes2Hex(c.PrevLocalExitRoot[:]), common.Bytes2Hex(c.NewLocalExitRoot[:]), common.Bytes2Hex(c.Metadata[:])) + if c.BridgeExits == nil { + res += " BridgeExits: nil\n" + } else { + for i, bridgeExit := range c.BridgeExits { + res += fmt.Sprintf(", BridgeExit[%d]: %s\n", i, bridgeExit.String()) + } + } + + if c.ImportedBridgeExits == nil { + res += " ImportedBridgeExits: nil\n" + } else { + for i, importedBridgeExit := range c.ImportedBridgeExits { + res += fmt.Sprintf(" ImportedBridgeExit[%d]: %s\n", i, importedBridgeExit.String()) + } + } + return res +} + // Hash returns a hash that uniquely identifies the certificate func (c *Certificate) Hash() common.Hash { bridgeExitsHashes := make([][]byte, len(c.BridgeExits)) @@ -131,6 +152,10 @@ type SignedCertificate struct { Signature *Signature `json:"signature"` } +func (s *SignedCertificate) String() string { + return fmt.Sprintf("Certificate:%s, \nSignature: %s", s.Certificate.String(), s.Signature.String()) +} + // CopyWithDefaulting returns a shallow copy of the signed certificate func (s *SignedCertificate) CopyWithDefaulting() *SignedCertificate { certificateCopy := *s.Certificate @@ -161,12 +186,20 @@ type Signature struct { OddParity bool `json:"odd_y_parity"` } +func (s *Signature) String() string { + return fmt.Sprintf("R: %s, S: %s, OddParity: %t", s.R.String(), s.S.String(), s.OddParity) +} + // TokenInfo encapsulates the information to uniquely identify a token on the origin network. type TokenInfo struct { OriginNetwork uint32 `json:"origin_network"` OriginTokenAddress common.Address `json:"origin_token_address"` } +func (t *TokenInfo) String() string { + return fmt.Sprintf("OriginNetwork: %d, OriginTokenAddress: %s", t.OriginNetwork, t.OriginTokenAddress.String()) +} + // GlobalIndex represents the global index of an imported bridge exit type GlobalIndex struct { MainnetFlag bool `json:"mainnet_flag"` @@ -192,6 +225,17 @@ type BridgeExit struct { Metadata []byte `json:"metadata"` } +func (b *BridgeExit) String() string { + res := fmt.Sprintf("LeafType: %s, DestinationNetwork: %d, DestinationAddress: %s, Amount: %s, Metadata: %s", + b.LeafType.String(), b.DestinationNetwork, b.DestinationAddress.String(), b.Amount.String(), string(b.Metadata)) + if b.TokenInfo == nil { + res += ", TokenInfo: nil" + } else { + res += fmt.Sprintf(", TokenInfo: %s", b.TokenInfo.String()) + } + return res +} + // Hash returns a hash that uniquely identifies the bridge exit func (b *BridgeExit) Hash() common.Hash { if b.Amount == nil { @@ -402,6 +446,22 @@ type ImportedBridgeExit struct { GlobalIndex *GlobalIndex `json:"global_index"` } +func (c *ImportedBridgeExit) String() string { + var res string + if c.BridgeExit == nil { + res = "BridgeExit: nil" + } else { + res = fmt.Sprintf("BridgeExit: %s", c.BridgeExit.String()) + } + if c.GlobalIndex == nil { + res += ", GlobalIndex: nil" + } else { + res += fmt.Sprintf(", GlobalIndex: %v", c.GlobalIndex) + } + res += fmt.Sprintf("ClaimData: %v", c.ClaimData) + return res +} + // Hash returns a hash that uniquely identifies the imported bridge exit func (c *ImportedBridgeExit) Hash() common.Hash { return crypto.Keccak256Hash( diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index 1cd023963..a236e9ced 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -168,25 +168,26 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif } a.saveCertificateToFile(signedCertificate) - + a.log.Debugf("certificate ready to be send to AggLayer: %s", signedCertificate.String()) certificateHash, err := a.aggLayerClient.SendCertificate(signedCertificate) if err != nil { return nil, fmt.Errorf("error sending certificate: %w", err) } - log.Infof("certificate send: Height: %d hash: %s", signedCertificate.Height, certificateHash.String()) - - if err := a.storage.SaveLastSentCertificate(ctx, aggsendertypes.CertificateInfo{ + a.log.Debugf("certificate send: Height: %d hash: %s", signedCertificate.Height, certificateHash.String()) + certInfo := aggsendertypes.CertificateInfo{ Height: certificate.Height, CertificateID: certificateHash, NewLocalExitRoot: certificate.NewLocalExitRoot, FromBlock: fromBlock, ToBlock: toBlock, - }); err != nil { - 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) + if err := a.storage.SaveLastSentCertificate(ctx, certInfo); err != nil { + return nil, fmt.Errorf("error saving last sent certificate %s in db: %w", certInfo.String(), err) + } + + a.log.Infof("certificate: %s sent successfully for range of l2 blocks (from block: %d, to block: %d) cert:%s", + certificateHash, fromBlock, toBlock, signedCertificate.String()) return signedCertificate, nil } diff --git a/aggsender/db/aggsender_db_storage.go b/aggsender/db/aggsender_db_storage.go index dc1f8b104..15866c298 100644 --- a/aggsender/db/aggsender_db_storage.go +++ b/aggsender/db/aggsender_db_storage.go @@ -184,7 +184,7 @@ func (a *AggSenderSQLStorage) DeleteCertificate(ctx context.Context, certificate // deleteCertificate deletes a certificate from the storage using the provided db func deleteCertificate(db meddler.DB, certificateID common.Hash) error { - if _, err := db.Exec(`DELETE FROM certificate_info WHERE certificate_id = $1;`, certificateID); err != nil { + if _, err := db.Exec(`DELETE FROM certificate_info WHERE certificate_id = $1;`, certificateID.String()); err != nil { return fmt.Errorf("error deleting certificate info: %w", err) } @@ -206,7 +206,7 @@ func (a *AggSenderSQLStorage) UpdateCertificateStatus(ctx context.Context, certi }() if _, err = tx.Exec(`UPDATE certificate_info SET status = $1 WHERE certificate_id = $2;`, - certificate.Status, certificate.CertificateID); err != nil { + certificate.Status, certificate.CertificateID.String()); err != nil { return fmt.Errorf("error updating certificate info: %w", err) } if err = tx.Commit(); err != nil { diff --git a/aggsender/types/types.go b/aggsender/types/types.go index d64211326..ffdf4d24d 100644 --- a/aggsender/types/types.go +++ b/aggsender/types/types.go @@ -52,8 +52,8 @@ type Logger interface { type CertificateInfo struct { Height uint64 `meddler:"height"` - CertificateID common.Hash `meddler:"certificate_id"` - NewLocalExitRoot common.Hash `meddler:"new_local_exit_root"` + CertificateID common.Hash `meddler:"certificate_id,hash"` + NewLocalExitRoot common.Hash `meddler:"new_local_exit_root,hash"` FromBlock uint64 `meddler:"from_block"` ToBlock uint64 `meddler:"to_block"` Status agglayer.CertificateStatus `meddler:"status"` From 3a8f9915bb37ee4e54dbd68825f90293e11759a3 Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:14:48 +0100 Subject: [PATCH 20/27] fix: lint --- agglayer/types.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agglayer/types.go b/agglayer/types.go index 5d66e71dc..c479ed574 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -88,7 +88,8 @@ type Certificate struct { func (c *Certificate) String() string { res := fmt.Sprintf("NetworkID: %d, Height: %d, PrevLocalExitRoot: %s, NewLocalExitRoot: %s, Metadata: %s\n", - c.NetworkID, c.Height, common.Bytes2Hex(c.PrevLocalExitRoot[:]), common.Bytes2Hex(c.NewLocalExitRoot[:]), common.Bytes2Hex(c.Metadata[:])) + c.NetworkID, c.Height, common.Bytes2Hex(c.PrevLocalExitRoot[:]), + common.Bytes2Hex(c.NewLocalExitRoot[:]), common.Bytes2Hex(c.Metadata[:])) if c.BridgeExits == nil { res += " BridgeExits: nil\n" } else { From 824d6ed00a57b5385552bd5447d5cab3ca88a3e3 Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:17:22 +0100 Subject: [PATCH 21/27] fix: bump kurtosis-cdk version to 0.2.18 --- .github/workflows/test-e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 7fdb5a2b5..980ad990f 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -70,7 +70,7 @@ jobs: with: repository: 0xPolygon/kurtosis-cdk path: "kurtosis-cdk" - ref: "v0.2.15" + ref: "v0.2.18" - name: Setup Bats and bats libs uses: bats-core/bats-action@2.0.0 From db602e6fdd89ecedb9aca8526e2b32ef12624a57 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Fri, 8 Nov 2024 10:51:05 +0100 Subject: [PATCH 22/27] fix: comments --- agglayer/types.go | 48 ++++++++++++++++++++++++++++++++++--- aggsender/aggsender.go | 5 +++- aggsender/aggsender_test.go | 3 +-- aggsender/config.go | 3 +-- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/agglayer/types.go b/agglayer/types.go index c479ed574..dd3de3629 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -90,6 +90,7 @@ func (c *Certificate) String() string { res := fmt.Sprintf("NetworkID: %d, Height: %d, PrevLocalExitRoot: %s, NewLocalExitRoot: %s, Metadata: %s\n", c.NetworkID, c.Height, common.Bytes2Hex(c.PrevLocalExitRoot[:]), common.Bytes2Hex(c.NewLocalExitRoot[:]), common.Bytes2Hex(c.Metadata[:])) + if c.BridgeExits == nil { res += " BridgeExits: nil\n" } else { @@ -105,6 +106,7 @@ func (c *Certificate) String() string { res += fmt.Sprintf(" ImportedBridgeExit[%d]: %s\n", i, importedBridgeExit.String()) } } + return res } @@ -154,7 +156,7 @@ type SignedCertificate struct { } func (s *SignedCertificate) String() string { - return fmt.Sprintf("Certificate:%s, \nSignature: %s", s.Certificate.String(), s.Signature.String()) + return fmt.Sprintf("Certificate:%s,\nSignature: %s", s.Certificate.String(), s.Signature.String()) } // CopyWithDefaulting returns a shallow copy of the signed certificate @@ -216,6 +218,11 @@ func (g *GlobalIndex) Hash() common.Hash { ) } +func (g *GlobalIndex) String() string { + return fmt.Sprintf("MainnetFlag: %t, RollupIndex: %d, LeafIndex: %d", + g.MainnetFlag, g.RollupIndex, g.LeafIndex) +} + // BridgeExit represents a token bridge exit type BridgeExit struct { LeafType LeafType `json:"leaf_type"` @@ -229,11 +236,13 @@ type BridgeExit struct { func (b *BridgeExit) String() string { res := fmt.Sprintf("LeafType: %s, DestinationNetwork: %d, DestinationAddress: %s, Amount: %s, Metadata: %s", b.LeafType.String(), b.DestinationNetwork, b.DestinationAddress.String(), b.Amount.String(), string(b.Metadata)) + if b.TokenInfo == nil { res += ", TokenInfo: nil" } else { res += fmt.Sprintf(", TokenInfo: %s", b.TokenInfo.String()) } + return res } @@ -320,6 +329,10 @@ func (m *MerkleProof) Hash() common.Hash { ) } +func (m *MerkleProof) String() string { + return fmt.Sprintf("Root: %s, Proof: %v", m.Root.String(), m.Proof) +} + // L1InfoTreeLeafInner represents the inner part of the L1 info tree leaf type L1InfoTreeLeafInner struct { GlobalExitRoot common.Hash `json:"global_exit_root"` @@ -349,6 +362,11 @@ func (l *L1InfoTreeLeafInner) MarshalJSON() ([]byte, error) { }) } +func (l *L1InfoTreeLeafInner) String() string { + return fmt.Sprintf("GlobalExitRoot: %s, BlockHash: %s, Timestamp: %d", + l.GlobalExitRoot.String(), l.BlockHash.String(), l.Timestamp) +} + // L1InfoTreeLeaf represents the leaf of the L1 info tree type L1InfoTreeLeaf struct { L1InfoTreeIndex uint32 `json:"l1_info_tree_index"` @@ -362,11 +380,21 @@ func (l *L1InfoTreeLeaf) Hash() common.Hash { return l.Inner.Hash() } +func (l *L1InfoTreeLeaf) String() string { + return fmt.Sprintf("L1InfoTreeIndex: %d, RollupExitRoot: %s, MainnetExitRoot: %s, Inner: %s", + l.L1InfoTreeIndex, + common.Bytes2Hex(l.RollupExitRoot[:]), + common.Bytes2Hex(l.MainnetExitRoot[:]), + l.Inner.String(), + ) +} + // Claim is the interface that will be implemented by the different types of claims type Claim interface { Type() string Hash() common.Hash MarshalJSON() ([]byte, error) + String() string } // ClaimFromMainnnet represents a claim originating from the mainnet @@ -403,6 +431,11 @@ func (c *ClaimFromMainnnet) Hash() common.Hash { ) } +func (c *ClaimFromMainnnet) String() string { + return fmt.Sprintf("ProofLeafMER: %s, ProofGERToL1Root: %s, L1Leaf: %s", + c.ProofLeafMER.String(), c.ProofGERToL1Root.String(), c.L1Leaf.String()) +} + // ClaimFromRollup represents a claim originating from a rollup type ClaimFromRollup struct { ProofLeafLER *MerkleProof `json:"proof_leaf_ler"` @@ -440,6 +473,11 @@ func (c *ClaimFromRollup) Hash() common.Hash { ) } +func (c *ClaimFromRollup) String() string { + return fmt.Sprintf("ProofLeafLER: %s, ProofLERToRER: %s, ProofGERToL1Root: %s, L1Leaf: %s", + c.ProofLeafLER.String(), c.ProofLERToRER.String(), c.ProofGERToL1Root.String(), c.L1Leaf.String()) +} + // ImportedBridgeExit represents a token bridge exit originating on another network but claimed on the current network. type ImportedBridgeExit struct { BridgeExit *BridgeExit `json:"bridge_exit"` @@ -449,17 +487,21 @@ type ImportedBridgeExit struct { func (c *ImportedBridgeExit) String() string { var res string + if c.BridgeExit == nil { res = "BridgeExit: nil" } else { res = fmt.Sprintf("BridgeExit: %s", c.BridgeExit.String()) } + if c.GlobalIndex == nil { res += ", GlobalIndex: nil" } else { - res += fmt.Sprintf(", GlobalIndex: %v", c.GlobalIndex) + res += fmt.Sprintf(", GlobalIndex: %s", c.GlobalIndex.String()) } - res += fmt.Sprintf("ClaimData: %v", c.ClaimData) + + res += fmt.Sprintf("ClaimData: %s", c.ClaimData.String()) + return res } diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index a236e9ced..739536331 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -64,7 +64,7 @@ func New( return nil, err } - logger.Info(cfg.String()) + logger.Infof("Aggsender Config: %s.", cfg.String()) return &AggSender{ cfg: cfg, @@ -169,11 +169,14 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif a.saveCertificateToFile(signedCertificate) a.log.Debugf("certificate ready to be send to AggLayer: %s", signedCertificate.String()) + certificateHash, err := a.aggLayerClient.SendCertificate(signedCertificate) if err != nil { return nil, fmt.Errorf("error sending certificate: %w", err) } + a.log.Debugf("certificate send: Height: %d hash: %s", signedCertificate.Height, certificateHash.String()) + certInfo := aggsendertypes.CertificateInfo{ Height: certificate.Height, CertificateID: certificateHash, diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index 3cce51dd7..e55422e05 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -45,8 +45,7 @@ func TestConfigString(t *testing.T) { SaveCertificatesToFilesPath: "/path/to/certificates", } - expected := "AggSender Config:\n" + - "StoragePath: /path/to/storage\n" + + expected := "StoragePath: /path/to/storage\n" + "AggLayerURL: http://agglayer.url\n" + "BlockGetInterval: 10s\n" + "CheckSettledInterval: 20s\n" + diff --git a/aggsender/config.go b/aggsender/config.go index 4ae07abb1..4ff78f96b 100644 --- a/aggsender/config.go +++ b/aggsender/config.go @@ -24,8 +24,7 @@ type Config struct { // String returns a string representation of the Config func (c Config) String() string { - return "AggSender Config:\n" + - "StoragePath: " + c.StoragePath + "\n" + + return "StoragePath: " + c.StoragePath + "\n" + "AggLayerURL: " + c.AggLayerURL + "\n" + "BlockGetInterval: " + c.BlockGetInterval.String() + "\n" + "CheckSettledInterval: " + c.CheckSettledInterval.String() + "\n" + From ef1b9bf3adbcea5629a71052311d6e6894d141c0 Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:19:39 +0100 Subject: [PATCH 23/27] fix: string conversion error on BridgeExit --- agglayer/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agglayer/types.go b/agglayer/types.go index dd3de3629..c776b6afb 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -235,7 +235,7 @@ type BridgeExit struct { func (b *BridgeExit) String() string { res := fmt.Sprintf("LeafType: %s, DestinationNetwork: %d, DestinationAddress: %s, Amount: %s, Metadata: %s", - b.LeafType.String(), b.DestinationNetwork, b.DestinationAddress.String(), b.Amount.String(), string(b.Metadata)) + b.LeafType.String(), b.DestinationNetwork, b.DestinationAddress.String(), b.Amount.String(), common.Bytes2Hex(b.Metadata)) if b.TokenInfo == nil { res += ", TokenInfo: nil" From 6254a2c23b683dd25ba171b15edd70a4a71de594 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Fri, 8 Nov 2024 11:27:56 +0100 Subject: [PATCH 24/27] fix: lint --- agglayer/types.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agglayer/types.go b/agglayer/types.go index c776b6afb..9350e7919 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -235,7 +235,8 @@ type BridgeExit struct { func (b *BridgeExit) String() string { res := fmt.Sprintf("LeafType: %s, DestinationNetwork: %d, DestinationAddress: %s, Amount: %s, Metadata: %s", - b.LeafType.String(), b.DestinationNetwork, b.DestinationAddress.String(), b.Amount.String(), common.Bytes2Hex(b.Metadata)) + b.LeafType.String(), b.DestinationNetwork, b.DestinationAddress.String(), + b.Amount.String(), common.Bytes2Hex(b.Metadata)) if b.TokenInfo == nil { res += ", TokenInfo: nil" From d20473a1b79213b801a9cb86c48177b97d789317 Mon Sep 17 00:00:00 2001 From: Victor Castell <0x@vcastellm.xyz> Date: Fri, 8 Nov 2024 11:02:14 +0000 Subject: [PATCH 25/27] fix: update minter key --- test/bridge-e2e.bats | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/bridge-e2e.bats b/test/bridge-e2e.bats index d504c1c92..01411a114 100644 --- a/test/bridge-e2e.bats +++ b/test/bridge-e2e.bats @@ -76,6 +76,9 @@ setup() { local initial_receiver_balance=$(cast balance "$receiver" --rpc-url "$l2_rpc_url") echo "Initial receiver balance of native token on L2 $initial_receiver_balance" >&3 + local initial_mint_balance=$(cast balance "0x8943545177806ED17B9F23F0a21ee5948eCaa776" --rpc-url "$l1_rpc_url") + echo "Initial minter balance on L1 $initial_mint_balance" >&3 + # Query for initial sender balance run query_contract "$l1_rpc_url" "$gas_token_addr" "$balance_of_fn_sig" "$sender_addr" assert_success @@ -85,7 +88,7 @@ setup() { # Mint gas token on L1 local tokens_amount="0.1ether" local wei_amount=$(cast --to-unit $tokens_amount wei) - local minter_key=${MINTER_KEY:-"42b6e34dc21598a807dc19d7784c71b2a7a01f6480dc6f58258f78e539f1a1fa"} + local minter_key=${MINTER_KEY:-"bcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31"} run mint_erc20_tokens "$l1_rpc_url" "$gas_token_addr" "$minter_key" "$sender_addr" "$tokens_amount" assert_success From dcbd6075f9b1a88e768424d7f523956249d1d8fc Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:10:20 +0100 Subject: [PATCH 26/27] fix: e2e --- .github/workflows/test-resequence.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-resequence.yml b/.github/workflows/test-resequence.yml index 71ebc7d7e..db52680e5 100644 --- a/.github/workflows/test-resequence.yml +++ b/.github/workflows/test-resequence.yml @@ -92,7 +92,7 @@ jobs: run: | mkdir -p ci_logs cd ci_logs - kurtosis service logs cdk-v1 cdk-erigon-node-001 --all > cdk-erigon-node-001.log + kurtosis service logs cdk-v1 cdk-erigon-rpc-001 --all > cdk-erigon-node-001.log kurtosis service logs cdk-v1 cdk-erigon-sequencer-001 --all > cdk-erigon-sequencer-001.log kurtosis service logs cdk-v1 zkevm-agglayer-001 --all > zkevm-agglayer-001.log kurtosis service logs cdk-v1 zkevm-prover-001 --all > zkevm-prover-001.log From a80899afd293b97a3014c4da64ae5e72b68bac91 Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Fri, 8 Nov 2024 14:00:49 +0100 Subject: [PATCH 27/27] fix: e2e tests --- .github/workflows/test-resequence.yml | 2 +- test/scripts/batch_verification_monitor.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-resequence.yml b/.github/workflows/test-resequence.yml index db52680e5..23d73423b 100644 --- a/.github/workflows/test-resequence.yml +++ b/.github/workflows/test-resequence.yml @@ -92,7 +92,7 @@ jobs: run: | mkdir -p ci_logs cd ci_logs - kurtosis service logs cdk-v1 cdk-erigon-rpc-001 --all > cdk-erigon-node-001.log + kurtosis service logs cdk-v1 cdk-erigon-rpc-001 --all > cdk-erigon-rpc-001.log kurtosis service logs cdk-v1 cdk-erigon-sequencer-001 --all > cdk-erigon-sequencer-001.log kurtosis service logs cdk-v1 zkevm-agglayer-001 --all > zkevm-agglayer-001.log kurtosis service logs cdk-v1 zkevm-prover-001 --all > zkevm-prover-001.log diff --git a/test/scripts/batch_verification_monitor.sh b/test/scripts/batch_verification_monitor.sh index 9c9238885..a0bfaefdc 100755 --- a/test/scripts/batch_verification_monitor.sh +++ b/test/scripts/batch_verification_monitor.sh @@ -17,7 +17,7 @@ timeout="$2" start_time=$(date +%s) end_time=$((start_time + timeout)) -rpc_url="$(kurtosis port print cdk cdk-erigon-node-001 rpc)" +rpc_url="$(kurtosis port print cdk cdk-erigon-rpc-001 rpc)" while true; do verified_batches="$(cast to-dec "$(cast rpc --rpc-url "$rpc_url" zkevm_verifiedBatchNumber | sed 's/"//g')")"