feat: compute pinata solution in wallet

This commit is contained in:
Daniil Polyakov 2025-11-30 03:16:47 +03:00
parent 4afbd65e3b
commit df88d8bad6
7 changed files with 86 additions and 61 deletions

View File

@ -1400,10 +1400,8 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
let pinata_account_id = PINATA_BASE58; let pinata_account_id = PINATA_BASE58;
let pinata_prize = 150; let pinata_prize = 150;
let solution = 989106;
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
to_account_id: make_public_account_input_from_str(ACC_SENDER), to: make_public_account_input_from_str(ACC_SENDER),
solution,
}); });
let wallet_config = fetch_config().await.unwrap(); let wallet_config = fetch_config().await.unwrap();
@ -1531,11 +1529,9 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
info!("########## test_pinata_private_receiver ##########"); info!("########## test_pinata_private_receiver ##########");
let pinata_account_id = PINATA_BASE58; let pinata_account_id = PINATA_BASE58;
let pinata_prize = 150; let pinata_prize = 150;
let solution = 989106;
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
to_account_id: make_private_account_input_from_str(ACC_SENDER_PRIVATE), to: make_private_account_input_from_str(ACC_SENDER_PRIVATE),
solution,
}); });
let wallet_config = fetch_config().await.unwrap(); let wallet_config = fetch_config().await.unwrap();
@ -1588,7 +1584,6 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
info!("########## test_pinata_private_receiver_new_account ##########"); info!("########## test_pinata_private_receiver_new_account ##########");
let pinata_account_id = PINATA_BASE58; let pinata_account_id = PINATA_BASE58;
let pinata_prize = 150; let pinata_prize = 150;
let solution = 989106;
// Create new account for the token supply holder (private) // Create new account for the token supply holder (private)
let SubcommandReturnValue::RegisterAccount { let SubcommandReturnValue::RegisterAccount {
@ -1605,8 +1600,7 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
}; };
let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim {
to_account_id: make_private_account_input_from_str(&winner_account_id.to_string()), to: make_private_account_input_from_str(&winner_account_id.to_string()),
solution,
}); });
let wallet_config = fetch_config().await.unwrap(); let wallet_config = fetch_config().await.unwrap();

View File

@ -20,6 +20,7 @@ base58.workspace = true
hex = "0.4.3" hex = "0.4.3"
rand.workspace = true rand.workspace = true
itertools = "0.14.0" itertools = "0.14.0"
sha2.workspace = true
[dependencies.key_protocol] [dependencies.key_protocol]
path = "../key_protocol" path = "../key_protocol"

View File

