From 4d36d80feed7e64c16dce199068c3f3908dda456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 27 Dec 2024 18:51:14 +0200 Subject: [PATCH 1/4] multi-transfer: max_tx_batch_block_duration Modified from u64::MAX to 100_000_000u64 due to overflow --- multi-transfer-esdt/src/lib.rs | 2 +- .../tests/multi_transfer_blackbox_test.rs | 164 ++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) diff --git a/multi-transfer-esdt/src/lib.rs b/multi-transfer-esdt/src/lib.rs index b07689f3..51410838 100644 --- a/multi-transfer-esdt/src/lib.rs +++ b/multi-transfer-esdt/src/lib.rs @@ -7,7 +7,7 @@ use sc_proxies::{bridge_proxy_contract_proxy, bridged_tokens_wrapper_proxy, esdt use transaction::{EthTransaction, PaymentsVec, Transaction, TxNonce}; const DEFAULT_MAX_TX_BATCH_SIZE: usize = 10; -const DEFAULT_MAX_TX_BATCH_BLOCK_DURATION: u64 = u64::MAX; +const DEFAULT_MAX_TX_BATCH_BLOCK_DURATION: u64 = 100_000_000u64; const CHAIN_SPECIFIC_TO_UNIVERSAL_TOKEN_MAPPING: &[u8] = b"chainSpecificToUniversalMapping"; #[multiversx_sc::contract] diff --git a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs index 5e193701..1c484968 100644 --- a/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs +++ b/multi-transfer-esdt/tests/multi_transfer_blackbox_test.rs @@ -91,6 +91,7 @@ const MAX_AMOUNT: u64 = 100_000_000_000_000u64; const BALANCE: &str = "2,000,000"; const ERROR: u64 = 4; const MINTED_AMOUNT: u64 = 100_000_000_000; +const DEFAULT_MAX_TX_BATCH_BLOCK_DURATION: u64 = 100_000_000u64; fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); @@ -1371,3 +1372,166 @@ fn batch_transfer_esdt_token_to_address_zero() { .batch_transfer_esdt_token(1u32, transfers) .run(); } + +#[test] +fn multi_transfer_reduce_max_tx_batch_size_test() { + let mut state: MultiTransferTestState = MultiTransferTestState::new(); + + state.multisig_deploy(); + state.multi_transfer_deploy(); + state.bridge_proxy_deploy(); + state.safe_deploy_real_contract(); + state.bridged_tokens_wrapper_deploy(); + state.config_multi_transfer(); + + state.world.set_esdt_balance( + MULTISIG_ADDRESS, + b"TOKEN-WITH", + BigUint::from(10_000_000u64), + ); + + let eth_tx = EthTransaction { + from: EthAddress::zero(), + to: ManagedAddress::zero(), + token_id: TokenIdentifier::from(TOKEN_TICKER), + amount: BigUint::from(MAX_AMOUNT), + tx_nonce: 1u64, + call_data: ManagedOption::none(), + }; + + let mut transfers: MultiValueEncoded> = + MultiValueEncoded::new(); + transfers.push(eth_tx.clone()); + + // Batch size is default 10 + state + .world + .tx() + .from(MULTISIG_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .batch_transfer_esdt_token(1u32, transfers.clone()) + .run(); + + state + .world + .tx() + .from(MULTISIG_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .batch_transfer_esdt_token(1u32, transfers.clone()) + .run(); + + state + .world + .tx() + .from(MULTISIG_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .batch_transfer_esdt_token(1u32, transfers.clone()) + .run(); + + state + .world + .tx() + .from(MULTISIG_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .batch_transfer_esdt_token(1u32, transfers.clone()) + .run(); + + let batch_status = state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .get_batch_status(1u64) + .returns(ReturnsResult) + .run(); + + assert_eq!( + batch_status, + BatchStatus::PartiallyFull { + end_block_nonce: DEFAULT_MAX_TX_BATCH_BLOCK_DURATION, + tx_ids: ManagedVec::from(vec![1u64, 1u64, 1u64, 1u64]) + }, + "Incorrect batch status" + ); + + let new_max_batch_status = 2usize; + state + .world + .tx() + .from(MULTISIG_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .set_max_tx_batch_size(new_max_batch_status) + .run(); + + //get_batch_status + let batch_id = 1u64; + let batch_status = state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .get_batch_status(batch_id) + .returns(ReturnsResult) + .run(); + + assert_eq!( + batch_status, + BatchStatus::WaitingForSignatures, + "Incorrect batch status" + ); + + state + .world + .tx() + .from(MULTISIG_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .batch_transfer_esdt_token(batch_id, transfers) + .run(); + + //First batch should be full + let batch_id = 1u64; + let batch_status = state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .get_batch_status(batch_id) + .returns(ReturnsResult) + .run(); + + assert_eq!( + batch_status, + BatchStatus::WaitingForSignatures, + "Incorrect batch status" + ); + + //A new batch should be created + let batch_id = 2u64; + let batch_status = state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(MULTI_TRANSFER_ADDRESS) + .typed(multi_transfer_esdt_proxy::MultiTransferEsdtProxy) + .get_batch_status(batch_id) + .returns(ReturnsResult) + .run(); + + assert_eq!( + batch_status, + BatchStatus::PartiallyFull { + end_block_nonce: DEFAULT_MAX_TX_BATCH_BLOCK_DURATION, + tx_ids: ManagedVec::from(vec![1u64]) + }, + "Incorrect batch status" + ); +} From e30ae97043282c5187855e009d18c98278684958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 27 Dec 2024 19:01:54 +0200 Subject: [PATCH 2/4] multi-transfer: fix tests --- multi-transfer-esdt/scenarios/setup_accounts.scen.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-transfer-esdt/scenarios/setup_accounts.scen.json b/multi-transfer-esdt/scenarios/setup_accounts.scen.json index 5454dccb..8f1f1f93 100644 --- a/multi-transfer-esdt/scenarios/setup_accounts.scen.json +++ b/multi-transfer-esdt/scenarios/setup_accounts.scen.json @@ -190,7 +190,7 @@ "str:firstBatchId": "1", "str:lastBatchId": "1", "str:maxTxBatchSize": "10", - "str:maxTxBatchBlockDuration": "0xffffffffffffffff" + "str:maxTxBatchBlockDuration": "100_000_000" } }, "sc:bridge-proxy": { From eb61af10a1b74075b83dc63c858987faf16b4421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 8 Jan 2025 09:38:14 +0200 Subject: [PATCH 3/4] esdt-safe: add unit tests --- esdt-safe/tests/esdt_safe_blackbox_test.rs | 161 +++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/esdt-safe/tests/esdt_safe_blackbox_test.rs b/esdt-safe/tests/esdt_safe_blackbox_test.rs index c3e3e06e..8ad025f1 100644 --- a/esdt-safe/tests/esdt_safe_blackbox_test.rs +++ b/esdt-safe/tests/esdt_safe_blackbox_test.rs @@ -1343,3 +1343,164 @@ fn withdraw_transaction_fees_test() { "accumulated_transaction_fees should be zero after withdrawal" ); } + +#[test] +fn esdt_safe_reduce_max_tx_batch_size_test() { + let mut state = EsdtSafeTestState::new(); + state.multisig_deploy(); + state.safe_deploy(); + + state.world.set_esdt_balance( + MULTISIG_ADDRESS, + b"TOKEN-WITH", + BigUint::from(10_000_000u64), + ); + + state.config_esdtsafe(); + + // Batch size is default 10 + state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .create_transaction( + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + OptionalValue::>::None, + ) + .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) + .run(); + + state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .create_transaction( + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + OptionalValue::>::None, + ) + .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) + .run(); + + state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .create_transaction( + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + OptionalValue::>::None, + ) + .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) + .run(); + + state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .create_transaction( + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + OptionalValue::>::None, + ) + .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) + .run(); + + //get_batch_status + let batch_id = 1u64; + let batch_status = state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .get_batch_status(batch_id) + .returns(ReturnsResult) + .run(); + + assert_eq!( + batch_status, + BatchStatus::PartiallyFull { + end_block_nonce: 100, + tx_ids: ManagedVec::from(vec![1u64, 2u64, 3u64, 4u64]) + }, + "Incorrect batch status" + ); + + let new_max_batch_status = 2usize; + state + .world + .tx() + .from(MULTISIG_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .set_max_tx_batch_size(new_max_batch_status) + .run(); + + state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .create_transaction( + EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + OptionalValue::>::None, + ) + .single_esdt(&TOKEN_ID.into(), 0, &BigUint::from(10u64)) + .run(); + + //First batch should be full + let batch_id = 1u64; + let batch_status = state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .get_batch_status(batch_id) + .returns(ReturnsResult) + .run(); + + assert_eq!( + batch_status, + BatchStatus::WaitingForSignatures, + "Incorrect batch status" + ); + + //A new batch should be created + let batch_id = 2u64; + let batch_status = state + .world + .tx() + .from(BRIDGED_TOKENS_WRAPPER_ADDRESS) + .to(ESDT_SAFE_ADDRESS) + .typed(esdt_safe_proxy::EsdtSafeProxy) + .get_batch_status(batch_id) + .returns(ReturnsResult) + .run(); + + assert_eq!( + batch_status, + BatchStatus::PartiallyFull { + end_block_nonce: 100, + tx_ids: ManagedVec::from(vec![5u64]) + }, + "Incorrect batch status" + ); +} From 479dff9f8af9f2ad174e3a042ab99cf643a9eb6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 8 Jan 2025 15:26:49 +0200 Subject: [PATCH 4/4] Add unit tests for bridge-proxy --- .../tests/bridge_proxy_blackbox_test.rs | 175 +++++++++++++++++- 1 file changed, 174 insertions(+), 1 deletion(-) diff --git a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs index b0087c2b..96174b23 100644 --- a/bridge-proxy/tests/bridge_proxy_blackbox_test.rs +++ b/bridge-proxy/tests/bridge_proxy_blackbox_test.rs @@ -8,7 +8,7 @@ use bridge_proxy::config::ProxyTrait as _; use crowdfunding_esdt::crowdfunding_esdt_proxy; use multiversx_sc::codec::NestedEncode; use multiversx_sc::contract_base::ManagedSerializer; -use multiversx_sc::sc_print; +use multiversx_sc::imports::MultiValue2; use multiversx_sc::types::{ EgldOrEsdtTokenIdentifier, EsdtTokenPayment, ManagedOption, MultiValueEncoded, ReturnsNewAddress, ReturnsResult, TestAddress, TestSCAddress, TestTokenIdentifier, @@ -25,6 +25,7 @@ use multiversx_sc::{ ManagedByteArray, ManagedVec, TokenIdentifier, }, }; +use multiversx_sc::{require, sc_print}; use multiversx_sc_scenario::imports::MxscPath; use multiversx_sc_scenario::{ api::StaticApi, @@ -1105,3 +1106,175 @@ fn bridge_proxy_refund_tx_test() { .check_account(BRIDGE_PROXY_ADDRESS) .check_storage("str:highestTxId", "1"); } + +#[test] +fn bridge_proxy_double_execute_same_tx_test() { + let mut test = BridgeProxyTestState::new(); + test.multisig_deploy(); + + test.deploy_bridge_proxy(); + test.deploy_crowdfunding(); + test.config_bridge(); + + let mut args = ManagedVec::new(); + + let call_data: CallData = CallData { + endpoint: ManagedBuffer::from("fund"), + gas_limit: GAS_LIMIT, + args: ManagedOption::some(args), + }; + + let call_data: ManagedBuffer = + ManagedSerializer::new().top_encode_to_managed_buffer(&call_data); + + let eth_tx = EthTransaction { + from: EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + to: ManagedAddress::from(CROWDFUNDING_ADDRESS.eval_to_array()), + token_id: BRIDGE_TOKEN_ID.into(), + amount: BigUint::from(500u64), + tx_nonce: 1u64, + call_data: ManagedOption::some(call_data), + }; + + test.world + .tx() + .from(MULTI_TRANSFER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .deposit(ð_tx, 1u64) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(BRIDGE_TOKEN_ID), + 0, + &BigUint::from(500u64), + ) + .run(); + + test.world + .query() + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .get_pending_transaction_by_id(1u32) + .returns(ExpectValue(eth_tx)) + .run(); + + test.world + .tx() + .from(OWNER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .gas(200_000_000) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .execute(1u32) + .run(); + + test.world + .query() + .to(CROWDFUNDING_ADDRESS) + .typed(crowdfunding_esdt_proxy::CrowdfundingProxy) + .get_current_funds() + .returns(ExpectValue(500u64)) + .run(); + + test.world + .tx() + .from(OWNER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .gas(200_000_000) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .execute(1u32) + .returns(ExpectError(4, "Invalid tx id")) + .run(); +} + +#[test] +fn bridge_proxy_execute_1000_even_tx_test() { + let mut test = BridgeProxyTestState::new(); + test.multisig_deploy(); + + test.deploy_bridge_proxy(); + test.deploy_crowdfunding(); + test.config_bridge(); + + let mut args = ManagedVec::new(); + + let call_data: CallData = CallData { + endpoint: ManagedBuffer::from("fund"), + gas_limit: GAS_LIMIT, + args: ManagedOption::some(args), + }; + + let call_data: ManagedBuffer = + ManagedSerializer::new().top_encode_to_managed_buffer(&call_data); + + let eth_tx = EthTransaction { + from: EthAddress { + raw_addr: ManagedByteArray::new_from_bytes(b"01020304050607080910"), + }, + to: ManagedAddress::from(CROWDFUNDING_ADDRESS.eval_to_array()), + token_id: BRIDGE_TOKEN_ID.into(), + amount: BigUint::from(5u64), + tx_nonce: 1u64, + call_data: ManagedOption::some(call_data), + }; + + for _ in 1..101 { + test.world + .tx() + .from(MULTI_TRANSFER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .deposit(ð_tx, 1u64) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(BRIDGE_TOKEN_ID), + 0, + &BigUint::from(5u64), + ) + .run(); + } + + for i in 1..101u32 { + test.world + .query() + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .get_pending_transaction_by_id(i) + .returns(ExpectValue(eth_tx.clone())) + .run(); + } + + for i in 1..101u32 { + if i % 2 == 1 { + continue; + } + test.world + .tx() + .from(OWNER_ADDRESS) + .to(BRIDGE_PROXY_ADDRESS) + .gas(200_000_000) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .execute(i) + .run(); + } + test.world + .query() + .to(CROWDFUNDING_ADDRESS) + .typed(crowdfunding_esdt_proxy::CrowdfundingProxy) + .get_current_funds() + .returns(ExpectValue(250u64)) + .run(); + + let pending_txs = test + .world + .query() + .to(BRIDGE_PROXY_ADDRESS) + .typed(bridge_proxy_contract_proxy::BridgeProxyContractProxy) + .get_pending_transactions() + .returns(ReturnsResult) + .run(); + + for tx in pending_txs { + let (tx_id, actual_tx) = tx.into_tuple(); + assert!(tx_id % 2 == 1); + } +}