From 27f4042d8fceb757f73754755968c73b0cd3904b Mon Sep 17 00:00:00 2001 From: karim-en Date: Tue, 2 Jan 2024 14:04:40 +0000 Subject: [PATCH 1/8] Add `min_proof_acceptance_height` --- eth-connector/src/connector.rs | 2 +- eth-connector/src/connector_impl.rs | 10 ++++++++-- eth-connector/src/lib.rs | 4 ++++ eth-connector/src/proof.rs | 4 ++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/eth-connector/src/connector.rs b/eth-connector/src/connector.rs index b2db616..1bcd710 100644 --- a/eth-connector/src/connector.rs +++ b/eth-connector/src/connector.rs @@ -34,7 +34,7 @@ pub trait FundsFinish { #[ext_contract(ext_proof_verifier)] pub trait ProofVerifier { #[result_serializer(borsh)] - fn verify_log_entry(&self, #[serializer(borsh)] args: VerifyProofArgs) -> bool; + fn verify_log_entry_in_bound(&self, #[serializer(borsh)] args: VerifyProofArgs) -> bool; } /// Withdraw method for legacy implementation in Engine diff --git a/eth-connector/src/connector_impl.rs b/eth-connector/src/connector_impl.rs index e939352..94f3b16 100644 --- a/eth-connector/src/connector_impl.rs +++ b/eth-connector/src/connector_impl.rs @@ -3,7 +3,7 @@ use crate::{ connector::{ext_funds_finish, ext_proof_verifier}, deposit_event::{DepositedEvent, TokenMessageData}, errors, log, panic_err, - proof::Proof, + proof::{Proof, VerifyProofArgs}, types::SdkUnwrap, AdminControlled, PausedMask, }; @@ -58,6 +58,9 @@ pub struct EthConnector { pub account_with_access_right: AccountId, /// Owner's account id. pub owner_id: AccountId, + /// Proofs from blocks that are below the acceptance height will be rejected. + // If `minBlockAcceptanceHeight` value is zero - proofs from block with any height are accepted. + pub min_proof_acceptance_height: u64, } impl AdminControlled for EthConnector { @@ -160,9 +163,12 @@ impl EthConnector { } }; + let mut verify_log_args: VerifyProofArgs = proof.into(); + verify_log_args.min_header_height = Some(self.min_proof_acceptance_height); + ext_proof_verifier::ext(self.prover_account.clone()) .with_static_gas(GAS_FOR_VERIFY_LOG_ENTRY) - .verify_log_entry(proof.into()) + .verify_log_entry_in_bound(verify_log_args) .then( ext_funds_finish::ext(current_account_id) .with_static_gas(GAS_FOR_FINISH_DEPOSIT) diff --git a/eth-connector/src/lib.rs b/eth-connector/src/lib.rs index da27a65..2ba3a29 100644 --- a/eth-connector/src/lib.rs +++ b/eth-connector/src/lib.rs @@ -166,6 +166,7 @@ impl EthConnectorContract { metadata: &FungibleTokenMetadata, account_with_access_right: AccountId, owner_id: &AccountId, + min_proof_acceptance_height: u64, ) -> Self { metadata.assert_valid(); @@ -177,6 +178,7 @@ impl EthConnectorContract { eth_custodian_address, account_with_access_right, owner_id: owner_id.clone(), + min_proof_acceptance_height, }; let mut this = Self { ft: FungibleToken { @@ -721,12 +723,14 @@ mod tests { }; let account_with_access_right = "engine.near".parse().unwrap(); let owner_id = "owner.near".parse().unwrap(); + let min_proof_acceptance_height = 1; EthConnectorContract::new( prover_account, eth_custodian_address, &metadata, account_with_access_right, &owner_id, + min_proof_acceptance_height, ) } } diff --git a/eth-connector/src/proof.rs b/eth-connector/src/proof.rs index d586b73..00b90a6 100644 --- a/eth-connector/src/proof.rs +++ b/eth-connector/src/proof.rs @@ -42,6 +42,8 @@ pub struct VerifyProofArgs { pub receipt_data: Vec, pub header_data: Vec, pub proof: Vec>, + pub min_header_height: Option, + pub max_header_height: Option, pub skip_bridge_call: bool, } @@ -54,6 +56,8 @@ impl From for VerifyProofArgs { receipt_data: value.receipt_data, header_data: value.header_data, proof: value.proof, + min_header_height: None, + max_header_height: None, skip_bridge_call: false, } } From 6aaca2698e72db671dd87d971f455ec2920fc69d Mon Sep 17 00:00:00 2001 From: karim-en Date: Tue, 2 Jan 2024 14:12:57 +0000 Subject: [PATCH 2/8] Fix tests --- eth-connector/src/admin_controlled.rs | 1 + eth-connector/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/eth-connector/src/admin_controlled.rs b/eth-connector/src/admin_controlled.rs index 4138cdb..659ae36 100644 --- a/eth-connector/src/admin_controlled.rs +++ b/eth-connector/src/admin_controlled.rs @@ -104,6 +104,7 @@ fn test_pause_control() { paused_mask: UNPAUSE_ALL, account_with_access_right: "aurora".parse().unwrap(), owner_id: "aurora".parse().unwrap(), + min_proof_acceptance_height: 0, }; assert!(connector.assert_not_paused(PAUSE_DEPOSIT).is_ok()); diff --git a/eth-connector/src/lib.rs b/eth-connector/src/lib.rs index 2ba3a29..ed3146a 100644 --- a/eth-connector/src/lib.rs +++ b/eth-connector/src/lib.rs @@ -723,7 +723,7 @@ mod tests { }; let account_with_access_right = "engine.near".parse().unwrap(); let owner_id = "owner.near".parse().unwrap(); - let min_proof_acceptance_height = 1; + let min_proof_acceptance_height = 0; EthConnectorContract::new( prover_account, eth_custodian_address, From ce2ffff47c3d325da9fbd4f7db2029b79c55b8f7 Mon Sep 17 00:00:00 2001 From: karim-en Date: Tue, 2 Jan 2024 14:22:55 +0000 Subject: [PATCH 3/8] Fix tests --- eth-connector-tests/src/utils.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eth-connector-tests/src/utils.rs b/eth-connector-tests/src/utils.rs index f82bfd6..f2c7e6e 100644 --- a/eth-connector-tests/src/utils.rs +++ b/eth-connector-tests/src/utils.rs @@ -40,6 +40,7 @@ impl TestContract { let prover_account = contract.id(); let metadata = Self::metadata_default(); let account_with_access_right: AccountId = CONTRACT_ACC.parse().unwrap(); + let min_proof_acceptance_height = 0; // Init eth-connector let res = contract .init( @@ -48,6 +49,7 @@ impl TestContract { metadata, &account_with_access_right, &owner_id, + min_proof_acceptance_height, ) .transact() .await?; From a250fd6a01c20b1add8f0f975e185d5eeb539d8f Mon Sep 17 00:00:00 2001 From: karim-en Date: Tue, 2 Jan 2024 16:29:15 +0000 Subject: [PATCH 4/8] Update `aurora-workspace` deps --- Cargo.lock | 4 ++-- eth-connector-tests/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 71b7095..cd88c53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -318,7 +318,7 @@ dependencies = [ [[package]] name = "aurora-workspace-eth-connector" version = "0.4.1" -source = "git+https://github.com/aurora-is-near/aurora-workspace.git?tag=0.4.1#dbed3551cbfd17f760a58e042c6f4c7f7f9a9a20" +source = "git+https://github.com/aurora-is-near/aurora-workspace.git?rev=16d6263d6561cbc1f838016ae15416eadc88ec90#16d6263d6561cbc1f838016ae15416eadc88ec90" dependencies = [ "anyhow", "aurora-engine-types", @@ -333,7 +333,7 @@ dependencies = [ [[package]] name = "aurora-workspace-utils" version = "0.4.1" -source = "git+https://github.com/aurora-is-near/aurora-workspace.git?tag=0.4.1#dbed3551cbfd17f760a58e042c6f4c7f7f9a9a20" +source = "git+https://github.com/aurora-is-near/aurora-workspace.git?rev=16d6263d6561cbc1f838016ae15416eadc88ec90#16d6263d6561cbc1f838016ae15416eadc88ec90" dependencies = [ "anyhow", "aurora-engine-types", diff --git a/eth-connector-tests/Cargo.toml b/eth-connector-tests/Cargo.toml index a075cda..905f6ea 100644 --- a/eth-connector-tests/Cargo.toml +++ b/eth-connector-tests/Cargo.toml @@ -28,8 +28,8 @@ hex = "0.4.3" ethabi = "18.0" rlp = { version = "0.5.0", default-features = false } aurora-engine-migration-tool = { git = "https://github.com/aurora-is-near/aurora-engine-migration-tool.git", tag = "0.2.2" } -aurora-workspace-eth-connector = { git = "https://github.com/aurora-is-near/aurora-workspace.git", tag = "0.4.1" } -aurora-workspace-utils = { git = "https://github.com/aurora-is-near/aurora-workspace.git", tag = "0.4.1" } +aurora-workspace-eth-connector = { git = "https://github.com/aurora-is-near/aurora-workspace.git", rev = "16d6263d6561cbc1f838016ae15416eadc88ec90" } +aurora-workspace-utils = { git = "https://github.com/aurora-is-near/aurora-workspace.git", rev = "16d6263d6561cbc1f838016ae15416eadc88ec90" } [features] migration-tests = [] From d9d98fbdd39d9d4023908a08abc4ba3b8be87a61 Mon Sep 17 00:00:00 2001 From: karim-en Date: Tue, 2 Jan 2024 18:31:27 +0000 Subject: [PATCH 5/8] Add tests for `min_proof_acceptance_height` --- eth-connector-tests/Cargo.toml | 2 +- eth-connector-tests/src/connector.rs | 40 +++++++++++++++++++++++++--- eth-connector-tests/src/utils.rs | 20 ++++++++++++-- eth-connector/src/lib.rs | 7 ++++- eth-connector/src/proof.rs | 6 +++++ 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/eth-connector-tests/Cargo.toml b/eth-connector-tests/Cargo.toml index 905f6ea..5e33993 100644 --- a/eth-connector-tests/Cargo.toml +++ b/eth-connector-tests/Cargo.toml @@ -13,7 +13,7 @@ publish = false autobenches = false [dev-dependencies] -aurora-eth-connector = { path = "../eth-connector" } +aurora-eth-connector = { path = "../eth-connector", features = ["integration-test"] } aurora-engine-types = { workspace = true, features = ["impl-serde"] } near-sdk.workspace = true near-primitives.workspace = true diff --git a/eth-connector-tests/src/connector.rs b/eth-connector-tests/src/connector.rs index c66dc60..480981d 100644 --- a/eth-connector-tests/src/connector.rs +++ b/eth-connector-tests/src/connector.rs @@ -776,6 +776,38 @@ async fn test_admin_controlled_admin_can_perform_actions_when_paused() { assert_eq!(data.eth_custodian_address, custodian_addr); } +#[tokio::test] +async fn test_deposit_with_proof_lower_than_acceptance_height() { + let min_proof_acceptance_height = 1000; + let contract = TestContract::new_with_options( + CUSTODIAN_ADDRESS, + "owner.root", + min_proof_acceptance_height, + ) + .await + .unwrap(); + + // Should fail + let proof_header_height = min_proof_acceptance_height - 1; + let user_account = contract.contract_account("eth_recipient").await.unwrap(); + let proof = contract.mock_proof(user_account.id(), 10, 1, proof_header_height); + let res = contract + .user_deposit_with_proof(&user_account, proof) + .await + .unwrap_err(); + assert!(contract.check_error_message(&res, "ERR_VERIFY_PROOF")); + + // Should succeed + let proof_header_height = min_proof_acceptance_height; + let user_account = contract.contract_account("eth_recipient").await.unwrap(); + let proof = contract.mock_proof(user_account.id(), 10, 1, proof_header_height); + let res = contract + .user_deposit_with_proof(&user_account, proof) + .await + .unwrap(); + assert!(res.is_success()); +} + #[tokio::test] async fn test_deposit_pausability() { use aurora_eth_connector::admin_controlled::{PAUSE_DEPOSIT, UNPAUSE_ALL}; @@ -793,7 +825,7 @@ async fn test_deposit_pausability() { .unwrap(); // 1st deposit call - should succeed - let proof1 = contract.mock_proof(user_acc.id(), 10, 1); + let proof1 = contract.mock_proof(user_acc.id(), 10, 1, 0); let res = contract .user_deposit_with_proof(&user_acc, proof1) .await @@ -811,14 +843,14 @@ async fn test_deposit_pausability() { assert!(res.is_success()); // 2nd deposit call - should fail for `user_acc` - let proof2 = contract.mock_proof(user_acc.id(), 20, 2); + let proof2 = contract.mock_proof(user_acc.id(), 20, 2, 0); let res = contract .user_deposit_with_proof(&user_acc, proof2) .await .unwrap_err(); assert!(contract.check_error_message(&res, "ERR_PAUSED")); - let proof3 = contract.mock_proof(user_acc.id(), 30, 3); + let proof3 = contract.mock_proof(user_acc.id(), 30, 3, 0); let res = contract .user_deposit_with_proof(&owner_acc, proof3) .await @@ -836,7 +868,7 @@ async fn test_deposit_pausability() { assert!(res.is_success()); // 3rd deposit call - should succeed - let proof4 = contract.mock_proof(user_acc.id(), 40, 4); + let proof4 = contract.mock_proof(user_acc.id(), 40, 4, 0); let res = contract .user_deposit_with_proof(&user_acc, proof4) .await diff --git a/eth-connector-tests/src/utils.rs b/eth-connector-tests/src/utils.rs index f2c7e6e..0af29cc 100644 --- a/eth-connector-tests/src/utils.rs +++ b/eth-connector-tests/src/utils.rs @@ -33,6 +33,14 @@ impl TestContract { pub async fn new_with_custodian_and_owner( eth_custodian_address: &str, owner_id: &str, + ) -> anyhow::Result { + Self::new_with_options(eth_custodian_address, owner_id, 0).await + } + + pub async fn new_with_options( + eth_custodian_address: &str, + owner_id: &str, + min_proof_acceptance_height: u64, ) -> anyhow::Result { let (contract, root_account) = Self::deploy_eth_connector().await?; let owner_id: AccountId = owner_id.parse().unwrap(); @@ -40,7 +48,6 @@ impl TestContract { let prover_account = contract.id(); let metadata = Self::metadata_default(); let account_with_access_right: AccountId = CONTRACT_ACC.parse().unwrap(); - let min_proof_acceptance_height = 0; // Init eth-connector let res = contract .init( @@ -236,6 +243,7 @@ impl TestContract { recipient_id: &AccountId, deposit_amount: u128, proof_index: u64, + header_height: u64, ) -> Proof { use aurora_engine_types::{ types::{Fee, NEP141Wei}, @@ -279,17 +287,25 @@ impl TestContract { ethabi::Token::Uint(U256::from(fee.as_u128())), ]), }; + // The borsh is used instead of rlp to simplify the mock logic + let header_data = + near_sdk::borsh::BorshSerialize::try_to_vec(&aurora_eth_connector::proof::MockHeader { + height: header_height, + }) + .unwrap(); + Proof { log_index: proof_index, // Only this field matters for the purpose of this test log_entry_data: rlp::encode(&log_entry).to_vec(), receipt_index: 1, + header_data, ..Default::default() } } pub async fn call_deposit_contract(&self) -> anyhow::Result<()> { - let proof = self.mock_proof(self.contract.id(), DEPOSITED_CONTRACT, 1); + let proof = self.mock_proof(self.contract.id(), DEPOSITED_CONTRACT, 1, 0); let res = self.deposit_with_proof(&proof).await?; assert!(res.is_success(), "call_deposit_contract: {res:#?}"); Ok(()) diff --git a/eth-connector/src/lib.rs b/eth-connector/src/lib.rs index ed3146a..e395b1f 100644 --- a/eth-connector/src/lib.rs +++ b/eth-connector/src/lib.rs @@ -206,8 +206,13 @@ impl EthConnectorContract { #[result_serializer(borsh)] #[must_use] #[allow(unused_variables)] - pub fn verify_log_entry(#[serializer(borsh)] proof_args: &VerifyProofArgs) -> bool { + pub fn verify_log_entry(&self, #[serializer(borsh)] proof_args: &VerifyProofArgs) -> bool { log!("Call from verify_log_entry"); + if let Ok(header) = proof::MockHeader::try_from_slice(&proof_args.header_data) { + if header.height < self.connector.min_proof_acceptance_height { + return false; + } + } true } diff --git a/eth-connector/src/proof.rs b/eth-connector/src/proof.rs index 00b90a6..5ea9fb7 100644 --- a/eth-connector/src/proof.rs +++ b/eth-connector/src/proof.rs @@ -63,6 +63,12 @@ impl From for VerifyProofArgs { } } +#[cfg(feature = "integration-test")] +#[derive(BorshDeserialize, BorshSerialize, Default, Debug)] +pub struct MockHeader { + pub height: u64, +} + #[cfg(test)] mod tests { use super::Proof; From b7a816931a16d6d2c734e420667dbfc04ef4aa23 Mon Sep 17 00:00:00 2001 From: karim-en Date: Tue, 2 Jan 2024 19:47:35 +0000 Subject: [PATCH 6/8] Fix mocked method --- eth-connector/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth-connector/src/lib.rs b/eth-connector/src/lib.rs index e395b1f..22f0ef0 100644 --- a/eth-connector/src/lib.rs +++ b/eth-connector/src/lib.rs @@ -206,8 +206,8 @@ impl EthConnectorContract { #[result_serializer(borsh)] #[must_use] #[allow(unused_variables)] - pub fn verify_log_entry(&self, #[serializer(borsh)] proof_args: &VerifyProofArgs) -> bool { - log!("Call from verify_log_entry"); + pub fn verify_log_entry_in_bound(&self, #[serializer(borsh)] proof_args: &VerifyProofArgs) -> bool { + log!("Call from verify_log_entry_in_bound"); if let Ok(header) = proof::MockHeader::try_from_slice(&proof_args.header_data) { if header.height < self.connector.min_proof_acceptance_height { return false; From 67224761d7261712d4f2d00fb43160949b96cafc Mon Sep 17 00:00:00 2001 From: karim-en Date: Tue, 2 Jan 2024 20:31:18 +0000 Subject: [PATCH 7/8] Fix test --- eth-connector-tests/src/connector.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/eth-connector-tests/src/connector.rs b/eth-connector-tests/src/connector.rs index 480981d..25232c5 100644 --- a/eth-connector-tests/src/connector.rs +++ b/eth-connector-tests/src/connector.rs @@ -799,7 +799,6 @@ async fn test_deposit_with_proof_lower_than_acceptance_height() { // Should succeed let proof_header_height = min_proof_acceptance_height; - let user_account = contract.contract_account("eth_recipient").await.unwrap(); let proof = contract.mock_proof(user_account.id(), 10, 1, proof_header_height); let res = contract .user_deposit_with_proof(&user_account, proof) From 2faa7f68939b5e6f78f97c9dea079669e3909a2c Mon Sep 17 00:00:00 2001 From: karim-en Date: Tue, 2 Jan 2024 20:34:44 +0000 Subject: [PATCH 8/8] Fix fmt --- eth-connector/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/eth-connector/src/lib.rs b/eth-connector/src/lib.rs index 22f0ef0..970ffa6 100644 --- a/eth-connector/src/lib.rs +++ b/eth-connector/src/lib.rs @@ -206,7 +206,10 @@ impl EthConnectorContract { #[result_serializer(borsh)] #[must_use] #[allow(unused_variables)] - pub fn verify_log_entry_in_bound(&self, #[serializer(borsh)] proof_args: &VerifyProofArgs) -> bool { + pub fn verify_log_entry_in_bound( + &self, + #[serializer(borsh)] proof_args: &VerifyProofArgs, + ) -> bool { log!("Call from verify_log_entry_in_bound"); if let Ok(header) = proof::MockHeader::try_from_slice(&proof_args.header_data) { if header.height < self.connector.min_proof_acceptance_height {