mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-18 05:59:33 +00:00
refactored
This commit is contained in:
parent
c2dad4b602
commit
a216234a95
@ -57,6 +57,7 @@ async fn fund_private_pda(
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id, true))
|
||||
.context("failed to serialize pda_fund_spend_proxy fund instruction")?,
|
||||
proxy_program,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||
@ -93,6 +94,7 @@ async fn spend_private_pda(
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id, false))
|
||||
.context("failed to serialize pda_fund_spend_proxy instruction")?,
|
||||
spend_program,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||
|
||||
@ -100,29 +100,38 @@ wallet token mint \
|
||||
--amount 0
|
||||
echo "LEZ holding initialized for keycard path 6"
|
||||
|
||||
# Keycard path 7: LEE holding
|
||||
# Keycard path 7: LEE holding (different definition — safe to submit immediately)
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/4" \
|
||||
--holder "m/44'/60'/0'/0/7" \
|
||||
--amount 0
|
||||
echo "LEE holding initialized for keycard path 7"
|
||||
|
||||
# pub-receiver: public LEZ holding (for token transfer test)
|
||||
# Wait for path2 (LEZ def) and path4 (LEE def) nonces to be confirmed before reusing them
|
||||
sleep 15
|
||||
|
||||
# pub-receiver: public LEZ holding
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/2" \
|
||||
--holder pub-receiver \
|
||||
--amount 0
|
||||
echo "LEZ holding initialized for pub-receiver"
|
||||
|
||||
# AMM seed accounts
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/2" \
|
||||
--holder amm-lez-fund \
|
||||
--amount 0
|
||||
# amm-lee-fund: LEE holding (different definition — safe to submit with pub-receiver)
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/4" \
|
||||
--holder amm-lee-fund \
|
||||
--amount 0
|
||||
echo "LEE holding initialized for amm-lee-fund"
|
||||
|
||||
# Wait for path2 nonce to be confirmed before the third LEZ mint
|
||||
sleep 15
|
||||
|
||||
# amm-lez-fund: LEZ holding
|
||||
wallet token mint \
|
||||
--definition "m/44'/60'/0'/0/2" \
|
||||
--holder amm-lez-fund \
|
||||
--amount 0
|
||||
echo "AMM seed holdings initialized"
|
||||
|
||||
# =============================================================================
|
||||
@ -143,6 +152,9 @@ wallet token send \
|
||||
--amount 20000
|
||||
echo "Transferred 20000 LEE → keycard path 7"
|
||||
|
||||
# Wait for path3 and path5 nonces to be confirmed before reusing them
|
||||
sleep 15
|
||||
|
||||
wallet token send \
|
||||
--from "m/44'/60'/0'/0/3" \
|
||||
--to amm-lez-fund \
|
||||
@ -292,19 +304,6 @@ wallet account get --account-id "m/44'/60'/0'/0/7"
|
||||
# =============================================================================
|
||||
# (9) Add liquidity — keycard accounts for holding A (path 6), B (path 7), LP (path 8)
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo "=== (9) Initialize LP holding (keycard path 8) before add-liquidity ==="
|
||||
wallet token mint \
|
||||
--definition "Public/$LP_DEF_ID" \
|
||||
--holder "m/44'/60'/0'/0/8" \
|
||||
--amount 0
|
||||
echo "Keycard path 8 (LP holding) initialized"
|
||||
|
||||
sleep 15
|
||||
|
||||
echo "Keycard path 8 (LP holding) state (after init):"
|
||||
wallet account get --account-id "m/44'/60'/0'/0/8"
|
||||
|
||||
echo ""
|
||||
echo "=== (9) Add liquidity (keycard path 6=LEZ, path 7=LEE, path 8=LP) ==="
|
||||
wallet amm add-liquidity \
|
||||
|
||||
@ -3,11 +3,14 @@ use std::path::PathBuf;
|
||||
use nssa::{AccountId, PublicKey, Signature};
|
||||
use nssa_core::NullifierPublicKey;
|
||||
use pyo3::{prelude::*, types::PyAny};
|
||||
use zeroize::Zeroizing;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
pub mod python_path;
|
||||
|
||||
/// NSK and VSK as fixed-length zeroizing byte arrays.
|
||||
type PrivateKeyPair = (Zeroizing<[u8; 32]>, Zeroizing<[u8; 32]>);
|
||||
|
||||
// TODO: encrypt at rest alongside broader wallet storage encryption work.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct KeycardPairingData {
|
||||
@ -140,6 +143,10 @@ impl KeycardWallet {
|
||||
})
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "64 - s_stripped.len() is safe: s_stripped.len() ≤ 31 because py_signature.len() is in [32, 63]"
|
||||
)]
|
||||
pub fn sign_message_for_path(
|
||||
&self,
|
||||
py: Python,
|
||||
@ -152,12 +159,19 @@ impl KeycardWallet {
|
||||
.call_method1("sign_message_for_path", (message, path))?
|
||||
.extract()?;
|
||||
|
||||
// The keycard Python library strips the leading zero from the S component when
|
||||
// S < 2^248. Re-insert it so the slice is always the expected 64 bytes (R || S).
|
||||
let py_signature = if py_signature.len() == 63 {
|
||||
// The keycard Python library strips leading zeros from S when S < 2^(8k) for some k.
|
||||
// Left-pad S back to 32 bytes so the full signature is always 64 bytes (R || S).
|
||||
let py_signature = if py_signature.len() < 64 {
|
||||
if py_signature.len() < 32 {
|
||||
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
|
||||
"signature from keycard too short: {} bytes",
|
||||
py_signature.len()
|
||||
)));
|
||||
}
|
||||
let s_stripped = &py_signature[32..];
|
||||
let mut padded = [0_u8; 64];
|
||||
padded[..32].copy_from_slice(&py_signature[..32]);
|
||||
padded[33..].copy_from_slice(&py_signature[32..]);
|
||||
padded[(64 - s_stripped.len())..].copy_from_slice(s_stripped);
|
||||
padded.to_vec()
|
||||
} else {
|
||||
py_signature
|
||||
@ -212,11 +226,7 @@ impl KeycardWallet {
|
||||
Ok(format!("Public/{}", AccountId::from(&public_key)))
|
||||
}
|
||||
|
||||
pub fn get_private_keys_for_path(
|
||||
&self,
|
||||
py: Python,
|
||||
path: &str,
|
||||
) -> PyResult<(Zeroizing<[u8; 32]>, Zeroizing<[u8; 32]>)> {
|
||||
pub fn get_private_keys_for_path(&self, py: Python, path: &str) -> PyResult<PrivateKeyPair> {
|
||||
let (raw_nsk, raw_vsk): (Vec<u8>, Vec<u8>) = self
|
||||
.instance
|
||||
.bind(py)
|
||||
@ -233,7 +243,7 @@ impl KeycardWallet {
|
||||
raw_nsk.len()
|
||||
)));
|
||||
}
|
||||
let mut arr = Zeroizing::new([0u8; 32]);
|
||||
let mut arr = Zeroizing::new([0_u8; 32]);
|
||||
arr.copy_from_slice(&raw_nsk);
|
||||
arr
|
||||
};
|
||||
@ -245,7 +255,7 @@ impl KeycardWallet {
|
||||
raw_vsk.len()
|
||||
)));
|
||||
}
|
||||
let mut arr = Zeroizing::new([0u8; 32]);
|
||||
let mut arr = Zeroizing::new([0_u8; 32]);
|
||||
arr.copy_from_slice(&raw_vsk);
|
||||
arr
|
||||
};
|
||||
@ -256,7 +266,7 @@ impl KeycardWallet {
|
||||
pub fn get_private_keys_for_path_with_connect(
|
||||
pin: &str,
|
||||
path: &str,
|
||||
) -> PyResult<(Zeroizing<[u8; 32]>, Zeroizing<[u8; 32]>)> {
|
||||
) -> PyResult<PrivateKeyPair> {
|
||||
Python::with_gil(|py| {
|
||||
python_path::add_python_path(py)?;
|
||||
|
||||
|
||||
@ -89,7 +89,7 @@ impl PrivateAccountKind {
|
||||
/// Borsh layout (all integers little-endian, variant index is u8):
|
||||
///
|
||||
/// ```text
|
||||
/// Regular(ident): 0x00 || ident (16 LE) || [0u8; 64]
|
||||
/// Regular(ident): 0x00 || ident (16 LE) || [0_u8; 64]
|
||||
/// Pda { program_id, seed, ident }: 0x01 || program_id (32) || seed (32) || ident (16 LE)
|
||||
/// ```
|
||||
///
|
||||
|
||||
@ -208,7 +208,7 @@ pub mod tests {
|
||||
let nonces_bytes: &[u8] = &[1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
// all remaining vec fields are empty: u32 len=0
|
||||
let empty_vec_bytes: &[u8] = &[0_u8; 4];
|
||||
// validity windows: unbounded = {from: None (0u8), to: None (0u8)}
|
||||
// validity windows: unbounded = {from: None (0_u8), to: None (0_u8)}
|
||||
let unbounded_window_bytes: &[u8] = &[0_u8; 2];
|
||||
|
||||
let expected_borsh_vec: Vec<u8> = [
|
||||
|
||||
@ -188,7 +188,10 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded(
|
||||
};
|
||||
let to_identifier = u128::from_le_bytes(unsafe { (*to_identifier).data });
|
||||
let amount = u128::from_le_bytes(unsafe { *amount });
|
||||
let key_path = optional_c_str(key_path);
|
||||
let from_mention = optional_c_str(key_path).map_or_else(
|
||||
|| CliAccountMention::Id(AccountIdWithPrivacy::Public(from_id)),
|
||||
CliAccountMention::KeyPath,
|
||||
);
|
||||
|
||||
let transfer = NativeTokenTransfer(&wallet);
|
||||
|
||||
@ -198,7 +201,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded(
|
||||
to_vpk,
|
||||
to_identifier,
|
||||
amount,
|
||||
&key_path,
|
||||
&from_mention,
|
||||
)) {
|
||||
Ok((tx_hash, _shared_key)) => {
|
||||
let tx_hash = CString::new(tx_hash.to_string())
|
||||
@ -466,11 +469,14 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded_owned(
|
||||
let from_id = AccountId::new(unsafe { (*from).data });
|
||||
let to_id = AccountId::new(unsafe { (*to).data });
|
||||
let amount = u128::from_le_bytes(unsafe { *amount });
|
||||
let key_path = optional_c_str(key_path);
|
||||
let from_mention = optional_c_str(key_path).map_or_else(
|
||||
|| CliAccountMention::Id(AccountIdWithPrivacy::Public(from_id)),
|
||||
CliAccountMention::KeyPath,
|
||||
);
|
||||
|
||||
let transfer = NativeTokenTransfer(&wallet);
|
||||
|
||||
match block_on(transfer.send_shielded_transfer(from_id, to_id, amount, &key_path)) {
|
||||
match block_on(transfer.send_shielded_transfer(from_id, to_id, amount, &from_mention)) {
|
||||
Ok((tx_hash, _shared_key)) => {
|
||||
let tx_hash = CString::new(tx_hash.to_string())
|
||||
.map_or(ptr::null_mut(), std::ffi::CString::into_raw);
|
||||
|
||||
@ -159,8 +159,8 @@ impl WalletSubcommand for KeycardSubcommand {
|
||||
let (nsk, vsk) =
|
||||
KeycardWallet::get_private_keys_for_path_with_connect(&pin, &key_path)
|
||||
.map_err(anyhow::Error::from)?;
|
||||
println!("NSK: {}", hex::encode(&*nsk));
|
||||
println!("VSK: {}", hex::encode(&*vsk));
|
||||
println!("NSK: {}", hex::encode(*nsk));
|
||||
println!("VSK: {}", hex::encode(*vsk));
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,8 +138,10 @@ impl CliAccountMention {
|
||||
Self::KeyPath(path) => {
|
||||
let pin = read_pin()?;
|
||||
let id_str =
|
||||
keycard_wallet::KeycardWallet::get_public_account_id_for_path_with_connect(&pin, path)
|
||||
.map_err(anyhow::Error::from)?;
|
||||
keycard_wallet::KeycardWallet::get_public_account_id_for_path_with_connect(
|
||||
&pin, path,
|
||||
)
|
||||
.map_err(anyhow::Error::from)?;
|
||||
AccountIdWithPrivacy::from_str(&id_str)
|
||||
.map_err(|e| anyhow::anyhow!("Invalid account id from keycard: {e}"))
|
||||
}
|
||||
@ -158,7 +160,6 @@ impl CliAccountMention {
|
||||
Self::Id(_) | Self::Label(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl FromStr for CliAccountMention {
|
||||
|
||||
@ -171,10 +171,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
|
||||
let a_id = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let b_id = user_holding_b.resolve(wallet_core.storage())?;
|
||||
match (a_id, b_id) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(a),
|
||||
AccountIdWithPrivacy::Public(b),
|
||||
) => {
|
||||
(AccountIdWithPrivacy::Public(a), AccountIdWithPrivacy::Public(b)) => {
|
||||
Amm(wallet_core)
|
||||
.send_swap_exact_input(
|
||||
a,
|
||||
@ -205,10 +202,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
|
||||
let a_id = user_holding_a.resolve(wallet_core.storage())?;
|
||||
let b_id = user_holding_b.resolve(wallet_core.storage())?;
|
||||
match (a_id, b_id) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(a),
|
||||
AccountIdWithPrivacy::Public(b),
|
||||
) => {
|
||||
(AccountIdWithPrivacy::Public(a), AccountIdWithPrivacy::Public(b)) => {
|
||||
Amm(wallet_core)
|
||||
.send_swap_exact_output(
|
||||
a,
|
||||
@ -256,6 +250,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
|
||||
max_amount_b,
|
||||
&user_holding_a,
|
||||
&user_holding_b,
|
||||
&user_holding_lp,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@ -400,7 +400,12 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
wallet_core: &mut WalletCore,
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
Self::ShieldedOwned { from, to, amount, from_mention } => {
|
||||
Self::ShieldedOwned {
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
from_mention,
|
||||
} => {
|
||||
let (tx_hash, secret) = NativeTokenTransfer(wallet_core)
|
||||
.send_shielded_transfer(from, to, amount, &from_mention)
|
||||
.await?;
|
||||
|
||||
@ -119,17 +119,16 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
let definition_account_id = definition_account_id.resolve(wallet_core.storage())?;
|
||||
let supply_account_id = supply_account_id.resolve(wallet_core.storage())?;
|
||||
let underlying_subcommand = match (definition_account_id, supply_account_id) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
) => TokenProgramSubcommand::Create(
|
||||
CreateNewTokenProgramSubcommand::NewPublicDefPublicSupp {
|
||||
definition_account_id: def_mention,
|
||||
supply_account_id: sup_mention,
|
||||
name,
|
||||
total_supply,
|
||||
},
|
||||
),
|
||||
(AccountIdWithPrivacy::Public(_), AccountIdWithPrivacy::Public(_)) => {
|
||||
TokenProgramSubcommand::Create(
|
||||
CreateNewTokenProgramSubcommand::NewPublicDefPublicSupp {
|
||||
definition_account_id: def_mention,
|
||||
supply_account_id: sup_mention,
|
||||
name,
|
||||
total_supply,
|
||||
},
|
||||
)
|
||||
}
|
||||
(
|
||||
AccountIdWithPrivacy::Public(definition_account_id),
|
||||
AccountIdWithPrivacy::Private(supply_account_id),
|
||||
@ -230,6 +229,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
sender_account_id: from,
|
||||
recipient_account_id: to,
|
||||
balance_to_move: amount,
|
||||
sender_mention: from_mention,
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -251,6 +251,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
recipient_vpk: to_vpk,
|
||||
recipient_identifier: to_identifier,
|
||||
balance_to_move: amount,
|
||||
sender_mention: from_mention,
|
||||
},
|
||||
),
|
||||
},
|
||||
@ -267,14 +268,13 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
let definition = definition.resolve(wallet_core.storage())?;
|
||||
let holder = holder.resolve(wallet_core.storage())?;
|
||||
let underlying_subcommand = match (definition, holder) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(definition),
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
) => TokenProgramSubcommand::Public(TokenProgramSubcommandPublic::BurnToken {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder_mention,
|
||||
amount,
|
||||
}),
|
||||
(AccountIdWithPrivacy::Public(definition), AccountIdWithPrivacy::Public(_)) => {
|
||||
TokenProgramSubcommand::Public(TokenProgramSubcommandPublic::BurnToken {
|
||||
definition_account_id: definition,
|
||||
holder_account_id: holder_mention,
|
||||
amount,
|
||||
})
|
||||
}
|
||||
(
|
||||
AccountIdWithPrivacy::Private(definition),
|
||||
AccountIdWithPrivacy::Private(holder),
|
||||
@ -338,16 +338,15 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
anyhow::bail!("List of public keys is uncomplete");
|
||||
}
|
||||
(Some(holder), None, None) => match (definition, holder) {
|
||||
(
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
AccountIdWithPrivacy::Public(_),
|
||||
) => TokenProgramSubcommand::Public(
|
||||
TokenProgramSubcommandPublic::MintToken {
|
||||
definition_account_id: def_mention,
|
||||
holder_account_id: hol_mention.expect("matched Some branch"),
|
||||
amount,
|
||||
},
|
||||
),
|
||||
(AccountIdWithPrivacy::Public(_), AccountIdWithPrivacy::Public(_)) => {
|
||||
TokenProgramSubcommand::Public(
|
||||
TokenProgramSubcommandPublic::MintToken {
|
||||
definition_account_id: def_mention,
|
||||
holder_account_id: hol_mention.expect("matched Some branch"),
|
||||
amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(
|
||||
AccountIdWithPrivacy::Private(definition),
|
||||
AccountIdWithPrivacy::Private(holder),
|
||||
@ -568,6 +567,8 @@ pub enum TokenProgramSubcommandShielded {
|
||||
recipient_account_id: AccountId,
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
#[arg(skip)]
|
||||
sender_mention: CliAccountMention,
|
||||
},
|
||||
// Transfer tokens using the token program
|
||||
TransferTokenShieldedForeign {
|
||||
@ -584,6 +585,8 @@ pub enum TokenProgramSubcommandShielded {
|
||||
recipient_identifier: Option<u128>,
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
#[arg(skip)]
|
||||
sender_mention: CliAccountMention,
|
||||
},
|
||||
// Burn tokens using the token program
|
||||
BurnTokenShielded {
|
||||
@ -689,7 +692,11 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
} => {
|
||||
let sender = sender_account_id.resolve(wallet_core.storage())?;
|
||||
let recipient = recipient_account_id.resolve(wallet_core.storage())?;
|
||||
let (AccountIdWithPrivacy::Public(sender_id), AccountIdWithPrivacy::Public(recipient_id)) = (sender, recipient) else {
|
||||
let (
|
||||
AccountIdWithPrivacy::Public(sender_id),
|
||||
AccountIdWithPrivacy::Public(recipient_id),
|
||||
) = (sender, recipient)
|
||||
else {
|
||||
anyhow::bail!("Only public accounts supported for token transfer");
|
||||
};
|
||||
Token(wallet_core)
|
||||
@ -713,7 +720,12 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
anyhow::bail!("Only public holder account supported for token burn");
|
||||
};
|
||||
Token(wallet_core)
|
||||
.send_burn_transaction(definition_account_id, holder_id, amount, &holder_account_id)
|
||||
.send_burn_transaction(
|
||||
definition_account_id,
|
||||
holder_id,
|
||||
amount,
|
||||
&holder_account_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
@ -724,11 +736,19 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
} => {
|
||||
let definition = definition_account_id.resolve(wallet_core.storage())?;
|
||||
let holder = holder_account_id.resolve(wallet_core.storage())?;
|
||||
let (AccountIdWithPrivacy::Public(def_id), AccountIdWithPrivacy::Public(holder_id)) = (definition, holder) else {
|
||||
let (AccountIdWithPrivacy::Public(def_id), AccountIdWithPrivacy::Public(holder_id)) =
|
||||
(definition, holder)
|
||||
else {
|
||||
anyhow::bail!("Only public accounts supported for token mint");
|
||||
};
|
||||
Token(wallet_core)
|
||||
.send_mint_transaction(def_id, holder_id, amount, &definition_account_id, &holder_account_id)
|
||||
.send_mint_transaction(
|
||||
def_id,
|
||||
holder_id,
|
||||
amount,
|
||||
&definition_account_id,
|
||||
&holder_account_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
@ -1049,6 +1069,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
recipient_vpk,
|
||||
recipient_identifier,
|
||||
balance_to_move,
|
||||
sender_mention,
|
||||
} => {
|
||||
let recipient_npk_res = hex::decode(recipient_npk)?;
|
||||
let mut recipient_npk = [0; 32];
|
||||
@ -1069,6 +1090,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
recipient_vpk,
|
||||
recipient_identifier.unwrap_or_else(rand::random),
|
||||
balance_to_move,
|
||||
&sender_mention,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -1088,12 +1110,14 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
sender_mention,
|
||||
} => {
|
||||
let (tx_hash, secret_recipient) = Token(wallet_core)
|
||||
.send_transfer_transaction_shielded_owned_account(
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
&sender_mention,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -1332,7 +1356,9 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand {
|
||||
} => {
|
||||
let definition = definition_account_id.resolve(wallet_core.storage())?;
|
||||
let supply = supply_account_id.resolve(wallet_core.storage())?;
|
||||
let (AccountIdWithPrivacy::Public(def_id), AccountIdWithPrivacy::Public(sup_id)) = (definition, supply) else {
|
||||
let (AccountIdWithPrivacy::Public(def_id), AccountIdWithPrivacy::Public(sup_id)) =
|
||||
(definition, supply)
|
||||
else {
|
||||
anyhow::bail!("Only public accounts supported for new token definition");
|
||||
};
|
||||
Token(wallet_core)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#![expect(
|
||||
clippy::print_stdout,
|
||||
clippy::print_stderr,
|
||||
reason = "This is a CLI application, printing to stdout and stderr is expected and convenient"
|
||||
)]
|
||||
#![expect(
|
||||
@ -17,10 +18,12 @@ use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
||||
use keycard_wallet::KeycardWallet;
|
||||
use log::info;
|
||||
use nssa::{
|
||||
Account, AccountId, PrivacyPreservingTransaction, PublicKey, Signature,
|
||||
Account, AccountId, PrivacyPreservingTransaction, PublicKey, PublicTransaction, Signature,
|
||||
privacy_preserving_transaction::{
|
||||
circuit::ProgramWithDependencies, message::EncryptedAccountData,
|
||||
},
|
||||
program::Program,
|
||||
public_transaction::WitnessSet as PublicWitnessSet,
|
||||
};
|
||||
use nssa_core::{
|
||||
Commitment, MembershipProof, SharedSecretKey,
|
||||
@ -36,6 +39,7 @@ use crate::{
|
||||
account::{AccountIdWithPrivacy, Label},
|
||||
config::WalletConfigOverrides,
|
||||
poller::TxPoller,
|
||||
signing::SigningGroups,
|
||||
storage::key_chain::SharedAccountEntry,
|
||||
};
|
||||
|
||||
@ -83,6 +87,20 @@ pub enum ExecutionFailureKind {
|
||||
KeycardError(#[from] pyo3::PyErr),
|
||||
}
|
||||
|
||||
impl ExecutionFailureKind {
|
||||
/// Convert an [`anyhow::Error`] (e.g. from [`SigningGroups`]) into a keycard error.
|
||||
#[must_use]
|
||||
#[expect(
|
||||
clippy::needless_pass_by_value,
|
||||
reason = "used as a method reference in map_err"
|
||||
)]
|
||||
pub fn from_anyhow(e: anyhow::Error) -> Self {
|
||||
Self::KeycardError(pyo3::PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::partial_pub_fields, reason = "TODO: make all fields private")]
|
||||
pub struct WalletCore {
|
||||
config_path: PathBuf,
|
||||
@ -545,6 +563,62 @@ impl WalletCore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a public transaction, fetching nonces automatically from
|
||||
/// [`SigningGroups::signing_ids`].
|
||||
pub async fn send_public_tx<T: serde::Serialize>(
|
||||
&self,
|
||||
program: &Program,
|
||||
account_ids: Vec<AccountId>,
|
||||
instruction: T,
|
||||
groups: SigningGroups,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let nonces = self
|
||||
.get_accounts_nonces(groups.signing_ids())
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
self.send_public_tx_with_nonces(program, account_ids, nonces, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send a public transaction with caller-supplied nonces.
|
||||
///
|
||||
/// Use this when nonce fetching requires special handling (e.g. the AMM LP account
|
||||
/// may not yet exist on-chain and needs a `Nonce(0)` fallback).
|
||||
pub async fn send_public_tx_with_nonces<T: serde::Serialize>(
|
||||
&self,
|
||||
program: &Program,
|
||||
account_ids: Vec<AccountId>,
|
||||
nonces: Vec<Nonce>,
|
||||
instruction: T,
|
||||
groups: SigningGroups,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)?;
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
crate::helperfunctions::read_pin()
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let sigs = groups
|
||||
.sign_all(&message.hash(), &pin)
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
|
||||
let tx = PublicTransaction::new(message, PublicWitnessSet::from_raw_parts(sigs));
|
||||
Ok(self
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn send_privacy_preserving_tx(
|
||||
&self,
|
||||
accounts: Vec<PrivacyPreservingAccount>,
|
||||
@ -574,36 +648,39 @@ impl WalletCore {
|
||||
|
||||
let mut pre_states = acc_manager.pre_states();
|
||||
|
||||
let (keycard_account, keycard_pin, keycard_path) = if let Some(key_path_str) = key_path.as_deref() {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let account_id_str =
|
||||
KeycardWallet::get_public_account_id_for_path_with_connect(&pin, key_path_str)?;
|
||||
let account_id: AccountId =
|
||||
match account_id_str.parse::<AccountIdWithPrivacy>().expect("Valid parsing of account id") {
|
||||
let (keycard_account, keycard_pin, keycard_path) =
|
||||
if let Some(key_path_str) = key_path.as_deref() {
|
||||
let pin = crate::helperfunctions::read_pin().map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<
|
||||
pyo3::exceptions::PyRuntimeError,
|
||||
_,
|
||||
>(e.to_string()))
|
||||
})?;
|
||||
let account_id_str =
|
||||
KeycardWallet::get_public_account_id_for_path_with_connect(&pin, key_path_str)?;
|
||||
let account_id: AccountId = match account_id_str
|
||||
.parse::<AccountIdWithPrivacy>()
|
||||
.expect("Valid parsing of account id")
|
||||
{
|
||||
AccountIdWithPrivacy::Public(id) | AccountIdWithPrivacy::Private(id) => id,
|
||||
};
|
||||
let account = self
|
||||
.get_account_public(account_id)
|
||||
.await
|
||||
.expect("Expect valid account");
|
||||
let pin_str = pin.as_str().to_owned();
|
||||
(
|
||||
Some(AccountWithMetadata {
|
||||
account,
|
||||
is_authorized: true,
|
||||
account_id,
|
||||
}),
|
||||
Some(pin_str),
|
||||
Some(key_path_str.to_owned()),
|
||||
)
|
||||
} else {
|
||||
(None, None, None)
|
||||
};
|
||||
let account = self
|
||||
.get_account_public(account_id)
|
||||
.await
|
||||
.expect("Expect valid account");
|
||||
let pin_str = pin.as_str().to_owned();
|
||||
(
|
||||
Some(AccountWithMetadata {
|
||||
account,
|
||||
is_authorized: true,
|
||||
account_id,
|
||||
}),
|
||||
Some(pin_str),
|
||||
Some(key_path_str.to_owned()),
|
||||
)
|
||||
} else {
|
||||
(None, None, None)
|
||||
};
|
||||
|
||||
let mut nonces: Vec<Nonce> = acc_manager.public_account_nonces().into_iter().collect();
|
||||
|
||||
@ -653,35 +730,39 @@ impl WalletCore {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set = if let (Some(pin), Some(path)) =
|
||||
(keycard_pin.as_deref(), keycard_path.as_deref())
|
||||
{
|
||||
let hash = message.hash();
|
||||
let local_auth = acc_manager.public_account_auth();
|
||||
let mut sigs: Vec<(Signature, PublicKey)> = local_auth
|
||||
.iter()
|
||||
.map(|&key| (Signature::new(key, &hash), PublicKey::new_from_private_key(key)))
|
||||
.collect();
|
||||
let keycard_sig = pyo3::Python::with_gil(|py| {
|
||||
let mut ctx = crate::signing::KeycardSessionContext::new(pin);
|
||||
let result = ctx
|
||||
.get_or_connect(py)
|
||||
.and_then(|w| w.sign_message_for_path(py, path, &hash));
|
||||
ctx.close(py);
|
||||
result
|
||||
})
|
||||
.map_err(ExecutionFailureKind::KeycardError)?;
|
||||
sigs.push(keycard_sig);
|
||||
nssa::privacy_preserving_transaction::witness_set::WitnessSet::from_raw_parts(
|
||||
sigs, proof,
|
||||
)
|
||||
} else {
|
||||
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
|
||||
&message,
|
||||
proof,
|
||||
&acc_manager.public_account_auth(),
|
||||
)
|
||||
};
|
||||
let witness_set =
|
||||
if let (Some(pin), Some(path)) = (keycard_pin.as_deref(), keycard_path.as_deref()) {
|
||||
let hash = message.hash();
|
||||
let local_auth = acc_manager.public_account_auth();
|
||||
let mut sigs: Vec<(Signature, PublicKey)> = local_auth
|
||||
.iter()
|
||||
.map(|&key| {
|
||||
(
|
||||
Signature::new(key, &hash),
|
||||
PublicKey::new_from_private_key(key),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let keycard_sig = pyo3::Python::with_gil(|py| {
|
||||
let mut ctx = crate::signing::KeycardSessionContext::new(pin);
|
||||
let result = ctx
|
||||
.get_or_connect(py)
|
||||
.and_then(|w| w.sign_message_for_path(py, path, &hash));
|
||||
ctx.close(py);
|
||||
result
|
||||
})
|
||||
.map_err(ExecutionFailureKind::KeycardError)?;
|
||||
sigs.push(keycard_sig);
|
||||
nssa::privacy_preserving_transaction::witness_set::WitnessSet::from_raw_parts(
|
||||
sigs, proof,
|
||||
)
|
||||
} else {
|
||||
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
|
||||
&message,
|
||||
proof,
|
||||
&acc_manager.public_account_auth(),
|
||||
)
|
||||
};
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
|
||||
let shared_secrets: Vec<_> = private_account_keys
|
||||
|
||||
@ -1,16 +1,9 @@
|
||||
use amm_core::{compute_liquidity_token_pda, compute_pool_pda, compute_vault_pda};
|
||||
use common::{HashType, transaction::NSSATransaction};
|
||||
use nssa::{AccountId, program::Program, public_transaction::WitnessSet};
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use common::HashType;
|
||||
use nssa::{AccountId, program::Program};
|
||||
use token_core::TokenHolding;
|
||||
|
||||
use crate::{
|
||||
ExecutionFailureKind, WalletCore,
|
||||
cli::CliAccountMention,
|
||||
helperfunctions::read_pin,
|
||||
signing::SigningGroups,
|
||||
};
|
||||
use crate::{ExecutionFailureKind, WalletCore, cli::CliAccountMention, signing::SigningGroups};
|
||||
pub struct Amm<'wallet>(pub &'wallet WalletCore);
|
||||
|
||||
impl Amm<'_> {
|
||||
@ -73,41 +66,35 @@ impl Amm<'_> {
|
||||
.add_sender(a_mention, user_holding_a, self.0)
|
||||
.and_then(|()| groups.add_sender(b_mention, user_holding_b, self.0))
|
||||
.and_then(|()| groups.add_recipient(lp_mention, user_holding_lp, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
|
||||
let mut nonces = self.0.get_accounts_nonces(vec![user_holding_a, user_holding_b]).await
|
||||
let mut nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![user_holding_a, user_holding_b])
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
if groups.signing_ids().contains(&user_holding_lp) {
|
||||
let lp_nonces = self.0.get_accounts_nonces(vec![user_holding_lp]).await
|
||||
let lp_nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![user_holding_lp])
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
nonces.push(lp_nonces.into_iter().next().unwrap_or(nssa_core::account::Nonce(0)));
|
||||
nonces.push(
|
||||
lp_nonces
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or(nssa_core::account::Nonce(0)),
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"Liquidity pool tokens receiver's account ({user_holding_lp}) private key not found in wallet. Proceeding with only liquidity provider's keys."
|
||||
);
|
||||
}
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
self.0
|
||||
.send_public_tx_with_nonces(&program, account_ids, nonces, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
@ -165,37 +152,18 @@ impl Amm<'_> {
|
||||
} else if definition_token_b_id == token_definition_id_in {
|
||||
(user_holding_b, b_mention)
|
||||
} else {
|
||||
return Err(ExecutionFailureKind::AccountDataError(token_definition_id_in));
|
||||
return Err(ExecutionFailureKind::AccountDataError(
|
||||
token_definition_id_in,
|
||||
));
|
||||
};
|
||||
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(seller_mention, account_id_auth, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
self.0
|
||||
.send_public_tx(&program, account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
@ -253,37 +221,18 @@ impl Amm<'_> {
|
||||
} else if definition_token_b_id == token_definition_id_in {
|
||||
(user_holding_b, b_mention)
|
||||
} else {
|
||||
return Err(ExecutionFailureKind::AccountDataError(token_definition_id_in));
|
||||
return Err(ExecutionFailureKind::AccountDataError(
|
||||
token_definition_id_in,
|
||||
));
|
||||
};
|
||||
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(seller_mention, account_id_auth, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
self.0
|
||||
.send_public_tx(&program, account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
@ -297,6 +246,7 @@ impl Amm<'_> {
|
||||
max_amount_to_add_token_b: u128,
|
||||
a_mention: &CliAccountMention,
|
||||
b_mention: &CliAccountMention,
|
||||
lp_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let instruction = amm_core::Instruction::AddLiquidity {
|
||||
min_amount_liquidity,
|
||||
@ -344,31 +294,36 @@ impl Amm<'_> {
|
||||
groups
|
||||
.add_sender(a_mention, user_holding_a, self.0)
|
||||
.and_then(|()| groups.add_sender(b_mention, user_holding_b, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
.and_then(|()| groups.add_recipient(lp_mention, user_holding_lp, self.0))
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
let mut nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![user_holding_a, user_holding_b])
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
if groups.signing_ids().contains(&user_holding_lp) {
|
||||
let lp_nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![user_holding_lp])
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
nonces.push(
|
||||
lp_nonces
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or(nssa_core::account::Nonce(0)),
|
||||
);
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
println!(
|
||||
"LP holder's account ({user_holding_lp}) private key not found in wallet. Proceeding with only liquidity providers' keys."
|
||||
);
|
||||
}
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
self.0
|
||||
.send_public_tx_with_nonces(&program, account_ids, nonces, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
@ -427,30 +382,9 @@ impl Amm<'_> {
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(lp_mention, user_holding_lp, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
self.0
|
||||
.send_public_tx(&program, account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ata_core::{compute_ata_seed, get_associated_token_account_id};
|
||||
use common::{HashType, transaction::NSSATransaction};
|
||||
use common::HashType;
|
||||
use nssa::{
|
||||
AccountId, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program,
|
||||
public_transaction::WitnessSet,
|
||||
};
|
||||
use nssa_core::SharedSecretKey;
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
|
||||
use crate::{
|
||||
ExecutionFailureKind, PrivacyPreservingAccount, WalletCore,
|
||||
cli::CliAccountMention,
|
||||
helperfunctions::read_pin,
|
||||
ExecutionFailureKind, PrivacyPreservingAccount, WalletCore, cli::CliAccountMention,
|
||||
signing::SigningGroups,
|
||||
};
|
||||
|
||||
@ -39,26 +34,10 @@ impl Ata<'_> {
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(owner_mention, owner_id, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction)?;
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self.0.sequencer_client.send_transaction(NSSATransaction::Public(tx)).await?)
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
self.0
|
||||
.send_public_tx(&program, account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_transfer(
|
||||
@ -77,31 +56,18 @@ impl Ata<'_> {
|
||||
);
|
||||
|
||||
let account_ids = vec![owner_id, sender_ata_id, recipient_id];
|
||||
let instruction = ata_core::Instruction::Transfer { ata_program_id, amount };
|
||||
let instruction = ata_core::Instruction::Transfer {
|
||||
ata_program_id,
|
||||
amount,
|
||||
};
|
||||
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(owner_mention, owner_id, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction)?;
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self.0.sequencer_client.send_transaction(NSSATransaction::Public(tx)).await?)
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
self.0
|
||||
.send_public_tx(&program, account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_burn(
|
||||
@ -119,31 +85,18 @@ impl Ata<'_> {
|
||||
);
|
||||
|
||||
let account_ids = vec![owner_id, holder_ata_id, definition_id];
|
||||
let instruction = ata_core::Instruction::Burn { ata_program_id, amount };
|
||||
let instruction = ata_core::Instruction::Burn {
|
||||
ata_program_id,
|
||||
amount,
|
||||
};
|
||||
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(owner_mention, owner_id, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program.id(), account_ids, nonces, instruction)?;
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self.0.sequencer_client.send_transaction(NSSATransaction::Public(tx)).await?)
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
self.0
|
||||
.send_public_tx(&program, account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_create_private_owner(
|
||||
@ -170,7 +123,12 @@ impl Ata<'_> {
|
||||
];
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(accounts, instruction_data, &ata_with_token_dependency(), &None)
|
||||
.send_privacy_preserving_tx(
|
||||
accounts,
|
||||
instruction_data,
|
||||
&ata_with_token_dependency(),
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
.map(|(hash, mut secrets)| {
|
||||
let secret = secrets.pop().expect("expected owner's secret");
|
||||
@ -207,7 +165,12 @@ impl Ata<'_> {
|
||||
];
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(accounts, instruction_data, &ata_with_token_dependency(), &None)
|
||||
.send_privacy_preserving_tx(
|
||||
accounts,
|
||||
instruction_data,
|
||||
&ata_with_token_dependency(),
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
.map(|(hash, mut secrets)| {
|
||||
let secret = secrets.pop().expect("expected owner's secret");
|
||||
@ -243,7 +206,12 @@ impl Ata<'_> {
|
||||
];
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(accounts, instruction_data, &ata_with_token_dependency(), &None)
|
||||
.send_privacy_preserving_tx(
|
||||
accounts,
|
||||
instruction_data,
|
||||
&ata_with_token_dependency(),
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
.map(|(hash, mut secrets)| {
|
||||
let secret = secrets.pop().expect("expected owner's secret");
|
||||
|
||||
@ -1,18 +1,9 @@
|
||||
use authenticated_transfer_core::Instruction as AuthTransferInstruction;
|
||||
use common::{HashType, transaction::NSSATransaction};
|
||||
use nssa::{
|
||||
AccountId, PublicTransaction,
|
||||
program::Program,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
};
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use common::HashType;
|
||||
use nssa::{AccountId, program::Program};
|
||||
|
||||
use super::NativeTokenTransfer;
|
||||
use crate::{
|
||||
ExecutionFailureKind, cli::CliAccountMention, helperfunctions::read_pin,
|
||||
signing::SigningGroups,
|
||||
};
|
||||
use crate::{ExecutionFailureKind, cli::CliAccountMention, signing::SigningGroups};
|
||||
|
||||
impl NativeTokenTransfer<'_> {
|
||||
pub async fn send_public_transfer(
|
||||
@ -27,52 +18,18 @@ impl NativeTokenTransfer<'_> {
|
||||
groups
|
||||
.add_sender(from_mention, from, self.0)
|
||||
.and_then(|()| groups.add_recipient(to_mention, to, self.0))
|
||||
.map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?;
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
|
||||
let program_id = Program::authenticated_transfer_program().id();
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(groups.signing_ids())
|
||||
self.0
|
||||
.send_public_tx(
|
||||
&Program::authenticated_transfer_program(),
|
||||
vec![from, to],
|
||||
AuthTransferInstruction::Transfer {
|
||||
amount: balance_to_move,
|
||||
},
|
||||
groups,
|
||||
)
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = Message::try_new(
|
||||
program_id,
|
||||
vec![from, to],
|
||||
nonces,
|
||||
AuthTransferInstruction::Transfer {
|
||||
amount: balance_to_move,
|
||||
},
|
||||
)
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?;
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let sigs = groups.sign_all(&message.hash(), &pin).map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))
|
||||
})?;
|
||||
|
||||
let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn register_account(
|
||||
@ -80,53 +37,18 @@ impl NativeTokenTransfer<'_> {
|
||||
from: AccountId,
|
||||
account_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let nonces = self
|
||||
.0
|
||||
.get_accounts_nonces(vec![from])
|
||||
.await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let account_ids = vec![from];
|
||||
let program_id = Program::authenticated_transfer_program().id();
|
||||
let message = Message::try_new(
|
||||
program_id,
|
||||
account_ids,
|
||||
nonces,
|
||||
AuthTransferInstruction::Initialize,
|
||||
)
|
||||
.map_err(ExecutionFailureKind::TransactionBuildError)?;
|
||||
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(account_mention, from, self.0)
|
||||
.map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?;
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
|
||||
e.to_string(),
|
||||
))
|
||||
})?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let sigs = groups.sign_all(&message.hash(), &pin).map_err(|e| {
|
||||
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string()))
|
||||
})?;
|
||||
|
||||
let tx = PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
self.0
|
||||
.send_public_tx(
|
||||
&Program::authenticated_transfer_program(),
|
||||
vec![from],
|
||||
AuthTransferInstruction::Initialize,
|
||||
groups,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,21 +1,16 @@
|
||||
use common::{HashType, transaction::NSSATransaction};
|
||||
use nssa::{AccountId, program::Program, public_transaction::WitnessSet};
|
||||
use common::HashType;
|
||||
use nssa::{AccountId, program::Program};
|
||||
use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey};
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use token_core::Instruction;
|
||||
|
||||
use crate::{
|
||||
ExecutionFailureKind, PrivacyPreservingAccount, WalletCore,
|
||||
cli::CliAccountMention,
|
||||
helperfunctions::read_pin,
|
||||
ExecutionFailureKind, PrivacyPreservingAccount, WalletCore, cli::CliAccountMention,
|
||||
signing::SigningGroups,
|
||||
};
|
||||
|
||||
pub struct Token<'wallet>(pub &'wallet WalletCore);
|
||||
|
||||
impl Token<'_> {
|
||||
#[expect(clippy::too_many_arguments, reason = "each parameter is distinct")]
|
||||
pub async fn send_new_definition(
|
||||
&self,
|
||||
definition_account_id: AccountId,
|
||||
@ -26,37 +21,17 @@ impl Token<'_> {
|
||||
supply_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let account_ids = vec![definition_account_id, supply_account_id];
|
||||
let program_id = nssa::program::Program::token().id();
|
||||
let instruction = Instruction::NewFungibleDefinition { name, total_supply };
|
||||
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(definition_mention, definition_account_id, self.0)
|
||||
.and_then(|()| groups.add_sender(supply_mention, supply_account_id, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
let message = nssa::public_transaction::Message::try_new(program_id, account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
self.0
|
||||
.send_public_tx(&Program::token(), account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_new_definition_private_owned_supply(
|
||||
@ -168,38 +143,19 @@ impl Token<'_> {
|
||||
recipient_mention: &CliAccountMention,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let account_ids = vec![sender_account_id, recipient_account_id];
|
||||
let program_id = nssa::program::Program::token().id();
|
||||
let instruction = Instruction::Transfer { amount_to_transfer: amount };
|
||||
let instruction = Instruction::Transfer {
|
||||
amount_to_transfer: amount,
|
||||
};
|
||||
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(sender_mention, sender_account_id, self.0)
|
||||
.and_then(|()| groups.add_recipient(recipient_mention, recipient_account_id, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(program_id, account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
self.0
|
||||
.send_public_tx(&Program::token(), account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_transfer_transaction_private_owned_account(
|
||||
@ -315,12 +271,14 @@ impl Token<'_> {
|
||||
sender_account_id: AccountId,
|
||||
recipient_account_id: AccountId,
|
||||
amount: u128,
|
||||
sender_mention: &CliAccountMention,
|
||||
) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> {
|
||||
let instruction = Instruction::Transfer {
|
||||
amount_to_transfer: amount,
|
||||
};
|
||||
let instruction_data =
|
||||
Program::serialize_instruction(instruction).expect("Instruction should serialize");
|
||||
let key_path = sender_mention.key_path().map(str::to_owned);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
@ -332,7 +290,7 @@ impl Token<'_> {
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
&None,
|
||||
&key_path,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
@ -351,12 +309,14 @@ impl Token<'_> {
|
||||
recipient_vpk: ViewingPublicKey,
|
||||
recipient_identifier: Identifier,
|
||||
amount: u128,
|
||||
sender_mention: &CliAccountMention,
|
||||
) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> {
|
||||
let instruction = Instruction::Transfer {
|
||||
amount_to_transfer: amount,
|
||||
};
|
||||
let instruction_data =
|
||||
Program::serialize_instruction(instruction).expect("Instruction should serialize");
|
||||
let key_path = sender_mention.key_path().map(str::to_owned);
|
||||
|
||||
self.0
|
||||
.send_privacy_preserving_tx(
|
||||
@ -370,7 +330,7 @@ impl Token<'_> {
|
||||
],
|
||||
instruction_data,
|
||||
&Program::token().into(),
|
||||
&None,
|
||||
&key_path,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
@ -397,31 +357,11 @@ impl Token<'_> {
|
||||
let mut groups = SigningGroups::new();
|
||||
groups
|
||||
.add_sender(holder_mention, holder_account_id, self.0)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
let message = nssa::public_transaction::Message::try_new(Program::token().id(), account_ids, nonces, instruction)
|
||||
.expect("Instruction should serialize");
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
self.0
|
||||
.send_public_tx(&Program::token(), account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_burn_transaction_private_owned_account(
|
||||
@ -544,31 +484,11 @@ impl Token<'_> {
|
||||
groups
|
||||
.add_sender(definition_mention, definition_account_id, self.0)
|
||||
.and_then(|()| groups.add_recipient(holder_mention, holder_account_id, self.0))
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
.map_err(ExecutionFailureKind::from_anyhow)?;
|
||||
|
||||
let nonces = self.0.get_accounts_nonces(groups.signing_ids()).await
|
||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||
|
||||
let message = nssa::public_transaction::Message::try_new(Program::token().id(), account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let pin = if groups.needs_pin() {
|
||||
read_pin()
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?
|
||||
.as_str()
|
||||
.to_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let sigs = groups.sign_all(&message.hash(), &pin)
|
||||
.map_err(|e| ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(e.to_string())))?;
|
||||
|
||||
let tx = nssa::PublicTransaction::new(message, WitnessSet::from_raw_parts(sigs));
|
||||
|
||||
Ok(self
|
||||
.0
|
||||
.sequencer_client
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?)
|
||||
self.0
|
||||
.send_public_tx(&Program::token(), account_ids, instruction, groups)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn send_mint_transaction_private_owned_account(
|
||||
@ -750,5 +670,4 @@ impl Token<'_> {
|
||||
(resp, first)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -112,75 +112,38 @@ impl SigningGroups {
|
||||
|
||||
Ok(sigs)
|
||||
}
|
||||
|
||||
/// Add a recipient. Same as [`add_sender`] but silently skips accounts with no local
|
||||
/// key and no keycard path — they are foreign and require neither a signature nor a nonce.
|
||||
pub fn add_recipient(
|
||||
&mut self,
|
||||
mention: &CliAccountMention,
|
||||
account_id: AccountId,
|
||||
wallet_core: &WalletCore,
|
||||
) -> Result<()> {
|
||||
if let CliAccountMention::KeyPath(path) = mention {
|
||||
self.keycard.push((account_id, path.clone()));
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(key) = wallet_core
|
||||
.storage()
|
||||
.key_chain()
|
||||
.pub_account_signing_key(account_id)
|
||||
{
|
||||
self.local.push((account_id, key.clone()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `true` when a PIN is required (at least one keycard signer is present).
|
||||
#[must_use]
|
||||
pub const fn needs_pin(&self) -> bool {
|
||||
!self.keycard.is_empty()
|
||||
}
|
||||
|
||||
/// Account IDs that require a nonce (every non-foreign signer).
|
||||
#[must_use]
|
||||
pub fn signing_ids(&self) -> Vec<AccountId> {
|
||||
self.local
|
||||
.iter()
|
||||
.map(|(id, _)| *id)
|
||||
.chain(self.keycard.iter().map(|(id, _)| *id))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Sign `hash` for every account in the group.
|
||||
///
|
||||
/// Local accounts are signed in pure Rust. Keycard accounts share one Python session.
|
||||
pub fn sign_all(&self, hash: &[u8; 32], pin: &str) -> Result<Vec<(Signature, PublicKey)>> {
|
||||
let mut sigs: Vec<(Signature, PublicKey)> = self
|
||||
.local
|
||||
.iter()
|
||||
.map(|(_, key)| {
|
||||
(
|
||||
Signature::new(key, hash),
|
||||
PublicKey::new_from_private_key(key),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !self.keycard.is_empty() {
|
||||
pyo3::Python::with_gil(|py| -> pyo3::PyResult<()> {
|
||||
python_path::add_python_path(py)?;
|
||||
let wallet = KeycardWallet::new(py)?;
|
||||
wallet.connect(py, pin)?;
|
||||
for (_, path) in &self.keycard {
|
||||
sigs.push(wallet.sign_message_for_path(py, path, hash)?);
|
||||
}
|
||||
drop(wallet.close_session(py));
|
||||
Ok(())
|
||||
})
|
||||
.map_err(anyhow::Error::from)?;
|
||||
}
|
||||
|
||||
Ok(sigs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Lazily opens and reuses a single Keycard session for all keycard signers in one transaction.
|
||||
pub struct KeycardSessionContext {
|
||||
pin: String,
|
||||
wallet: Option<KeycardWallet>,
|
||||
}
|
||||
|
||||
impl KeycardSessionContext {
|
||||
pub fn new(pin: impl Into<String>) -> Self {
|
||||
Self {
|
||||
pin: pin.into(),
|
||||
wallet: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_or_connect<'py>(
|
||||
&'py mut self,
|
||||
py: Python<'py>,
|
||||
) -> pyo3::PyResult<&'py KeycardWallet> {
|
||||
if self.wallet.is_none() {
|
||||
python_path::add_python_path(py)?;
|
||||
let wallet = KeycardWallet::new(py)?;
|
||||
wallet.connect(py, &self.pin)?;
|
||||
self.wallet = Some(wallet);
|
||||
}
|
||||
Ok(self.wallet.as_ref().unwrap())
|
||||
}
|
||||
|
||||
pub fn close(self, py: Python<'_>) {
|
||||
if let Some(w) = self.wallet {
|
||||
drop(w.close_session(py));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user