mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-25 23:23:11 +00:00
Merge branch 'main' into Pravdyvy/indexer-state-management
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
name = "integration_tests"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
nssa_core = { workspace = true, features = ["host"] }
|
||||
@@ -13,6 +14,9 @@ common.workspace = true
|
||||
key_protocol.workspace = true
|
||||
indexer_service.workspace = true
|
||||
indexer_core.workspace = true
|
||||
wallet-ffi.workspace = true
|
||||
serde_json.workspace = true
|
||||
token_core.workspace = true
|
||||
|
||||
url.workspace = true
|
||||
anyhow.workspace = true
|
||||
|
||||
@@ -42,6 +42,7 @@ pub struct TestContext {
|
||||
wallet: WalletCore,
|
||||
_sequencer_handle: SequencerHandle,
|
||||
_indexer_handle: IndexerHandle,
|
||||
wallet_password: String,
|
||||
_temp_sequencer_dir: TempDir,
|
||||
_temp_wallet_dir: TempDir,
|
||||
}
|
||||
@@ -87,7 +88,7 @@ impl TestContext {
|
||||
format!("http://{sequencer_addr}")
|
||||
};
|
||||
|
||||
let (wallet, temp_wallet_dir) = Self::setup_wallet(sequencer_addr.clone())
|
||||
let (wallet, temp_wallet_dir, wallet_password) = Self::setup_wallet(sequencer_addr.clone())
|
||||
.await
|
||||
.context("Failed to setup wallet")?;
|
||||
|
||||
@@ -103,6 +104,7 @@ impl TestContext {
|
||||
Ok(Self {
|
||||
sequencer_client,
|
||||
wallet,
|
||||
wallet_password,
|
||||
_sequencer_handle,
|
||||
_indexer_handle,
|
||||
_temp_sequencer_dir: temp_sequencer_dir,
|
||||
@@ -130,7 +132,7 @@ impl TestContext {
|
||||
Ok((sequencer_handle, sequencer_addr, temp_sequencer_dir))
|
||||
}
|
||||
|
||||
async fn setup_wallet(sequencer_addr: String) -> Result<(WalletCore, TempDir)> {
|
||||
async fn setup_wallet(sequencer_addr: String) -> Result<(WalletCore, TempDir, String)> {
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let wallet_config_source_path =
|
||||
PathBuf::from(manifest_dir).join("configs/wallet/wallet_config.json");
|
||||
@@ -148,11 +150,12 @@ impl TestContext {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let wallet_password = "test_pass".to_owned();
|
||||
let wallet = WalletCore::new_init_storage(
|
||||
config_path,
|
||||
storage_path,
|
||||
Some(config_overrides),
|
||||
"test_pass".to_owned(),
|
||||
wallet_password.clone(),
|
||||
)
|
||||
.context("Failed to init wallet")?;
|
||||
wallet
|
||||
@@ -160,7 +163,7 @@ impl TestContext {
|
||||
.await
|
||||
.context("Failed to store wallet persistent data")?;
|
||||
|
||||
Ok((wallet, temp_wallet_dir))
|
||||
Ok((wallet, temp_wallet_dir, wallet_password))
|
||||
}
|
||||
|
||||
/// Get reference to the wallet.
|
||||
@@ -168,6 +171,10 @@ impl TestContext {
|
||||
&self.wallet
|
||||
}
|
||||
|
||||
pub fn wallet_password(&self) -> &str {
|
||||
&self.wallet_password
|
||||
}
|
||||
|
||||
/// Get mutable reference to the wallet.
|
||||
pub fn wallet_mut(&mut self) -> &mut WalletCore {
|
||||
&mut self.wallet
|
||||
@@ -179,6 +186,20 @@ impl TestContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// A test context to be used in normal #[test] tests
|
||||
pub struct BlockingTestContext {
|
||||
pub ctx: TestContext,
|
||||
pub runtime: tokio::runtime::Runtime,
|
||||
}
|
||||
|
||||
impl BlockingTestContext {
|
||||
pub fn new() -> Result<Self> {
|
||||
let runtime = tokio::runtime::Runtime::new().unwrap();
|
||||
let ctx = runtime.block_on(TestContext::new())?;
|
||||
Ok(Self { ctx, runtime })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_public_account_id(account_id: &str) -> String {
|
||||
format!("Public/{account_id}")
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use integration_tests::{
|
||||
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
||||
use log::info;
|
||||
use nssa::program::Program;
|
||||
use token_core::{TokenDefinition, TokenHolding};
|
||||
use tokio::test;
|
||||
use wallet::cli::{
|
||||
Command, SubcommandReturnValue,
|
||||
@@ -59,11 +60,13 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create new token
|
||||
let name = "A NAME".to_string();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(&definition_account_id.to_string()),
|
||||
supply_account_id: format_public_account_id(&supply_account_id.to_string()),
|
||||
name: "A NAME".to_string(),
|
||||
total_supply: 37,
|
||||
name: name.clone(),
|
||||
total_supply,
|
||||
};
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
|
||||
@@ -76,16 +79,16 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
.get_account(definition_account_id.to_string())
|
||||
.await?
|
||||
.account;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(definition_acc.program_owner, Program::token().id());
|
||||
// The data of a token definition account has the following layout:
|
||||
// [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) || metadata id (32 bytes)]
|
||||
assert_eq!(
|
||||
definition_acc.data.as_ref(),
|
||||
&[
|
||||
0, 65, 32, 78, 65, 77, 69, 37, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
token_definition,
|
||||
TokenDefinition::Fungible {
|
||||
name: name.clone(),
|
||||
total_supply,
|
||||
metadata_id: None
|
||||
}
|
||||
);
|
||||
|
||||
// Check the status of the token holding account with the total supply
|
||||
@@ -97,24 +100,23 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
|
||||
// The account must be owned by the token program
|
||||
assert_eq!(supply_acc.program_owner, Program::token().id());
|
||||
// The data of a token holding account has the following layout:
|
||||
// [ 0x01 || corresponding_token_definition_id (32 bytes) || balance (little endian 16 bytes) ]
|
||||
// First byte of the data equal to 1 means it's a token holding account
|
||||
assert_eq!(supply_acc.data.as_ref()[0], 1);
|
||||
// Bytes from 1 to 33 represent the id of the token this account is associated with
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
assert_eq!(
|
||||
&supply_acc.data.as_ref()[1..33],
|
||||
definition_account_id.to_bytes()
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: total_supply
|
||||
}
|
||||
);
|
||||
assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 37);
|
||||
|
||||
// Transfer 7 tokens from supply_acc to recipient_account_id
|
||||
let transfer_amount = 7;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||
from: format_public_account_id(&supply_account_id.to_string()),
|
||||
to: Some(format_public_account_id(&recipient_account_id.to_string())),
|
||||
to_npk: None,
|
||||
to_ipk: None,
|
||||
amount: 7,
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -129,9 +131,14 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
.await?
|
||||
.account;
|
||||
assert_eq!(supply_acc.program_owner, Program::token().id());
|
||||
assert_eq!(supply_acc.data[0], 1);
|
||||
assert_eq!(&supply_acc.data[1..33], definition_account_id.to_bytes());
|
||||
assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 30);
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: total_supply - transfer_amount
|
||||
}
|
||||
);
|
||||
|
||||
// Check the status of the recipient account after transfer
|
||||
let recipient_acc = ctx
|
||||
@@ -140,15 +147,21 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
.await?
|
||||
.account;
|
||||
assert_eq!(recipient_acc.program_owner, Program::token().id());
|
||||
assert_eq!(recipient_acc.data[0], 1);
|
||||
assert_eq!(&recipient_acc.data[1..33], definition_account_id.to_bytes());
|
||||
assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 7);
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: transfer_amount
|
||||
}
|
||||
);
|
||||
|
||||
// Burn 3 tokens from recipient_acc
|
||||
let burn_amount = 3;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Burn {
|
||||
definition: format_public_account_id(&definition_account_id.to_string()),
|
||||
holder: format_public_account_id(&recipient_account_id.to_string()),
|
||||
amount: 3,
|
||||
amount: burn_amount,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -162,13 +175,15 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
.get_account(definition_account_id.to_string())
|
||||
.await?
|
||||
.account;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
definition_acc.data.as_ref(),
|
||||
&[
|
||||
0, 65, 32, 78, 65, 77, 69, 34, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
token_definition,
|
||||
TokenDefinition::Fungible {
|
||||
name: name.clone(),
|
||||
total_supply: total_supply - burn_amount,
|
||||
metadata_id: None
|
||||
}
|
||||
);
|
||||
|
||||
// Check the status of the recipient account after burn
|
||||
@@ -177,16 +192,24 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
.get_account(recipient_account_id.to_string())
|
||||
.await?
|
||||
.account;
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
|
||||
assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 4);
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: transfer_amount - burn_amount
|
||||
}
|
||||
);
|
||||
|
||||
// Mint 10 tokens at recipient_acc
|
||||
let mint_amount = 10;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||
definition: format_public_account_id(&definition_account_id.to_string()),
|
||||
holder: Some(format_public_account_id(&recipient_account_id.to_string())),
|
||||
holder_npk: None,
|
||||
holder_ipk: None,
|
||||
amount: 10,
|
||||
amount: mint_amount,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -200,13 +223,15 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
.get_account(definition_account_id.to_string())
|
||||
.await?
|
||||
.account;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
definition_acc.data.as_ref(),
|
||||
&[
|
||||
0, 65, 32, 78, 65, 77, 69, 44, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
token_definition,
|
||||
TokenDefinition::Fungible {
|
||||
name,
|
||||
total_supply: total_supply - burn_amount + mint_amount,
|
||||
metadata_id: None
|
||||
}
|
||||
);
|
||||
|
||||
// Check the status of the recipient account after mint
|
||||
@@ -215,10 +240,14 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
.get_account(recipient_account_id.to_string())
|
||||
.await?
|
||||
.account;
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(recipient_acc.data[33..].try_into()?),
|
||||
14
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: transfer_amount - burn_amount + mint_amount
|
||||
}
|
||||
);
|
||||
|
||||
info!("Successfully created and transferred public token");
|
||||
@@ -270,11 +299,13 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create new token
|
||||
let name = "A NAME".to_string();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(&definition_account_id.to_string()),
|
||||
supply_account_id: format_private_account_id(&supply_account_id.to_string()),
|
||||
name: "A NAME".to_string(),
|
||||
total_supply: 37,
|
||||
name: name.clone(),
|
||||
total_supply,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -288,14 +319,16 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
.get_account(definition_account_id.to_string())
|
||||
.await?
|
||||
.account;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(definition_acc.program_owner, Program::token().id());
|
||||
assert_eq!(
|
||||
definition_acc.data.as_ref(),
|
||||
&[
|
||||
0, 65, 32, 78, 65, 77, 69, 37, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
token_definition,
|
||||
TokenDefinition::Fungible {
|
||||
name: name.clone(),
|
||||
total_supply,
|
||||
metadata_id: None
|
||||
}
|
||||
);
|
||||
|
||||
let new_commitment1 = ctx
|
||||
@@ -305,12 +338,13 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
assert!(verify_commitment_is_in_state(new_commitment1, ctx.sequencer_client()).await);
|
||||
|
||||
// Transfer 7 tokens from supply_acc to recipient_account_id
|
||||
let transfer_amount = 7;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||
from: format_private_account_id(&supply_account_id.to_string()),
|
||||
to: Some(format_private_account_id(&recipient_account_id.to_string())),
|
||||
to_npk: None,
|
||||
to_ipk: None,
|
||||
amount: 7,
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -331,10 +365,11 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
assert!(verify_commitment_is_in_state(new_commitment2, ctx.sequencer_client()).await);
|
||||
|
||||
// Burn 3 tokens from recipient_acc
|
||||
let burn_amount = 3;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Burn {
|
||||
definition: format_public_account_id(&definition_account_id.to_string()),
|
||||
holder: format_private_account_id(&recipient_account_id.to_string()),
|
||||
amount: 3,
|
||||
amount: burn_amount,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -348,13 +383,15 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
.get_account(definition_account_id.to_string())
|
||||
.await?
|
||||
.account;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
definition_acc.data.as_ref(),
|
||||
&[
|
||||
0, 65, 32, 78, 65, 77, 69, 34, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
token_definition,
|
||||
TokenDefinition::Fungible {
|
||||
name,
|
||||
total_supply: total_supply - burn_amount,
|
||||
metadata_id: None
|
||||
}
|
||||
);
|
||||
|
||||
let new_commitment2 = ctx
|
||||
@@ -368,10 +405,14 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
.wallet()
|
||||
.get_account_private(&recipient_account_id)
|
||||
.context("Failed to get recipient account")?;
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(recipient_acc.data[33..].try_into()?),
|
||||
4 // 7 - 3
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: transfer_amount - burn_amount
|
||||
}
|
||||
);
|
||||
|
||||
info!("Successfully created and transferred token with private supply");
|
||||
@@ -414,11 +455,13 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token with private definition
|
||||
let name = "A NAME".to_string();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_private_account_id(&definition_account_id.to_string()),
|
||||
supply_account_id: format_public_account_id(&supply_account_id.to_string()),
|
||||
name: "A NAME".to_string(),
|
||||
total_supply: 37,
|
||||
name: name.clone(),
|
||||
total_supply,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -441,8 +484,14 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
.account;
|
||||
|
||||
assert_eq!(supply_acc.program_owner, Program::token().id());
|
||||
assert_eq!(supply_acc.data.as_ref()[0], 1);
|
||||
assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 37);
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: total_supply
|
||||
}
|
||||
);
|
||||
|
||||
// Create private recipient account
|
||||
let result = wallet::cli::execute_subcommand(
|
||||
@@ -471,6 +520,7 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
};
|
||||
|
||||
// Mint to public account
|
||||
let mint_amount_public = 10;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||
definition: format_private_account_id(&definition_account_id.to_string()),
|
||||
holder: Some(format_public_account_id(
|
||||
@@ -478,7 +528,7 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
)),
|
||||
holder_npk: None,
|
||||
holder_ipk: None,
|
||||
amount: 10,
|
||||
amount: mint_amount_public,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -491,10 +541,15 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
.wallet()
|
||||
.get_account_private(&definition_account_id)
|
||||
.context("Failed to get definition account")?;
|
||||
let token_definition = TokenDefinition::try_from(&definition_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(definition_acc.data[7..23].try_into()?),
|
||||
47 // 37 + 10
|
||||
token_definition,
|
||||
TokenDefinition::Fungible {
|
||||
name: name.clone(),
|
||||
total_supply: total_supply + mint_amount_public,
|
||||
metadata_id: None
|
||||
}
|
||||
);
|
||||
|
||||
// Verify public recipient received tokens
|
||||
@@ -503,13 +558,18 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
.get_account(recipient_account_id_public.to_string())
|
||||
.await?
|
||||
.account;
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(recipient_acc.data[33..].try_into()?),
|
||||
10
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: mint_amount_public
|
||||
}
|
||||
);
|
||||
|
||||
// Mint to private account
|
||||
let mint_amount_private = 5;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||
definition: format_private_account_id(&definition_account_id.to_string()),
|
||||
holder: Some(format_private_account_id(
|
||||
@@ -517,7 +577,7 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
)),
|
||||
holder_npk: None,
|
||||
holder_ipk: None,
|
||||
amount: 5,
|
||||
amount: mint_amount_private,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -537,10 +597,14 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
.wallet()
|
||||
.get_account_private(&recipient_account_id_private)
|
||||
.context("Failed to get private recipient account")?;
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc_private.data)?;
|
||||
|
||||
assert_eq!(
|
||||
u128::from_le_bytes(recipient_acc_private.data[33..].try_into()?),
|
||||
5
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: mint_amount_private
|
||||
}
|
||||
);
|
||||
|
||||
info!("Successfully created token with private definition and minted to both account types");
|
||||
@@ -579,11 +643,13 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token with both private definition and supply
|
||||
let name = "A NAME".to_string();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_private_account_id(&definition_account_id.to_string()),
|
||||
supply_account_id: format_private_account_id(&supply_account_id.to_string()),
|
||||
name: "A NAME".to_string(),
|
||||
total_supply: 37,
|
||||
name,
|
||||
total_supply,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -610,8 +676,15 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
|
||||
.wallet()
|
||||
.get_account_private(&supply_account_id)
|
||||
.context("Failed to get supply account")?;
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
|
||||
assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 37);
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: total_supply
|
||||
}
|
||||
);
|
||||
|
||||
// Create recipient account
|
||||
let result = wallet::cli::execute_subcommand(
|
||||
@@ -627,12 +700,13 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
|
||||
};
|
||||
|
||||
// Transfer tokens
|
||||
let transfer_amount = 7;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||
from: format_private_account_id(&supply_account_id.to_string()),
|
||||
to: Some(format_private_account_id(&recipient_account_id.to_string())),
|
||||
to_npk: None,
|
||||
to_ipk: None,
|
||||
amount: 7,
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -658,13 +732,27 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
|
||||
.wallet()
|
||||
.get_account_private(&supply_account_id)
|
||||
.context("Failed to get supply account")?;
|
||||
assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 30);
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: total_supply - transfer_amount
|
||||
}
|
||||
);
|
||||
|
||||
let recipient_acc = ctx
|
||||
.wallet()
|
||||
.get_account_private(&recipient_account_id)
|
||||
.context("Failed to get recipient account")?;
|
||||
assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 7);
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: transfer_amount
|
||||
}
|
||||
);
|
||||
|
||||
info!("Successfully created and transferred token with both private definition and supply");
|
||||
|
||||
@@ -715,11 +803,13 @@ async fn shielded_token_transfer() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token
|
||||
let name = "A NAME".to_string();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(&definition_account_id.to_string()),
|
||||
supply_account_id: format_public_account_id(&supply_account_id.to_string()),
|
||||
name: "A NAME".to_string(),
|
||||
total_supply: 37,
|
||||
name,
|
||||
total_supply,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -728,12 +818,13 @@ async fn shielded_token_transfer() -> Result<()> {
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
// Perform shielded transfer: public supply -> private recipient
|
||||
let transfer_amount = 7;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||
from: format_public_account_id(&supply_account_id.to_string()),
|
||||
to: Some(format_private_account_id(&recipient_account_id.to_string())),
|
||||
to_npk: None,
|
||||
to_ipk: None,
|
||||
amount: 7,
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -747,7 +838,14 @@ async fn shielded_token_transfer() -> Result<()> {
|
||||
.get_account(supply_account_id.to_string())
|
||||
.await?
|
||||
.account;
|
||||
assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 30);
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: total_supply - transfer_amount
|
||||
}
|
||||
);
|
||||
|
||||
// Verify recipient commitment exists
|
||||
let new_commitment = ctx
|
||||
@@ -761,7 +859,14 @@ async fn shielded_token_transfer() -> Result<()> {
|
||||
.wallet()
|
||||
.get_account_private(&recipient_account_id)
|
||||
.context("Failed to get recipient account")?;
|
||||
assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 7);
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: transfer_amount
|
||||
}
|
||||
);
|
||||
|
||||
info!("Successfully performed shielded token transfer");
|
||||
|
||||
@@ -812,11 +917,13 @@ async fn deshielded_token_transfer() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token with private supply
|
||||
let name = "A NAME".to_string();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_public_account_id(&definition_account_id.to_string()),
|
||||
supply_account_id: format_private_account_id(&supply_account_id.to_string()),
|
||||
name: "A NAME".to_string(),
|
||||
total_supply: 37,
|
||||
name,
|
||||
total_supply,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -825,12 +932,13 @@ async fn deshielded_token_transfer() -> Result<()> {
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
// Perform deshielded transfer: private supply -> public recipient
|
||||
let transfer_amount = 7;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Send {
|
||||
from: format_private_account_id(&supply_account_id.to_string()),
|
||||
to: Some(format_public_account_id(&recipient_account_id.to_string())),
|
||||
to_npk: None,
|
||||
to_ipk: None,
|
||||
amount: 7,
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -850,7 +958,14 @@ async fn deshielded_token_transfer() -> Result<()> {
|
||||
.wallet()
|
||||
.get_account_private(&supply_account_id)
|
||||
.context("Failed to get supply account")?;
|
||||
assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 30);
|
||||
let token_holding = TokenHolding::try_from(&supply_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: total_supply - transfer_amount
|
||||
}
|
||||
);
|
||||
|
||||
// Verify recipient balance
|
||||
let recipient_acc = ctx
|
||||
@@ -858,7 +973,14 @@ async fn deshielded_token_transfer() -> Result<()> {
|
||||
.get_account(recipient_account_id.to_string())
|
||||
.await?
|
||||
.account;
|
||||
assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 7);
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: transfer_amount
|
||||
}
|
||||
);
|
||||
|
||||
info!("Successfully performed deshielded token transfer");
|
||||
|
||||
@@ -896,11 +1018,13 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
||||
};
|
||||
|
||||
// Create token
|
||||
let name = "A NAME".to_string();
|
||||
let total_supply = 37;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::New {
|
||||
definition_account_id: format_private_account_id(&definition_account_id.to_string()),
|
||||
supply_account_id: format_private_account_id(&supply_account_id.to_string()),
|
||||
name: "A NAME".to_string(),
|
||||
total_supply: 37,
|
||||
name,
|
||||
total_supply,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -931,12 +1055,13 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
||||
.context("Failed to get private account keys")?;
|
||||
|
||||
// Mint using claiming path (foreign account)
|
||||
let mint_amount = 9;
|
||||
let subcommand = TokenProgramAgnosticSubcommand::Mint {
|
||||
definition: format_private_account_id(&definition_account_id.to_string()),
|
||||
holder: None,
|
||||
holder_npk: Some(hex::encode(holder_keys.nullifer_public_key.0)),
|
||||
holder_ipk: Some(hex::encode(holder_keys.incoming_viewing_public_key.0)),
|
||||
amount: 9,
|
||||
amount: mint_amount,
|
||||
};
|
||||
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -960,7 +1085,14 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
||||
.wallet()
|
||||
.get_account_private(&recipient_account_id)
|
||||
.context("Failed to get recipient account")?;
|
||||
assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 9);
|
||||
let token_holding = TokenHolding::try_from(&recipient_acc.data)?;
|
||||
assert_eq!(
|
||||
token_holding,
|
||||
TokenHolding::Fungible {
|
||||
definition_id: definition_account_id,
|
||||
balance: mint_amount
|
||||
}
|
||||
);
|
||||
|
||||
info!("Successfully minted tokens using claiming path");
|
||||
|
||||
|
||||
@@ -0,0 +1,618 @@
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
ffi::{CStr, CString, c_char},
|
||||
io::Write,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use integration_tests::{
|
||||
ACC_RECEIVER, ACC_SENDER, ACC_SENDER_PRIVATE, BlockingTestContext,
|
||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS,
|
||||
};
|
||||
use log::info;
|
||||
use nssa::{Account, AccountId, PublicKey, program::Program};
|
||||
use nssa_core::program::DEFAULT_PROGRAM_ID;
|
||||
use tempfile::tempdir;
|
||||
use wallet::WalletCore;
|
||||
use wallet_ffi::{
|
||||
FfiAccount, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys, FfiPublicAccountKey,
|
||||
FfiTransferResult, WalletHandle, error,
|
||||
};
|
||||
|
||||
unsafe extern "C" {
|
||||
fn wallet_ffi_create_new(
|
||||
config_path: *const c_char,
|
||||
storage_path: *const c_char,
|
||||
password: *const c_char,
|
||||
) -> *mut WalletHandle;
|
||||
|
||||
fn wallet_ffi_destroy(handle: *mut WalletHandle);
|
||||
|
||||
fn wallet_ffi_create_account_public(
|
||||
handle: *mut WalletHandle,
|
||||
out_account_id: *mut FfiBytes32,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_create_account_private(
|
||||
handle: *mut WalletHandle,
|
||||
out_account_id: *mut FfiBytes32,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_list_accounts(
|
||||
handle: *mut WalletHandle,
|
||||
out_list: *mut FfiAccountList,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_free_account_list(list: *mut FfiAccountList);
|
||||
|
||||
fn wallet_ffi_get_balance(
|
||||
handle: *mut WalletHandle,
|
||||
account_id: *const FfiBytes32,
|
||||
is_public: bool,
|
||||
out_balance: *mut [u8; 16],
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_get_account_public(
|
||||
handle: *mut WalletHandle,
|
||||
account_id: *const FfiBytes32,
|
||||
out_account: *mut FfiAccount,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_free_account_data(account: *mut FfiAccount);
|
||||
|
||||
fn wallet_ffi_get_public_account_key(
|
||||
handle: *mut WalletHandle,
|
||||
account_id: *const FfiBytes32,
|
||||
out_public_key: *mut FfiPublicAccountKey,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_get_private_account_keys(
|
||||
handle: *mut WalletHandle,
|
||||
account_id: *const FfiBytes32,
|
||||
out_keys: *mut FfiPrivateAccountKeys,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_free_private_account_keys(keys: *mut FfiPrivateAccountKeys);
|
||||
|
||||
fn wallet_ffi_account_id_to_base58(account_id: *const FfiBytes32) -> *mut std::ffi::c_char;
|
||||
|
||||
fn wallet_ffi_free_string(ptr: *mut c_char);
|
||||
|
||||
fn wallet_ffi_account_id_from_base58(
|
||||
base58_str: *const std::ffi::c_char,
|
||||
out_account_id: *mut FfiBytes32,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_transfer_public(
|
||||
handle: *mut WalletHandle,
|
||||
from: *const FfiBytes32,
|
||||
to: *const FfiBytes32,
|
||||
amount: *const [u8; 16],
|
||||
out_result: *mut FfiTransferResult,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_free_transfer_result(result: *mut FfiTransferResult);
|
||||
|
||||
fn wallet_ffi_register_public_account(
|
||||
handle: *mut WalletHandle,
|
||||
account_id: *const FfiBytes32,
|
||||
out_result: *mut FfiTransferResult,
|
||||
) -> error::WalletFfiError;
|
||||
}
|
||||
|
||||
fn new_wallet_ffi_with_test_context_config(ctx: &BlockingTestContext) -> *mut WalletHandle {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let config_path = tempdir.path().join("wallet_config.json");
|
||||
let storage_path = tempdir.path().join("storage.json");
|
||||
let mut config = ctx.ctx.wallet().config().to_owned();
|
||||
if let Some(config_overrides) = ctx.ctx.wallet().config_overrides().clone() {
|
||||
config.apply_overrides(config_overrides);
|
||||
}
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&config_path)
|
||||
.unwrap();
|
||||
|
||||
let config_with_overrides_serialized = serde_json::to_vec_pretty(&config).unwrap();
|
||||
|
||||
file.write_all(&config_with_overrides_serialized).unwrap();
|
||||
|
||||
let config_path = CString::new(config_path.to_str().unwrap()).unwrap();
|
||||
let storage_path = CString::new(storage_path.to_str().unwrap()).unwrap();
|
||||
let password = CString::new(ctx.ctx.wallet_password()).unwrap();
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_create_new(
|
||||
config_path.as_ptr(),
|
||||
storage_path.as_ptr(),
|
||||
password.as_ptr(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn new_wallet_ffi_with_default_config(password: &str) -> *mut WalletHandle {
|
||||
let tempdir = tempdir().unwrap();
|
||||
let config_path = tempdir.path().join("wallet_config.json");
|
||||
let storage_path = tempdir.path().join("storage.json");
|
||||
let config_path_c = CString::new(config_path.to_str().unwrap()).unwrap();
|
||||
let storage_path_c = CString::new(storage_path.to_str().unwrap()).unwrap();
|
||||
let password = CString::new(password).unwrap();
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_create_new(
|
||||
config_path_c.as_ptr(),
|
||||
storage_path_c.as_ptr(),
|
||||
password.as_ptr(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn new_wallet_rust_with_default_config(password: &str) -> WalletCore {
|
||||
let tempdir = tempdir().unwrap();
|
||||
let config_path = tempdir.path().join("wallet_config.json");
|
||||
let storage_path = tempdir.path().join("storage.json");
|
||||
|
||||
WalletCore::new_init_storage(
|
||||
config_path.to_path_buf(),
|
||||
storage_path.to_path_buf(),
|
||||
None,
|
||||
password.to_string(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_create_public_accounts() {
|
||||
let password = "password_for_tests";
|
||||
let n_accounts = 10;
|
||||
// First `n_accounts` public accounts created with Rust wallet
|
||||
let new_public_account_ids_rust = {
|
||||
let mut account_ids = Vec::new();
|
||||
|
||||
let mut wallet_rust = new_wallet_rust_with_default_config(password);
|
||||
for _ in 0..n_accounts {
|
||||
let account_id = wallet_rust.create_new_account_public(None).0;
|
||||
account_ids.push(*account_id.value());
|
||||
}
|
||||
account_ids
|
||||
};
|
||||
|
||||
// First `n_accounts` public accounts created with wallet FFI
|
||||
let new_public_account_ids_ffi = unsafe {
|
||||
let mut account_ids = Vec::new();
|
||||
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_default_config(password);
|
||||
for _ in 0..n_accounts {
|
||||
let mut out_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
wallet_ffi_create_account_public(
|
||||
wallet_ffi_handle,
|
||||
(&mut out_account_id) as *mut FfiBytes32,
|
||||
);
|
||||
account_ids.push(out_account_id.data);
|
||||
}
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
account_ids
|
||||
};
|
||||
|
||||
assert_eq!(new_public_account_ids_ffi, new_public_account_ids_rust);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_create_private_accounts() {
|
||||
let password = "password_for_tests";
|
||||
let n_accounts = 10;
|
||||
// First `n_accounts` private accounts created with Rust wallet
|
||||
let new_private_account_ids_rust = {
|
||||
let mut account_ids = Vec::new();
|
||||
|
||||
let mut wallet_rust = new_wallet_rust_with_default_config(password);
|
||||
for _ in 0..n_accounts {
|
||||
let account_id = wallet_rust.create_new_account_private(None).0;
|
||||
account_ids.push(*account_id.value());
|
||||
}
|
||||
account_ids
|
||||
};
|
||||
|
||||
// First `n_accounts` private accounts created with wallet FFI
|
||||
let new_private_account_ids_ffi = unsafe {
|
||||
let mut account_ids = Vec::new();
|
||||
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_default_config(password);
|
||||
for _ in 0..n_accounts {
|
||||
let mut out_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
wallet_ffi_create_account_private(
|
||||
wallet_ffi_handle,
|
||||
(&mut out_account_id) as *mut FfiBytes32,
|
||||
);
|
||||
account_ids.push(out_account_id.data);
|
||||
}
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
account_ids
|
||||
};
|
||||
|
||||
assert_eq!(new_private_account_ids_ffi, new_private_account_ids_rust)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_list_accounts() {
|
||||
let password = "password_for_tests";
|
||||
|
||||
// Create the wallet FFI
|
||||
let wallet_ffi_handle = unsafe {
|
||||
let handle = new_wallet_ffi_with_default_config(password);
|
||||
// Create 5 public accounts and 5 private accounts
|
||||
for _ in 0..5 {
|
||||
let mut out_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
wallet_ffi_create_account_public(handle, (&mut out_account_id) as *mut FfiBytes32);
|
||||
wallet_ffi_create_account_private(handle, (&mut out_account_id) as *mut FfiBytes32);
|
||||
}
|
||||
|
||||
handle
|
||||
};
|
||||
|
||||
// Create the wallet Rust
|
||||
let wallet_rust = {
|
||||
let mut wallet = new_wallet_rust_with_default_config(password);
|
||||
// Create 5 public accounts and 5 private accounts
|
||||
for _ in 0..5 {
|
||||
wallet.create_new_account_public(None);
|
||||
wallet.create_new_account_private(None);
|
||||
}
|
||||
wallet
|
||||
};
|
||||
|
||||
// Get the account list with FFI method
|
||||
let mut wallet_ffi_account_list = unsafe {
|
||||
let mut out_list = FfiAccountList::default();
|
||||
wallet_ffi_list_accounts(wallet_ffi_handle, (&mut out_list) as *mut FfiAccountList);
|
||||
out_list
|
||||
};
|
||||
|
||||
let wallet_rust_account_ids = wallet_rust
|
||||
.storage()
|
||||
.user_data
|
||||
.account_ids()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Assert same number of elements between Rust and FFI result
|
||||
assert_eq!(wallet_rust_account_ids.len(), wallet_ffi_account_list.count);
|
||||
|
||||
let wallet_ffi_account_list_slice = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
wallet_ffi_account_list.entries,
|
||||
wallet_ffi_account_list.count,
|
||||
)
|
||||
};
|
||||
|
||||
// Assert same account ids between Rust and FFI result
|
||||
assert_eq!(
|
||||
wallet_rust_account_ids
|
||||
.iter()
|
||||
.map(|id| id.value())
|
||||
.collect::<HashSet<_>>(),
|
||||
wallet_ffi_account_list_slice
|
||||
.iter()
|
||||
.map(|entry| &entry.account_id.data)
|
||||
.collect::<HashSet<_>>()
|
||||
);
|
||||
|
||||
// Assert `is_pub` flag is correct in the FFI result
|
||||
for entry in wallet_ffi_account_list_slice.iter() {
|
||||
let account_id = AccountId::new(entry.account_id.data);
|
||||
let is_pub_default_in_rust_wallet = wallet_rust
|
||||
.storage()
|
||||
.user_data
|
||||
.default_pub_account_signing_keys
|
||||
.contains_key(&account_id);
|
||||
let is_pub_key_tree_wallet_rust = wallet_rust
|
||||
.storage()
|
||||
.user_data
|
||||
.public_key_tree
|
||||
.account_id_map
|
||||
.contains_key(&account_id);
|
||||
|
||||
let is_public_in_rust_wallet = is_pub_default_in_rust_wallet || is_pub_key_tree_wallet_rust;
|
||||
|
||||
assert_eq!(entry.is_public, is_public_in_rust_wallet);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_account_list((&mut wallet_ffi_account_list) as *mut FfiAccountList);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_get_balance_public() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let account_id: AccountId = ACC_SENDER.parse().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
|
||||
let balance = unsafe {
|
||||
let mut out_balance: [u8; 16] = [0; 16];
|
||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
||||
let _result = wallet_ffi_get_balance(
|
||||
wallet_ffi_handle,
|
||||
(&ffi_account_id) as *const FfiBytes32,
|
||||
true,
|
||||
(&mut out_balance) as *mut [u8; 16],
|
||||
);
|
||||
u128::from_le_bytes(out_balance)
|
||||
};
|
||||
assert_eq!(balance, 10000);
|
||||
|
||||
info!("Successfully retrieved account balance");
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_get_account_public() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let account_id: AccountId = ACC_SENDER.parse().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let mut out_account = FfiAccount::default();
|
||||
|
||||
let account: Account = unsafe {
|
||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
||||
let _result = wallet_ffi_get_account_public(
|
||||
wallet_ffi_handle,
|
||||
(&ffi_account_id) as *const FfiBytes32,
|
||||
(&mut out_account) as *mut FfiAccount,
|
||||
);
|
||||
(&out_account).try_into().unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
account.program_owner,
|
||||
Program::authenticated_transfer_program().id()
|
||||
);
|
||||
assert_eq!(account.balance, 10000);
|
||||
assert!(account.data.is_empty());
|
||||
assert_eq!(account.nonce, 0);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_account_data((&mut out_account) as *mut FfiAccount);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
info!("Successfully retrieved account with correct details");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_get_public_account_keys() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let account_id: AccountId = ACC_SENDER.parse().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let mut out_key = FfiPublicAccountKey::default();
|
||||
|
||||
let key: PublicKey = unsafe {
|
||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
||||
let _result = wallet_ffi_get_public_account_key(
|
||||
wallet_ffi_handle,
|
||||
(&ffi_account_id) as *const FfiBytes32,
|
||||
(&mut out_key) as *mut FfiPublicAccountKey,
|
||||
);
|
||||
(&out_key).try_into().unwrap()
|
||||
};
|
||||
|
||||
let expected_key = {
|
||||
let private_key = ctx
|
||||
.ctx
|
||||
.wallet()
|
||||
.get_account_public_signing_key(&account_id)
|
||||
.unwrap();
|
||||
PublicKey::new_from_private_key(private_key)
|
||||
};
|
||||
|
||||
assert_eq!(key, expected_key);
|
||||
|
||||
info!("Successfully retrieved account key");
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_get_private_account_keys() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let account_id: AccountId = ACC_SENDER_PRIVATE.parse().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let mut keys = FfiPrivateAccountKeys::default();
|
||||
|
||||
unsafe {
|
||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
||||
let _result = wallet_ffi_get_private_account_keys(
|
||||
wallet_ffi_handle,
|
||||
(&ffi_account_id) as *const FfiBytes32,
|
||||
(&mut keys) as *mut FfiPrivateAccountKeys,
|
||||
);
|
||||
};
|
||||
|
||||
let key_chain = &ctx
|
||||
.ctx
|
||||
.wallet()
|
||||
.storage()
|
||||
.user_data
|
||||
.get_private_account(&account_id)
|
||||
.unwrap()
|
||||
.0;
|
||||
|
||||
let expected_npk = &key_chain.nullifer_public_key;
|
||||
let expected_ivk = &key_chain.incoming_viewing_public_key;
|
||||
|
||||
assert_eq!(&keys.npk(), expected_npk);
|
||||
assert_eq!(&keys.ivk().unwrap(), expected_ivk);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_private_account_keys((&mut keys) as *mut FfiPrivateAccountKeys);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
info!("Successfully retrieved account keys");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_account_id_to_base58() {
|
||||
let account_id_str = ACC_SENDER;
|
||||
let account_id: AccountId = account_id_str.parse().unwrap();
|
||||
let ffi_bytes: FfiBytes32 = (&account_id).into();
|
||||
let ptr = unsafe { wallet_ffi_account_id_to_base58((&ffi_bytes) as *const FfiBytes32) };
|
||||
|
||||
let ffi_result = unsafe { CStr::from_ptr(ptr).to_str().unwrap() };
|
||||
|
||||
assert_eq!(account_id_str, ffi_result);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_string(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_base58_to_account_id() {
|
||||
let account_id_str = ACC_SENDER;
|
||||
let account_id_c_str = CString::new(account_id_str).unwrap();
|
||||
let account_id: AccountId = unsafe {
|
||||
let mut out_account_id_bytes = FfiBytes32::default();
|
||||
wallet_ffi_account_id_from_base58(
|
||||
account_id_c_str.as_ptr(),
|
||||
(&mut out_account_id_bytes) as *mut FfiBytes32,
|
||||
);
|
||||
out_account_id_bytes.into()
|
||||
};
|
||||
|
||||
let expected_account_id = account_id_str.parse().unwrap();
|
||||
|
||||
assert_eq!(account_id, expected_account_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_init_public_account_auth_transfer() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
|
||||
// Create a new uninitialized public account
|
||||
let mut out_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
unsafe {
|
||||
wallet_ffi_create_account_public(
|
||||
wallet_ffi_handle,
|
||||
(&mut out_account_id) as *mut FfiBytes32,
|
||||
);
|
||||
}
|
||||
|
||||
// Check its program owner is the default program id
|
||||
let account: Account = unsafe {
|
||||
let mut out_account = FfiAccount::default();
|
||||
let _result = wallet_ffi_get_account_public(
|
||||
wallet_ffi_handle,
|
||||
(&out_account_id) as *const FfiBytes32,
|
||||
(&mut out_account) as *mut FfiAccount,
|
||||
);
|
||||
(&out_account).try_into().unwrap()
|
||||
};
|
||||
assert_eq!(account.program_owner, DEFAULT_PROGRAM_ID);
|
||||
|
||||
// Call the init funciton
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
wallet_ffi_register_public_account(
|
||||
wallet_ffi_handle,
|
||||
(&out_account_id) as *const FfiBytes32,
|
||||
(&mut transfer_result) as *mut FfiTransferResult,
|
||||
);
|
||||
}
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
|
||||
|
||||
// Check that the program owner is now the authenticated transfer program
|
||||
let account: Account = unsafe {
|
||||
let mut out_account = FfiAccount::default();
|
||||
let _result = wallet_ffi_get_account_public(
|
||||
wallet_ffi_handle,
|
||||
(&out_account_id) as *const FfiBytes32,
|
||||
(&mut out_account) as *mut FfiAccount,
|
||||
);
|
||||
(&out_account).try_into().unwrap()
|
||||
};
|
||||
assert_eq!(
|
||||
account.program_owner,
|
||||
Program::authenticated_transfer_program().id()
|
||||
);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_transfer_result((&mut transfer_result) as *mut FfiTransferResult);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_transfer_public() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let from: FfiBytes32 = (&ACC_SENDER.parse::<AccountId>().unwrap()).into();
|
||||
let to: FfiBytes32 = (&ACC_RECEIVER.parse::<AccountId>().unwrap()).into();
|
||||
let amount: [u8; 16] = 100u128.to_le_bytes();
|
||||
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
wallet_ffi_transfer_public(
|
||||
wallet_ffi_handle,
|
||||
(&from) as *const FfiBytes32,
|
||||
(&to) as *const FfiBytes32,
|
||||
(&amount) as *const [u8; 16],
|
||||
(&mut transfer_result) as *mut FfiTransferResult,
|
||||
);
|
||||
}
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
|
||||
|
||||
let from_balance = unsafe {
|
||||
let mut out_balance: [u8; 16] = [0; 16];
|
||||
let _result = wallet_ffi_get_balance(
|
||||
wallet_ffi_handle,
|
||||
(&from) as *const FfiBytes32,
|
||||
true,
|
||||
(&mut out_balance) as *mut [u8; 16],
|
||||
);
|
||||
u128::from_le_bytes(out_balance)
|
||||
};
|
||||
|
||||
let to_balance = unsafe {
|
||||
let mut out_balance: [u8; 16] = [0; 16];
|
||||
let _result = wallet_ffi_get_balance(
|
||||
wallet_ffi_handle,
|
||||
(&to) as *const FfiBytes32,
|
||||
true,
|
||||
(&mut out_balance) as *mut [u8; 16],
|
||||
);
|
||||
u128::from_le_bytes(out_balance)
|
||||
};
|
||||
|
||||
assert_eq!(from_balance, 9900);
|
||||
assert_eq!(to_balance, 20100);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_transfer_result((&mut transfer_result) as *mut FfiTransferResult);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user