@ -81,7 +81,7 @@ pub enum OverCommand {
pub struct Args { pub struct Args {
/// Continious run flag /// Continious run flag
#[arg(short, long)] #[arg(short, long)]
pub continious_run: bool, pub continuous_run: bool,
/// Wallet command /// Wallet command
#[command(subcommand)] #[command(subcommand)]
pub command: Option<OverCommand>, pub command: Option<OverCommand>,
@ -162,7 +162,7 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
Ok(subcommand_ret) Ok(subcommand_ret)
} }
pub async fn execute_continious_run() -> Result<()> { pub async fn execute_continuous_run() -> Result<()> {
let config = fetch_config().await?; let config = fetch_config().await?;
let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
let mut wallet_core = WalletCore::start_from_config_update_chain(config.clone()).await?; let mut wallet_core = WalletCore::start_from_config_update_chain(config.clone()).await?;

View File

@ -61,7 +61,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
.register_account(account_id) .register_account(account_id)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let transfer_tx = let transfer_tx =
wallet_core.poll_native_token_transfer(res.tx_hash).await?; wallet_core.poll_native_token_transfer(res.tx_hash).await?;
@ -79,7 +79,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
.register_account_private(account_id) .register_account_private(account_id)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -320,7 +320,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
.send_private_transfer_to_owned_account(from, to, amount) .send_private_transfer_to_owned_account(from, to, amount)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -364,7 +364,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
.send_private_transfer_to_outer_account(from, to_npk, to_ipk, amount) .send_private_transfer_to_outer_account(from, to_npk, to_ipk, amount)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -404,7 +404,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
.send_shielded_transfer(from, to, amount) .send_shielded_transfer(from, to, amount)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -449,7 +449,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
.send_shielded_transfer_to_outer_account(from, to_npk, to_ipk, amount) .send_shielded_transfer_to_outer_account(from, to_npk, to_ipk, amount)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
@ -483,7 +483,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
.send_deshielded_transfer(from, to, amount) .send_deshielded_transfer(from, to, amount)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -513,7 +513,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
.send_public_transfer(from, to, amount) .send_public_transfer(from, to, amount)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?;

View File

@ -1,4 +1,4 @@
use anyhow::Result; use anyhow::{Context, Result};
use clap::Subcommand; use clap::Subcommand;
use common::{PINATA_BASE58, transaction::NSSATransaction}; use common::{PINATA_BASE58, transaction::NSSATransaction};
@ -14,12 +14,9 @@ use crate::{
pub enum PinataProgramAgnosticSubcommand { pub enum PinataProgramAgnosticSubcommand {
/// Claim pinata /// Claim pinata
Claim { Claim {
/// to_account_id - valid 32 byte base58 string with privacy prefix /// to - valid 32 byte base58 string with privacy prefix
#[arg(long)] #[arg(long)]
to_account_id: String, to: String,
/// solution - solution to pinata challenge
#[arg(long)]
solution: u128,
}, },
} }
@ -29,26 +26,20 @@ impl WalletSubcommand for PinataProgramAgnosticSubcommand {
wallet_core: &mut WalletCore, wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> { ) -> Result<SubcommandReturnValue> {
let underlying_subcommand = match self { let underlying_subcommand = match self {
PinataProgramAgnosticSubcommand::Claim { PinataProgramAgnosticSubcommand::Claim { to } => {
to_account_id, let (to, to_addr_privacy) = parse_addr_with_privacy_prefix(&to)?;
solution,
} => {
let (to_account_id, to_addr_privacy) =
parse_addr_with_privacy_prefix(&to_account_id)?;
match to_addr_privacy { match to_addr_privacy {
AccountPrivacyKind::Public => { AccountPrivacyKind::Public => {
PinataProgramSubcommand::Public(PinataProgramSubcommandPublic::Claim { PinataProgramSubcommand::Public(PinataProgramSubcommandPublic::Claim {
pinata_account_id: PINATA_BASE58.to_string(), pinata_account_id: PINATA_BASE58.to_string(),
winner_account_id: to_account_id, winner_account_id: to,
solution,
}) })
} }
AccountPrivacyKind::Private => PinataProgramSubcommand::Private( AccountPrivacyKind::Private => PinataProgramSubcommand::Private(
PinataProgramSubcommandPrivate::ClaimPrivateOwned { PinataProgramSubcommandPrivate::ClaimPrivateOwned {
pinata_account_id: PINATA_BASE58.to_string(), pinata_account_id: PINATA_BASE58.to_string(),
winner_account_id: to_account_id, winner_account_id: to,
solution,
}, },
), ),
} }
@ -82,9 +73,6 @@ pub enum PinataProgramSubcommandPublic {
/// winner_account_id - valid 32 byte hex string /// winner_account_id - valid 32 byte hex string
#[arg(long)] #[arg(long)]
winner_account_id: String, winner_account_id: String,
/// solution - solution to pinata challenge
#[arg(long)]
solution: u128,
}, },
} }
@ -100,9 +88,6 @@ pub enum PinataProgramSubcommandPrivate {
/// winner_account_id - valid 32 byte hex string /// winner_account_id - valid 32 byte hex string
#[arg(long)] #[arg(long)]
winner_account_id: String, winner_account_id: String,
/// solution - solution to pinata challenge
#[arg(long)]
solution: u128,
}, },
} }
@ -115,17 +100,21 @@ impl WalletSubcommand for PinataProgramSubcommandPublic {
PinataProgramSubcommandPublic::Claim { PinataProgramSubcommandPublic::Claim {
pinata_account_id, pinata_account_id,
winner_account_id, winner_account_id,
solution,
} => { } => {
let pinata_account_id = pinata_account_id.parse().unwrap();
let solution = find_solution(wallet_core, pinata_account_id)
.await
.context("failed to compute solution")?;
let res = Pinata(wallet_core) let res = Pinata(wallet_core)
.claim( .claim(
pinata_account_id.parse().unwrap(), pinata_account_id,
winner_account_id.parse().unwrap(), winner_account_id.parse().unwrap(),
solution, solution,
) )
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -134,10 +123,6 @@ impl WalletSubcommand for PinataProgramSubcommandPublic {
println!("Transaction data is {transfer_tx:?}"); println!("Transaction data is {transfer_tx:?}");
let path = wallet_core.store_persistent_data().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::Empty) Ok(SubcommandReturnValue::Empty)
} }
} }
@ -153,16 +138,18 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate {
PinataProgramSubcommandPrivate::ClaimPrivateOwned { PinataProgramSubcommandPrivate::ClaimPrivateOwned {
pinata_account_id, pinata_account_id,
winner_account_id, winner_account_id,
solution,
} => { } => {
let pinata_account_id = pinata_account_id.parse().unwrap(); let pinata_account_id = pinata_account_id.parse().unwrap();
let winner_account_id = winner_account_id.parse().unwrap(); let winner_account_id = winner_account_id.parse().unwrap();
let solution = find_solution(wallet_core, pinata_account_id)
.await
.context("failed to compute solution")?;
let (res, secret_winner) = Pinata(wallet_core) let (res, secret_winner) = Pinata(wallet_core)
.claim_private_owned_account(pinata_account_id, winner_account_id, solution) .claim_private_owned_account(pinata_account_id, winner_account_id, solution)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -205,3 +192,46 @@ impl WalletSubcommand for PinataProgramSubcommand {
} }
} }
} }
async fn find_solution(wallet: &WalletCore, pinata_account_id: nssa::AccountId) -> Result<u128> {
let account = wallet.get_account_public(pinata_account_id).await?;
let data: [u8; 33] = account
.data
.try_into()
.map_err(|_| anyhow::Error::msg("invalid pinata account data"))?;
println!("Computing solution for pinata...");
let now = std::time::Instant::now();
let solution = compute_solution(data);
println!("Found solution {solution} in {:?}", now.elapsed());
Ok(solution)
}
fn compute_solution(data: [u8; 33]) -> u128 {
let difficulty = data[0];
let seed = &data[1..];
let mut solution = 0u128;
while !validate_solution(difficulty, seed, solution) {
solution = solution.checked_add(1).expect("solution overflowed u128");
}
solution
}
fn validate_solution(difficulty: u8, seed: &[u8], solution: u128) -> bool {
use sha2::{Digest as _, digest::FixedOutput as _};
let mut bytes = [0; 32 + 16];
bytes[..32].copy_from_slice(seed);
bytes[32..].copy_from_slice(&solution.to_le_bytes());
let mut hasher = sha2::Sha256::new();
hasher.update(bytes);
let digest: [u8; 32] = hasher.finalize_fixed().into();
let difficulty = difficulty as usize;
digest[..difficulty].iter().all(|&b| b == 0)
}

View File

@ -399,7 +399,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
) )
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -437,7 +437,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
) )
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -490,7 +490,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
) )
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -538,7 +538,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded {
) )
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -598,7 +598,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
) )
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core
@ -631,7 +631,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
) )
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send are {res:#?}");
let tx_hash = res.tx_hash; let tx_hash = res.tx_hash;
let transfer_tx = wallet_core let transfer_tx = wallet_core

View File

@ -1,7 +1,7 @@
use anyhow::Result; use anyhow::Result;
use clap::{CommandFactory as _, Parser as _}; use clap::{CommandFactory as _, Parser as _};
use tokio::runtime::Builder; use tokio::runtime::Builder;
use wallet::cli::{Args, OverCommand, execute_continious_run, execute_setup, execute_subcommand}; use wallet::cli::{Args, OverCommand, execute_continuous_run, execute_setup, execute_subcommand};
pub const NUM_THREADS: usize = 2; pub const NUM_THREADS: usize = 2;
@ -23,16 +23,16 @@ fn main() -> Result<()> {
env_logger::init(); env_logger::init();
runtime.block_on(async move { runtime.block_on(async move {
if let Some(overcommand) = args.command { if let Some(over_command) = args.command {
match overcommand { match over_command {
OverCommand::Command(command) => { OverCommand::Command(command) => {
let _output = execute_subcommand(command).await?; let _output = execute_subcommand(command).await?;
Ok(()) Ok(())
} }
OverCommand::Setup { password } => execute_setup(password).await, OverCommand::Setup { password } => execute_setup(password).await,
} }
} else if args.continious_run { } else if args.continuous_run {
execute_continious_run().await execute_continuous_run().await
} else { } else {
let help = Args::command().render_long_help(); let help = Args::command().render_long_help();
println!("{help}"); println!("{help}");