mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-11 02:29:25 +00:00
Merge pull request #414 from logos-blockchain/arjentix/protect-from-pda-griefing-attack
feat: protect from public pda griefing attacks
This commit is contained in:
commit
b8bb6913be
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,6 +1,4 @@
|
|||||||
use nssa_core::program::{
|
use nssa_core::program::{AccountPostState, Claim, ProgramInput, ProgramOutput, read_nssa_inputs};
|
||||||
AccountPostState, DEFAULT_PROGRAM_ID, ProgramInput, ProgramOutput, read_nssa_inputs,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hello-world example program.
|
// Hello-world example program.
|
||||||
//
|
//
|
||||||
@ -45,13 +43,7 @@ fn main() {
|
|||||||
|
|
||||||
// Wrap the post state account values inside a `AccountPostState` instance.
|
// Wrap the post state account values inside a `AccountPostState` instance.
|
||||||
// This is used to forward the account claiming request if any
|
// This is used to forward the account claiming request if any
|
||||||
let post_state = if post_account.program_owner == DEFAULT_PROGRAM_ID {
|
let post_state = AccountPostState::new_claimed_if_default(post_account, Claim::Authorized);
|
||||||
// This produces a claim request
|
|
||||||
AccountPostState::new_claimed(post_account)
|
|
||||||
} else {
|
|
||||||
// This doesn't produce a claim request
|
|
||||||
AccountPostState::new(post_account)
|
|
||||||
};
|
|
||||||
|
|
||||||
// The output is a proposed state difference. It will only succeed if the pre states coincide
|
// The output is a proposed state difference. It will only succeed if the pre states coincide
|
||||||
// with the previous values of the accounts, and the transition to the post states conforms
|
// with the previous values of the accounts, and the transition to the post states conforms
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
use nssa_core::program::{
|
use nssa_core::program::{AccountPostState, Claim, ProgramInput, ProgramOutput, read_nssa_inputs};
|
||||||
AccountPostState, DEFAULT_PROGRAM_ID, ProgramInput, ProgramOutput, read_nssa_inputs,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hello-world with authorization example program.
|
// Hello-world with authorization example program.
|
||||||
//
|
//
|
||||||
@ -52,13 +50,7 @@ fn main() {
|
|||||||
|
|
||||||
// Wrap the post state account values inside a `AccountPostState` instance.
|
// Wrap the post state account values inside a `AccountPostState` instance.
|
||||||
// This is used to forward the account claiming request if any
|
// This is used to forward the account claiming request if any
|
||||||
let post_state = if post_account.program_owner == DEFAULT_PROGRAM_ID {
|
let post_state = AccountPostState::new_claimed_if_default(post_account, Claim::Authorized);
|
||||||
// This produces a claim request
|
|
||||||
AccountPostState::new_claimed(post_account)
|
|
||||||
} else {
|
|
||||||
// This doesn't produce a claim request
|
|
||||||
AccountPostState::new(post_account)
|
|
||||||
};
|
|
||||||
|
|
||||||
// The output is a proposed state difference. It will only succeed if the pre states coincide
|
// The output is a proposed state difference. It will only succeed if the pre states coincide
|
||||||
// with the previous values of the accounts, and the transition to the post states conforms
|
// with the previous values of the accounts, and the transition to the post states conforms
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata, Data},
|
account::{AccountWithMetadata, Data},
|
||||||
program::{
|
program::{AccountPostState, Claim, ProgramInput, ProgramOutput, read_nssa_inputs},
|
||||||
AccountPostState, DEFAULT_PROGRAM_ID, ProgramInput, ProgramOutput, read_nssa_inputs,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hello-world with write + move_data example program.
|
// Hello-world with write + move_data example program.
|
||||||
@ -26,16 +24,6 @@ const MOVE_DATA_FUNCTION_ID: u8 = 1;
|
|||||||
|
|
||||||
type Instruction = (u8, Vec<u8>);
|
type Instruction = (u8, Vec<u8>);
|
||||||
|
|
||||||
fn build_post_state(post_account: Account) -> AccountPostState {
|
|
||||||
if post_account.program_owner == DEFAULT_PROGRAM_ID {
|
|
||||||
// This produces a claim request
|
|
||||||
AccountPostState::new_claimed(post_account)
|
|
||||||
} else {
|
|
||||||
// This doesn't produce a claim request
|
|
||||||
AccountPostState::new(post_account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(pre_state: AccountWithMetadata, greeting: &[u8]) -> AccountPostState {
|
fn write(pre_state: AccountWithMetadata, greeting: &[u8]) -> AccountPostState {
|
||||||
// Construct the post state account values
|
// Construct the post state account values
|
||||||
let post_account = {
|
let post_account = {
|
||||||
@ -48,7 +36,7 @@ fn write(pre_state: AccountWithMetadata, greeting: &[u8]) -> AccountPostState {
|
|||||||
this
|
this
|
||||||
};
|
};
|
||||||
|
|
||||||
build_post_state(post_account)
|
AccountPostState::new_claimed_if_default(post_account, Claim::Authorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_data(from_pre: AccountWithMetadata, to_pre: AccountWithMetadata) -> Vec<AccountPostState> {
|
fn move_data(from_pre: AccountWithMetadata, to_pre: AccountWithMetadata) -> Vec<AccountPostState> {
|
||||||
@ -58,7 +46,7 @@ fn move_data(from_pre: AccountWithMetadata, to_pre: AccountWithMetadata) -> Vec<
|
|||||||
let from_post = {
|
let from_post = {
|
||||||
let mut this = from_pre.account;
|
let mut this = from_pre.account;
|
||||||
this.data = Data::default();
|
this.data = Data::default();
|
||||||
build_post_state(this)
|
AccountPostState::new_claimed_if_default(this, Claim::Authorized)
|
||||||
};
|
};
|
||||||
|
|
||||||
let to_post = {
|
let to_post = {
|
||||||
@ -68,7 +56,7 @@ fn move_data(from_pre: AccountWithMetadata, to_pre: AccountWithMetadata) -> Vec<
|
|||||||
this.data = bytes
|
this.data = bytes
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Data should fit within the allowed limits");
|
.expect("Data should fit within the allowed limits");
|
||||||
build_post_state(this)
|
AccountPostState::new_claimed_if_default(this, Claim::Authorized)
|
||||||
};
|
};
|
||||||
|
|
||||||
vec![from_post, to_post]
|
vec![from_post, to_post]
|
||||||
|
|||||||
@ -11,10 +11,13 @@ use integration_tests::{
|
|||||||
NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext,
|
NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use nssa::{AccountId, program::Program};
|
use nssa::program::Program;
|
||||||
use sequencer_service_rpc::RpcClient as _;
|
use sequencer_service_rpc::RpcClient as _;
|
||||||
use tokio::test;
|
use tokio::test;
|
||||||
use wallet::cli::Command;
|
use wallet::cli::{
|
||||||
|
Command, SubcommandReturnValue,
|
||||||
|
account::{AccountSubcommand, NewSubcommand},
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
async fn deploy_and_execute_program() -> Result<()> {
|
async fn deploy_and_execute_program() -> Result<()> {
|
||||||
@ -40,14 +43,31 @@ async fn deploy_and_execute_program() -> Result<()> {
|
|||||||
// logic)
|
// logic)
|
||||||
let bytecode = std::fs::read(binary_filepath)?;
|
let bytecode = std::fs::read(binary_filepath)?;
|
||||||
let data_changer = Program::new(bytecode)?;
|
let data_changer = Program::new(bytecode)?;
|
||||||
let account_id: AccountId = "11".repeat(16).parse()?;
|
|
||||||
|
let SubcommandReturnValue::RegisterAccount { account_id } = wallet::cli::execute_subcommand(
|
||||||
|
ctx.wallet_mut(),
|
||||||
|
Command::Account(AccountSubcommand::New(NewSubcommand::Public {
|
||||||
|
cci: None,
|
||||||
|
label: None,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
else {
|
||||||
|
panic!("Expected RegisterAccount return value");
|
||||||
|
};
|
||||||
|
|
||||||
|
let nonces = ctx.wallet().get_accounts_nonces(vec![account_id]).await?;
|
||||||
|
let private_key = ctx
|
||||||
|
.wallet()
|
||||||
|
.get_account_public_signing_key(account_id)
|
||||||
|
.unwrap();
|
||||||
let message = nssa::public_transaction::Message::try_new(
|
let message = nssa::public_transaction::Message::try_new(
|
||||||
data_changer.id(),
|
data_changer.id(),
|
||||||
vec![account_id],
|
vec![account_id],
|
||||||
vec![],
|
nonces,
|
||||||
vec![0],
|
vec![0],
|
||||||
)?;
|
)?;
|
||||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
|
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[private_key]);
|
||||||
let transaction = nssa::PublicTransaction::new(message, witness_set);
|
let transaction = nssa::PublicTransaction::new(message, witness_set);
|
||||||
let _response = ctx
|
let _response = ctx
|
||||||
.sequencer_client()
|
.sequencer_client()
|
||||||
@ -64,7 +84,7 @@ async fn deploy_and_execute_program() -> Result<()> {
|
|||||||
assert_eq!(post_state_account.program_owner, data_changer.id());
|
assert_eq!(post_state_account.program_owner, data_changer.id());
|
||||||
assert_eq!(post_state_account.balance, 0);
|
assert_eq!(post_state_account.balance, 0);
|
||||||
assert_eq!(post_state_account.data.as_ref(), &[0]);
|
assert_eq!(post_state_account.data.as_ref(), &[0]);
|
||||||
assert_eq!(post_state_account.nonce.0, 0);
|
assert_eq!(post_state_account.nonce.0, 1);
|
||||||
|
|
||||||
info!("Successfully deployed and executed program");
|
info!("Successfully deployed and executed program");
|
||||||
|
|
||||||
|
|||||||
@ -924,7 +924,7 @@ fn test_wallet_ffi_transfer_deshielded() -> Result<()> {
|
|||||||
let home = tempfile::tempdir()?;
|
let home = tempfile::tempdir()?;
|
||||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||||
let from: FfiBytes32 = (&ctx.ctx().existing_private_accounts()[0]).into();
|
let from: FfiBytes32 = (&ctx.ctx().existing_private_accounts()[0]).into();
|
||||||
let to = FfiBytes32::from_bytes([37; 32]);
|
let to: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[0]).into();
|
||||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||||
|
|
||||||
let mut transfer_result = FfiTransferResult::default();
|
let mut transfer_result = FfiTransferResult::default();
|
||||||
@ -967,7 +967,7 @@ fn test_wallet_ffi_transfer_deshielded() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(from_balance, 9900);
|
assert_eq!(from_balance, 9900);
|
||||||
assert_eq!(to_balance, 100);
|
assert_eq!(to_balance, 10100);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
wallet_ffi_free_transfer_result(&raw mut transfer_result);
|
wallet_ffi_free_transfer_result(&raw mut transfer_result);
|
||||||
|
|||||||
@ -22,7 +22,7 @@ pub struct ProgramInput<T> {
|
|||||||
/// Each program can derive up to `2^256` unique account IDs by choosing different
|
/// Each program can derive up to `2^256` unique account IDs by choosing different
|
||||||
/// seeds. PDAs allow programs to control namespaced account identifiers without
|
/// seeds. PDAs allow programs to control namespaced account identifiers without
|
||||||
/// collisions between programs.
|
/// collisions between programs.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct PdaSeed([u8; 32]);
|
pub struct PdaSeed([u8; 32]);
|
||||||
|
|
||||||
impl PdaSeed {
|
impl PdaSeed {
|
||||||
@ -91,11 +91,26 @@ impl ChainedCall {
|
|||||||
/// A post state may optionally request that the executing program
|
/// A post state may optionally request that the executing program
|
||||||
/// becomes the owner of the account (a “claim”). This is used to signal
|
/// becomes the owner of the account (a “claim”). This is used to signal
|
||||||
/// that the program intends to take ownership of the account.
|
/// that the program intends to take ownership of the account.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[cfg_attr(any(feature = "host", test), derive(PartialEq, Eq))]
|
#[cfg_attr(any(feature = "host", test), derive(PartialEq, Eq))]
|
||||||
pub struct AccountPostState {
|
pub struct AccountPostState {
|
||||||
account: Account,
|
account: Account,
|
||||||
claim: bool,
|
claim: Option<Claim>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A claim request for an account, indicating that the executing program intends to take ownership
|
||||||
|
/// of the account.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Claim {
|
||||||
|
/// The program requests ownership of the account which was authorized by the signer.
|
||||||
|
///
|
||||||
|
/// Note that it's possible to successfully execute program outputting [`AccountPostState`] with
|
||||||
|
/// `is_authorized == false` and `claim == Some(Claim::Authorized)`.
|
||||||
|
/// This will give no error if program had authorization in pre state and may be useful
|
||||||
|
/// if program decides to give up authorization for a chained call.
|
||||||
|
Authorized,
|
||||||
|
/// The program requests ownership of the account through a PDA.
|
||||||
|
Pda(PdaSeed),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountPostState {
|
impl AccountPostState {
|
||||||
@ -105,7 +120,7 @@ impl AccountPostState {
|
|||||||
pub const fn new(account: Account) -> Self {
|
pub const fn new(account: Account) -> Self {
|
||||||
Self {
|
Self {
|
||||||
account,
|
account,
|
||||||
claim: false,
|
claim: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,25 +128,27 @@ impl AccountPostState {
|
|||||||
/// This indicates that the executing program intends to claim the
|
/// This indicates that the executing program intends to claim the
|
||||||
/// account as its own and is allowed to mutate it.
|
/// account as its own and is allowed to mutate it.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new_claimed(account: Account) -> Self {
|
pub const fn new_claimed(account: Account, claim: Claim) -> Self {
|
||||||
Self {
|
Self {
|
||||||
account,
|
account,
|
||||||
claim: true,
|
claim: Some(claim),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a post state that requests ownership of the account
|
/// Creates a post state that requests ownership of the account
|
||||||
/// if the account's program owner is the default program ID.
|
/// if the account's program owner is the default program ID.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_claimed_if_default(account: Account) -> Self {
|
pub fn new_claimed_if_default(account: Account, claim: Claim) -> Self {
|
||||||
let claim = account.program_owner == DEFAULT_PROGRAM_ID;
|
let is_default_owner = account.program_owner == DEFAULT_PROGRAM_ID;
|
||||||
Self { account, claim }
|
Self {
|
||||||
|
account,
|
||||||
|
claim: is_default_owner.then_some(claim),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if this post state requests that the account
|
/// Returns whether this post state requires a claim.
|
||||||
/// be claimed (owned) by the executing program.
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn requires_claim(&self) -> bool {
|
pub const fn required_claim(&self) -> Option<Claim> {
|
||||||
self.claim
|
self.claim
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +159,7 @@ impl AccountPostState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the underlying account.
|
/// Returns the underlying account.
|
||||||
|
#[must_use]
|
||||||
pub const fn account_mut(&mut self) -> &mut Account {
|
pub const fn account_mut(&mut self) -> &mut Account {
|
||||||
&mut self.account
|
&mut self.account
|
||||||
}
|
}
|
||||||
@ -598,10 +616,10 @@ mod tests {
|
|||||||
nonce: 10_u128.into(),
|
nonce: 10_u128.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let account_post_state = AccountPostState::new_claimed(account.clone());
|
let account_post_state = AccountPostState::new_claimed(account.clone(), Claim::Authorized);
|
||||||
|
|
||||||
assert_eq!(account, account_post_state.account);
|
assert_eq!(account, account_post_state.account);
|
||||||
assert!(account_post_state.requires_claim());
|
assert_eq!(account_post_state.required_claim(), Some(Claim::Authorized));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -616,7 +634,7 @@ mod tests {
|
|||||||
let account_post_state = AccountPostState::new(account.clone());
|
let account_post_state = AccountPostState::new(account.clone());
|
||||||
|
|
||||||
assert_eq!(account, account_post_state.account);
|
assert_eq!(account, account_post_state.account);
|
||||||
assert!(!account_post_state.requires_claim());
|
assert!(account_post_state.required_claim().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use borsh::{BorshDeserialize, BorshSerialize};
|
|||||||
use log::debug;
|
use log::debug;
|
||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountId, AccountWithMetadata},
|
account::{Account, AccountId, AccountWithMetadata},
|
||||||
program::{BlockId, ChainedCall, DEFAULT_PROGRAM_ID, validate_execution},
|
program::{BlockId, ChainedCall, Claim, DEFAULT_PROGRAM_ID, validate_execution},
|
||||||
};
|
};
|
||||||
use sha2::{Digest as _, digest::FixedOutput as _};
|
use sha2::{Digest as _, digest::FixedOutput as _};
|
||||||
|
|
||||||
@ -157,6 +157,10 @@ impl PublicTransaction {
|
|||||||
&chained_call.pda_seeds,
|
&chained_call.pda_seeds,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let is_authorized = |account_id: &AccountId| {
|
||||||
|
signer_account_ids.contains(account_id) || authorized_pdas.contains(account_id)
|
||||||
|
};
|
||||||
|
|
||||||
for pre in &program_output.pre_states {
|
for pre in &program_output.pre_states {
|
||||||
let account_id = pre.account_id;
|
let account_id = pre.account_id;
|
||||||
// Check that the program output pre_states coincide with the values in the public
|
// Check that the program output pre_states coincide with the values in the public
|
||||||
@ -172,10 +176,8 @@ impl PublicTransaction {
|
|||||||
|
|
||||||
// Check that authorization flags are consistent with the provided ones or
|
// Check that authorization flags are consistent with the provided ones or
|
||||||
// authorized by program through the PDA mechanism
|
// authorized by program through the PDA mechanism
|
||||||
let is_authorized = signer_account_ids.contains(&account_id)
|
|
||||||
|| authorized_pdas.contains(&account_id);
|
|
||||||
ensure!(
|
ensure!(
|
||||||
pre.is_authorized == is_authorized,
|
pre.is_authorized == is_authorized(&account_id),
|
||||||
NssaError::InvalidProgramBehavior
|
NssaError::InvalidProgramBehavior
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -199,17 +201,35 @@ impl PublicTransaction {
|
|||||||
NssaError::OutOfValidityWindow
|
NssaError::OutOfValidityWindow
|
||||||
);
|
);
|
||||||
|
|
||||||
for post in program_output
|
for (i, post) in program_output.post_states.iter_mut().enumerate() {
|
||||||
.post_states
|
let Some(claim) = post.required_claim() else {
|
||||||
.iter_mut()
|
continue;
|
||||||
.filter(|post| post.requires_claim())
|
};
|
||||||
{
|
|
||||||
// The invoked program can only claim accounts with default program id.
|
// The invoked program can only claim accounts with default program id.
|
||||||
if post.account().program_owner == DEFAULT_PROGRAM_ID {
|
ensure!(
|
||||||
post.account_mut().program_owner = chained_call.program_id;
|
post.account().program_owner == DEFAULT_PROGRAM_ID,
|
||||||
} else {
|
NssaError::InvalidProgramBehavior
|
||||||
return Err(NssaError::InvalidProgramBehavior);
|
);
|
||||||
|
|
||||||
|
let account_id = program_output.pre_states[i].account_id;
|
||||||
|
|
||||||
|
match claim {
|
||||||
|
Claim::Authorized => {
|
||||||
|
// The program can only claim accounts that were authorized by the signer.
|
||||||
|
ensure!(
|
||||||
|
is_authorized(&account_id),
|
||||||
|
NssaError::InvalidProgramBehavior
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Claim::Pda(seed) => {
|
||||||
|
// The program can only claim accounts that correspond to the PDAs it is
|
||||||
|
// authorized to claim.
|
||||||
|
let pda = AccountId::from((&chained_call.program_id, &seed));
|
||||||
|
ensure!(account_id == pda, NssaError::InvalidProgramBehavior);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
post.account_mut().program_owner = chained_call.program_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the state diff
|
// Update the state diff
|
||||||
|
|||||||
@ -456,16 +456,19 @@ pub mod tests {
|
|||||||
fn transfer_transaction(
|
fn transfer_transaction(
|
||||||
from: AccountId,
|
from: AccountId,
|
||||||
from_key: &PrivateKey,
|
from_key: &PrivateKey,
|
||||||
nonce: u128,
|
from_nonce: u128,
|
||||||
to: AccountId,
|
to: AccountId,
|
||||||
|
to_key: &PrivateKey,
|
||||||
|
to_nonce: u128,
|
||||||
balance: u128,
|
balance: u128,
|
||||||
) -> PublicTransaction {
|
) -> PublicTransaction {
|
||||||
let account_ids = vec![from, to];
|
let account_ids = vec![from, to];
|
||||||
let nonces = vec![Nonce(nonce)];
|
let nonces = vec![Nonce(from_nonce), Nonce(to_nonce)];
|
||||||
let program_id = Program::authenticated_transfer_program().id();
|
let program_id = Program::authenticated_transfer_program().id();
|
||||||
let message =
|
let message =
|
||||||
public_transaction::Message::try_new(program_id, account_ids, nonces, balance).unwrap();
|
public_transaction::Message::try_new(program_id, account_ids, nonces, balance).unwrap();
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[from_key]);
|
let witness_set =
|
||||||
|
public_transaction::WitnessSet::for_message(&message, &[from_key, to_key]);
|
||||||
PublicTransaction::new(message, witness_set)
|
PublicTransaction::new(message, witness_set)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,17 +570,18 @@ pub mod tests {
|
|||||||
let initial_data = [(account_id, 100)];
|
let initial_data = [(account_id, 100)];
|
||||||
let mut state = V03State::new_with_genesis_accounts(&initial_data, &[]);
|
let mut state = V03State::new_with_genesis_accounts(&initial_data, &[]);
|
||||||
let from = account_id;
|
let from = account_id;
|
||||||
let to = AccountId::new([2; 32]);
|
let to_key = PrivateKey::try_new([2; 32]).unwrap();
|
||||||
|
let to = AccountId::from(&PublicKey::new_from_private_key(&to_key));
|
||||||
assert_eq!(state.get_account_by_id(to), Account::default());
|
assert_eq!(state.get_account_by_id(to), Account::default());
|
||||||
let balance_to_move = 5;
|
let balance_to_move = 5;
|
||||||
|
|
||||||
let tx = transfer_transaction(from, &key, 0, to, balance_to_move);
|
let tx = transfer_transaction(from, &key, 0, to, &to_key, 0, balance_to_move);
|
||||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||||
|
|
||||||
assert_eq!(state.get_account_by_id(from).balance, 95);
|
assert_eq!(state.get_account_by_id(from).balance, 95);
|
||||||
assert_eq!(state.get_account_by_id(to).balance, 5);
|
assert_eq!(state.get_account_by_id(to).balance, 5);
|
||||||
assert_eq!(state.get_account_by_id(from).nonce, Nonce(1));
|
assert_eq!(state.get_account_by_id(from).nonce, Nonce(1));
|
||||||
assert_eq!(state.get_account_by_id(to).nonce, Nonce(0));
|
assert_eq!(state.get_account_by_id(to).nonce, Nonce(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -588,11 +592,12 @@ pub mod tests {
|
|||||||
let mut state = V03State::new_with_genesis_accounts(&initial_data, &[]);
|
let mut state = V03State::new_with_genesis_accounts(&initial_data, &[]);
|
||||||
let from = account_id;
|
let from = account_id;
|
||||||
let from_key = key;
|
let from_key = key;
|
||||||
let to = AccountId::new([2; 32]);
|
let to_key = PrivateKey::try_new([2; 32]).unwrap();
|
||||||
|
let to = AccountId::from(&PublicKey::new_from_private_key(&to_key));
|
||||||
let balance_to_move = 101;
|
let balance_to_move = 101;
|
||||||
assert!(state.get_account_by_id(from).balance < balance_to_move);
|
assert!(state.get_account_by_id(from).balance < balance_to_move);
|
||||||
|
|
||||||
let tx = transfer_transaction(from, &from_key, 0, to, balance_to_move);
|
let tx = transfer_transaction(from, &from_key, 0, to, &to_key, 0, balance_to_move);
|
||||||
let result = state.transition_from_public_transaction(&tx, 1);
|
let result = state.transition_from_public_transaction(&tx, 1);
|
||||||
|
|
||||||
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
|
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
|
||||||
@ -613,16 +618,17 @@ pub mod tests {
|
|||||||
let from = account_id2;
|
let from = account_id2;
|
||||||
let from_key = key2;
|
let from_key = key2;
|
||||||
let to = account_id1;
|
let to = account_id1;
|
||||||
|
let to_key = key1;
|
||||||
assert_ne!(state.get_account_by_id(to), Account::default());
|
assert_ne!(state.get_account_by_id(to), Account::default());
|
||||||
let balance_to_move = 8;
|
let balance_to_move = 8;
|
||||||
|
|
||||||
let tx = transfer_transaction(from, &from_key, 0, to, balance_to_move);
|
let tx = transfer_transaction(from, &from_key, 0, to, &to_key, 0, balance_to_move);
|
||||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||||
|
|
||||||
assert_eq!(state.get_account_by_id(from).balance, 192);
|
assert_eq!(state.get_account_by_id(from).balance, 192);
|
||||||
assert_eq!(state.get_account_by_id(to).balance, 108);
|
assert_eq!(state.get_account_by_id(to).balance, 108);
|
||||||
assert_eq!(state.get_account_by_id(from).nonce, Nonce(1));
|
assert_eq!(state.get_account_by_id(from).nonce, Nonce(1));
|
||||||
assert_eq!(state.get_account_by_id(to).nonce, Nonce(0));
|
assert_eq!(state.get_account_by_id(to).nonce, Nonce(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -633,21 +639,38 @@ pub mod tests {
|
|||||||
let account_id2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
let account_id2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
||||||
let initial_data = [(account_id1, 100)];
|
let initial_data = [(account_id1, 100)];
|
||||||
let mut state = V03State::new_with_genesis_accounts(&initial_data, &[]);
|
let mut state = V03State::new_with_genesis_accounts(&initial_data, &[]);
|
||||||
let account_id3 = AccountId::new([3; 32]);
|
let key3 = PrivateKey::try_new([3; 32]).unwrap();
|
||||||
|
let account_id3 = AccountId::from(&PublicKey::new_from_private_key(&key3));
|
||||||
let balance_to_move = 5;
|
let balance_to_move = 5;
|
||||||
|
|
||||||
let tx = transfer_transaction(account_id1, &key1, 0, account_id2, balance_to_move);
|
let tx = transfer_transaction(
|
||||||
|
account_id1,
|
||||||
|
&key1,
|
||||||
|
0,
|
||||||
|
account_id2,
|
||||||
|
&key2,
|
||||||
|
0,
|
||||||
|
balance_to_move,
|
||||||
|
);
|
||||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||||
let balance_to_move = 3;
|
let balance_to_move = 3;
|
||||||
let tx = transfer_transaction(account_id2, &key2, 0, account_id3, balance_to_move);
|
let tx = transfer_transaction(
|
||||||
|
account_id2,
|
||||||
|
&key2,
|
||||||
|
1,
|
||||||
|
account_id3,
|
||||||
|
&key3,
|
||||||
|
0,
|
||||||
|
balance_to_move,
|
||||||
|
);
|
||||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||||
|
|
||||||
assert_eq!(state.get_account_by_id(account_id1).balance, 95);
|
assert_eq!(state.get_account_by_id(account_id1).balance, 95);
|
||||||
assert_eq!(state.get_account_by_id(account_id2).balance, 2);
|
assert_eq!(state.get_account_by_id(account_id2).balance, 2);
|
||||||
assert_eq!(state.get_account_by_id(account_id3).balance, 3);
|
assert_eq!(state.get_account_by_id(account_id3).balance, 3);
|
||||||
assert_eq!(state.get_account_by_id(account_id1).nonce, Nonce(1));
|
assert_eq!(state.get_account_by_id(account_id1).nonce, Nonce(1));
|
||||||
assert_eq!(state.get_account_by_id(account_id2).nonce, Nonce(1));
|
assert_eq!(state.get_account_by_id(account_id2).nonce, Nonce(2));
|
||||||
assert_eq!(state.get_account_by_id(account_id3).nonce, Nonce(0));
|
assert_eq!(state.get_account_by_id(account_id3).nonce, Nonce(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -2212,15 +2235,14 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn claiming_mechanism() {
|
fn claiming_mechanism() {
|
||||||
let program = Program::authenticated_transfer_program();
|
let program = Program::authenticated_transfer_program();
|
||||||
let key = PrivateKey::try_new([1; 32]).unwrap();
|
let from_key = PrivateKey::try_new([1; 32]).unwrap();
|
||||||
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
let from = AccountId::from(&PublicKey::new_from_private_key(&from_key));
|
||||||
let initial_balance = 100;
|
let initial_balance = 100;
|
||||||
let initial_data = [(account_id, initial_balance)];
|
let initial_data = [(from, initial_balance)];
|
||||||
let mut state =
|
let mut state =
|
||||||
V03State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
V03State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||||
let from = account_id;
|
let to_key = PrivateKey::try_new([2; 32]).unwrap();
|
||||||
let from_key = key;
|
let to = AccountId::from(&PublicKey::new_from_private_key(&to_key));
|
||||||
let to = AccountId::new([2; 32]);
|
|
||||||
let amount: u128 = 37;
|
let amount: u128 = 37;
|
||||||
|
|
||||||
// Check the recipient is an uninitialized account
|
// Check the recipient is an uninitialized account
|
||||||
@ -2229,17 +2251,19 @@ pub mod tests {
|
|||||||
let expected_recipient_post = Account {
|
let expected_recipient_post = Account {
|
||||||
program_owner: program.id(),
|
program_owner: program.id(),
|
||||||
balance: amount,
|
balance: amount,
|
||||||
|
nonce: Nonce(1),
|
||||||
..Account::default()
|
..Account::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let message = public_transaction::Message::try_new(
|
let message = public_transaction::Message::try_new(
|
||||||
program.id(),
|
program.id(),
|
||||||
vec![from, to],
|
vec![from, to],
|
||||||
vec![Nonce(0)],
|
vec![Nonce(0), Nonce(0)],
|
||||||
amount,
|
amount,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
let witness_set =
|
||||||
|
public_transaction::WitnessSet::for_message(&message, &[&from_key, &to_key]);
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||||
@ -2249,6 +2273,58 @@ pub mod tests {
|
|||||||
assert_eq!(recipient_post, expected_recipient_post);
|
assert_eq!(recipient_post, expected_recipient_post);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unauthorized_public_account_claiming_fails() {
|
||||||
|
let program = Program::authenticated_transfer_program();
|
||||||
|
let account_key = PrivateKey::try_new([9; 32]).unwrap();
|
||||||
|
let account_id = AccountId::from(&PublicKey::new_from_private_key(&account_key));
|
||||||
|
let mut state = V03State::new_with_genesis_accounts(&[], &[]);
|
||||||
|
|
||||||
|
assert_eq!(state.get_account_by_id(account_id), Account::default());
|
||||||
|
|
||||||
|
let message =
|
||||||
|
public_transaction::Message::try_new(program.id(), vec![account_id], vec![], 0_u128)
|
||||||
|
.unwrap();
|
||||||
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||||
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
let result = state.transition_from_public_transaction(&tx, 1);
|
||||||
|
|
||||||
|
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
|
||||||
|
assert_eq!(state.get_account_by_id(account_id), Account::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn authorized_public_account_claiming_succeeds() {
|
||||||
|
let program = Program::authenticated_transfer_program();
|
||||||
|
let account_key = PrivateKey::try_new([10; 32]).unwrap();
|
||||||
|
let account_id = AccountId::from(&PublicKey::new_from_private_key(&account_key));
|
||||||
|
let mut state = V03State::new_with_genesis_accounts(&[], &[]);
|
||||||
|
|
||||||
|
assert_eq!(state.get_account_by_id(account_id), Account::default());
|
||||||
|
|
||||||
|
let message = public_transaction::Message::try_new(
|
||||||
|
program.id(),
|
||||||
|
vec![account_id],
|
||||||
|
vec![Nonce(0)],
|
||||||
|
0_u128,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&account_key]);
|
||||||
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
state.get_account_by_id(account_id),
|
||||||
|
Account {
|
||||||
|
program_owner: program.id(),
|
||||||
|
nonce: Nonce(1),
|
||||||
|
..Account::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn public_chained_call() {
|
fn public_chained_call() {
|
||||||
let program = Program::chain_caller();
|
let program = Program::chain_caller();
|
||||||
@ -2382,15 +2458,14 @@ pub mod tests {
|
|||||||
// program and not the chained_caller program.
|
// program and not the chained_caller program.
|
||||||
let chain_caller = Program::chain_caller();
|
let chain_caller = Program::chain_caller();
|
||||||
let auth_transfer = Program::authenticated_transfer_program();
|
let auth_transfer = Program::authenticated_transfer_program();
|
||||||
let key = PrivateKey::try_new([1; 32]).unwrap();
|
let from_key = PrivateKey::try_new([1; 32]).unwrap();
|
||||||
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
let from = AccountId::from(&PublicKey::new_from_private_key(&from_key));
|
||||||
let initial_balance = 100;
|
let initial_balance = 100;
|
||||||
let initial_data = [(account_id, initial_balance)];
|
let initial_data = [(from, initial_balance)];
|
||||||
let mut state =
|
let mut state =
|
||||||
V03State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
V03State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||||
let from = account_id;
|
let to_key = PrivateKey::try_new([2; 32]).unwrap();
|
||||||
let from_key = key;
|
let to = AccountId::from(&PublicKey::new_from_private_key(&to_key));
|
||||||
let to = AccountId::new([2; 32]);
|
|
||||||
let amount: u128 = 37;
|
let amount: u128 = 37;
|
||||||
|
|
||||||
// Check the recipient is an uninitialized account
|
// Check the recipient is an uninitialized account
|
||||||
@ -2400,6 +2475,7 @@ pub mod tests {
|
|||||||
// The expected program owner is the authenticated transfer program
|
// The expected program owner is the authenticated transfer program
|
||||||
program_owner: auth_transfer.id(),
|
program_owner: auth_transfer.id(),
|
||||||
balance: amount,
|
balance: amount,
|
||||||
|
nonce: Nonce(1),
|
||||||
..Account::default()
|
..Account::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2415,11 +2491,12 @@ pub mod tests {
|
|||||||
chain_caller.id(),
|
chain_caller.id(),
|
||||||
vec![to, from], // The chain_caller program permutes the account order in the chain
|
vec![to, from], // The chain_caller program permutes the account order in the chain
|
||||||
// call
|
// call
|
||||||
vec![Nonce(0)],
|
vec![Nonce(0), Nonce(0)],
|
||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
let witness_set =
|
||||||
|
public_transaction::WitnessSet::for_message(&message, &[&from_key, &to_key]);
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
state.transition_from_public_transaction(&tx, 1).unwrap();
|
||||||
@ -2430,6 +2507,88 @@ pub mod tests {
|
|||||||
assert_eq!(to_post, expected_to_post);
|
assert_eq!(to_post, expected_to_post);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unauthorized_public_account_claiming_fails_when_executed_privately() {
|
||||||
|
let program = Program::authenticated_transfer_program();
|
||||||
|
let account_id = AccountId::new([11; 32]);
|
||||||
|
let public_account = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||||
|
|
||||||
|
let result = execute_and_prove(
|
||||||
|
vec![public_account],
|
||||||
|
Program::serialize_instruction(0_u128).unwrap(),
|
||||||
|
vec![0],
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
&program.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(matches!(result, Err(NssaError::ProgramProveFailed(_))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn authorized_public_account_claiming_succeeds_when_executed_privately() {
|
||||||
|
let program = Program::authenticated_transfer_program();
|
||||||
|
let program_id = program.id();
|
||||||
|
let sender_keys = test_private_account_keys_1();
|
||||||
|
let sender_private_account = Account {
|
||||||
|
program_owner: program_id,
|
||||||
|
balance: 100,
|
||||||
|
..Account::default()
|
||||||
|
};
|
||||||
|
let sender_commitment = Commitment::new(&sender_keys.npk(), &sender_private_account);
|
||||||
|
let mut state =
|
||||||
|
V03State::new_with_genesis_accounts(&[], std::slice::from_ref(&sender_commitment));
|
||||||
|
let sender_pre = AccountWithMetadata::new(sender_private_account, true, &sender_keys.npk());
|
||||||
|
let recipient_private_key = PrivateKey::try_new([2; 32]).unwrap();
|
||||||
|
let recipient_account_id =
|
||||||
|
AccountId::from(&PublicKey::new_from_private_key(&recipient_private_key));
|
||||||
|
let recipient_pre =
|
||||||
|
AccountWithMetadata::new(Account::default(), true, recipient_account_id);
|
||||||
|
let esk = [5; 32];
|
||||||
|
let shared_secret = SharedSecretKey::new(&esk, &sender_keys.vpk());
|
||||||
|
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||||
|
|
||||||
|
let (output, proof) = execute_and_prove(
|
||||||
|
vec![sender_pre, recipient_pre],
|
||||||
|
Program::serialize_instruction(37_u128).unwrap(),
|
||||||
|
vec![1, 0],
|
||||||
|
vec![(sender_keys.npk(), shared_secret)],
|
||||||
|
vec![sender_keys.nsk],
|
||||||
|
vec![state.get_proof_for_commitment(&sender_commitment)],
|
||||||
|
&program.into(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let message = Message::try_from_circuit_output(
|
||||||
|
vec![recipient_account_id],
|
||||||
|
vec![Nonce(0)],
|
||||||
|
vec![(sender_keys.npk(), sender_keys.vpk(), epk)],
|
||||||
|
output,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_private_key]);
|
||||||
|
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
state
|
||||||
|
.transition_from_privacy_preserving_transaction(&tx, 1)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let nullifier = Nullifier::for_account_update(&sender_commitment, &sender_keys.nsk);
|
||||||
|
assert!(state.private_state.1.contains(&nullifier));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
state.get_account_by_id(recipient_account_id),
|
||||||
|
Account {
|
||||||
|
program_owner: program_id,
|
||||||
|
balance: 37,
|
||||||
|
nonce: Nonce(1),
|
||||||
|
..Account::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test_case::test_case(1; "single call")]
|
#[test_case::test_case(1; "single call")]
|
||||||
#[test_case::test_case(2; "two calls")]
|
#[test_case::test_case(2; "two calls")]
|
||||||
fn private_chained_call(number_of_calls: u32) {
|
fn private_chained_call(number_of_calls: u32) {
|
||||||
@ -2547,85 +2706,6 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pda_mechanism_with_pinata_token_program() {
|
|
||||||
let pinata_token = Program::pinata_token();
|
|
||||||
let token = Program::token();
|
|
||||||
|
|
||||||
let pinata_definition_id = AccountId::new([1; 32]);
|
|
||||||
let pinata_token_definition_id = AccountId::new([2; 32]);
|
|
||||||
// Total supply of pinata token will be in an account under a PDA.
|
|
||||||
let pinata_token_holding_id = AccountId::from((&pinata_token.id(), &PdaSeed::new([0; 32])));
|
|
||||||
let winner_token_holding_id = AccountId::new([3; 32]);
|
|
||||||
|
|
||||||
let expected_winner_account_holding = token_core::TokenHolding::Fungible {
|
|
||||||
definition_id: pinata_token_definition_id,
|
|
||||||
balance: 150,
|
|
||||||
};
|
|
||||||
let expected_winner_token_holding_post = Account {
|
|
||||||
program_owner: token.id(),
|
|
||||||
data: Data::from(&expected_winner_account_holding),
|
|
||||||
..Account::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut state = V03State::new_with_genesis_accounts(&[], &[]);
|
|
||||||
state.add_pinata_token_program(pinata_definition_id);
|
|
||||||
|
|
||||||
// Execution of the token program to create new token for the pinata token
|
|
||||||
// definition and supply accounts
|
|
||||||
let total_supply: u128 = 10_000_000;
|
|
||||||
let instruction = token_core::Instruction::NewFungibleDefinition {
|
|
||||||
name: String::from("PINATA"),
|
|
||||||
total_supply,
|
|
||||||
};
|
|
||||||
let message = public_transaction::Message::try_new(
|
|
||||||
token.id(),
|
|
||||||
vec![pinata_token_definition_id, pinata_token_holding_id],
|
|
||||||
vec![],
|
|
||||||
instruction,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
|
||||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
|
||||||
|
|
||||||
// Execution of winner's token holding account initialization
|
|
||||||
let instruction = token_core::Instruction::InitializeAccount;
|
|
||||||
let message = public_transaction::Message::try_new(
|
|
||||||
token.id(),
|
|
||||||
vec![pinata_token_definition_id, winner_token_holding_id],
|
|
||||||
vec![],
|
|
||||||
instruction,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
|
||||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
|
||||||
|
|
||||||
// Submit a solution to the pinata program to claim the prize
|
|
||||||
let solution: u128 = 989_106;
|
|
||||||
let message = public_transaction::Message::try_new(
|
|
||||||
pinata_token.id(),
|
|
||||||
vec![
|
|
||||||
pinata_definition_id,
|
|
||||||
pinata_token_holding_id,
|
|
||||||
winner_token_holding_id,
|
|
||||||
],
|
|
||||||
vec![],
|
|
||||||
solution,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
|
||||||
state.transition_from_public_transaction(&tx, 1).unwrap();
|
|
||||||
|
|
||||||
let winner_token_holding_post = state.get_account_by_id(winner_token_holding_id);
|
|
||||||
assert_eq!(
|
|
||||||
winner_token_holding_post,
|
|
||||||
expected_winner_token_holding_post
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn claiming_mechanism_cannot_claim_initialied_accounts() {
|
fn claiming_mechanism_cannot_claim_initialied_accounts() {
|
||||||
let claimer = Program::claimer();
|
let claimer = Program::claimer();
|
||||||
@ -2769,6 +2849,53 @@ pub mod tests {
|
|||||||
assert!(state.private_state.1.contains(&nullifier));
|
assert!(state.private_state.1.contains(&nullifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn private_unauthorized_uninitialized_account_can_still_be_claimed() {
|
||||||
|
let mut state = V03State::new_with_genesis_accounts(&[], &[]).with_test_programs();
|
||||||
|
|
||||||
|
let private_keys = test_private_account_keys_1();
|
||||||
|
// This is intentional: claim authorization was introduced to protect public accounts,
|
||||||
|
// especially PDAs. Private PDAs are not useful in practice because there is no way to
|
||||||
|
// operate them without the corresponding private keys, so unauthorized private claiming
|
||||||
|
// remains allowed.
|
||||||
|
let unauthorized_account =
|
||||||
|
AccountWithMetadata::new(Account::default(), false, &private_keys.npk());
|
||||||
|
|
||||||
|
let program = Program::claimer();
|
||||||
|
let esk = [5; 32];
|
||||||
|
let shared_secret = SharedSecretKey::new(&esk, &private_keys.vpk());
|
||||||
|
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||||
|
|
||||||
|
let (output, proof) = execute_and_prove(
|
||||||
|
vec![unauthorized_account],
|
||||||
|
Program::serialize_instruction(0_u128).unwrap(),
|
||||||
|
vec![2],
|
||||||
|
vec![(private_keys.npk(), shared_secret)],
|
||||||
|
vec![],
|
||||||
|
vec![None],
|
||||||
|
&program.into(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let message = Message::try_from_circuit_output(
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
vec![(private_keys.npk(), private_keys.vpk(), epk)],
|
||||||
|
output,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||||
|
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
state
|
||||||
|
.transition_from_privacy_preserving_transaction(&tx, 1)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let nullifier = Nullifier::for_account_initialization(&private_keys.npk());
|
||||||
|
assert!(state.private_state.1.contains(&nullifier));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn private_account_claimed_then_used_without_init_flag_should_fail() {
|
fn private_account_claimed_then_used_without_init_flag_should_fail() {
|
||||||
let mut state = V03State::new_with_genesis_accounts(&[], &[]).with_test_programs();
|
let mut state = V03State::new_with_genesis_accounts(&[], &[]).with_test_programs();
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata},
|
account::{Account, AccountWithMetadata},
|
||||||
program::{
|
program::{
|
||||||
AccountPostState, DEFAULT_PROGRAM_ID, ProgramInput, ProgramOutput, read_nssa_inputs,
|
AccountPostState, Claim, DEFAULT_PROGRAM_ID, ProgramInput, ProgramOutput, read_nssa_inputs,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Initializes a default account under the ownership of this program.
|
/// Initializes a default account under the ownership of this program.
|
||||||
fn initialize_account(pre_state: AccountWithMetadata) -> AccountPostState {
|
fn initialize_account(pre_state: AccountWithMetadata) -> AccountPostState {
|
||||||
let account_to_claim = AccountPostState::new_claimed(pre_state.account);
|
let account_to_claim = AccountPostState::new_claimed(pre_state.account, Claim::Authorized);
|
||||||
let is_authorized = pre_state.is_authorized;
|
let is_authorized = pre_state.is_authorized;
|
||||||
|
|
||||||
// Continue only if the account to claim has default values
|
// Continue only if the account to claim has default values
|
||||||
@ -52,7 +52,7 @@ fn transfer(
|
|||||||
|
|
||||||
// Claim recipient account if it has default program owner
|
// Claim recipient account if it has default program owner
|
||||||
if recipient_post_account.program_owner == DEFAULT_PROGRAM_ID {
|
if recipient_post_account.program_owner == DEFAULT_PROGRAM_ID {
|
||||||
AccountPostState::new_claimed(recipient_post_account)
|
AccountPostState::new_claimed(recipient_post_account, Claim::Authorized)
|
||||||
} else {
|
} else {
|
||||||
AccountPostState::new(recipient_post_account)
|
AccountPostState::new(recipient_post_account)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use nssa_core::program::{AccountPostState, ProgramInput, ProgramOutput, read_nssa_inputs};
|
use nssa_core::program::{AccountPostState, Claim, ProgramInput, ProgramOutput, read_nssa_inputs};
|
||||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||||
|
|
||||||
const PRIZE: u128 = 150;
|
const PRIZE: u128 = 150;
|
||||||
@ -82,7 +82,7 @@ fn main() {
|
|||||||
instruction_words,
|
instruction_words,
|
||||||
vec![pinata, winner],
|
vec![pinata, winner],
|
||||||
vec![
|
vec![
|
||||||
AccountPostState::new_claimed_if_default(pinata_post),
|
AccountPostState::new_claimed_if_default(pinata_post, Claim::Authorized),
|
||||||
AccountPostState::new(winner_post),
|
AccountPostState::new(winner_post),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -10,8 +10,8 @@ use nssa_core::{
|
|||||||
account::{Account, AccountId, AccountWithMetadata, Nonce},
|
account::{Account, AccountId, AccountWithMetadata, Nonce},
|
||||||
compute_digest_for_path,
|
compute_digest_for_path,
|
||||||
program::{
|
program::{
|
||||||
AccountPostState, ChainedCall, DEFAULT_PROGRAM_ID, MAX_NUMBER_CHAINED_CALLS, ProgramId,
|
AccountPostState, ChainedCall, Claim, DEFAULT_PROGRAM_ID, MAX_NUMBER_CHAINED_CALLS,
|
||||||
ProgramOutput, ValidityWindow, validate_execution,
|
ProgramId, ProgramOutput, ValidityWindow, validate_execution,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use risc0_zkvm::{guest::env, serde::to_vec};
|
use risc0_zkvm::{guest::env, serde::to_vec};
|
||||||
@ -25,7 +25,11 @@ struct ExecutionState {
|
|||||||
|
|
||||||
impl ExecutionState {
|
impl ExecutionState {
|
||||||
/// Validate program outputs and derive the overall execution state.
|
/// Validate program outputs and derive the overall execution state.
|
||||||
pub fn derive_from_outputs(program_id: ProgramId, program_outputs: Vec<ProgramOutput>) -> Self {
|
pub fn derive_from_outputs(
|
||||||
|
visibility_mask: &[u8],
|
||||||
|
program_id: ProgramId,
|
||||||
|
program_outputs: Vec<ProgramOutput>,
|
||||||
|
) -> Self {
|
||||||
let valid_from_id = program_outputs
|
let valid_from_id = program_outputs
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|output| output.validity_window.start())
|
.filter_map(|output| output.validity_window.start())
|
||||||
@ -102,6 +106,7 @@ impl ExecutionState {
|
|||||||
&chained_call.pda_seeds,
|
&chained_call.pda_seeds,
|
||||||
);
|
);
|
||||||
execution_state.validate_and_sync_states(
|
execution_state.validate_and_sync_states(
|
||||||
|
visibility_mask,
|
||||||
chained_call.program_id,
|
chained_call.program_id,
|
||||||
&authorized_pdas,
|
&authorized_pdas,
|
||||||
program_output.pre_states,
|
program_output.pre_states,
|
||||||
@ -134,7 +139,7 @@ impl ExecutionState {
|
|||||||
{
|
{
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
post.program_owner, DEFAULT_PROGRAM_ID,
|
post.program_owner, DEFAULT_PROGRAM_ID,
|
||||||
"Account {account_id:?} was modified but not claimed"
|
"Account {account_id} was modified but not claimed"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,6 +149,7 @@ impl ExecutionState {
|
|||||||
/// Validate program pre and post states and populate the execution state.
|
/// Validate program pre and post states and populate the execution state.
|
||||||
fn validate_and_sync_states(
|
fn validate_and_sync_states(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
visibility_mask: &[u8],
|
||||||
program_id: ProgramId,
|
program_id: ProgramId,
|
||||||
authorized_pdas: &HashSet<AccountId>,
|
authorized_pdas: &HashSet<AccountId>,
|
||||||
pre_states: Vec<AccountWithMetadata>,
|
pre_states: Vec<AccountWithMetadata>,
|
||||||
@ -151,14 +157,25 @@ impl ExecutionState {
|
|||||||
) {
|
) {
|
||||||
for (pre, mut post) in pre_states.into_iter().zip(post_states) {
|
for (pre, mut post) in pre_states.into_iter().zip(post_states) {
|
||||||
let pre_account_id = pre.account_id;
|
let pre_account_id = pre.account_id;
|
||||||
|
let pre_is_authorized = pre.is_authorized;
|
||||||
let post_states_entry = self.post_states.entry(pre.account_id);
|
let post_states_entry = self.post_states.entry(pre.account_id);
|
||||||
match &post_states_entry {
|
match &post_states_entry {
|
||||||
Entry::Occupied(occupied) => {
|
Entry::Occupied(occupied) => {
|
||||||
|
#[expect(
|
||||||
|
clippy::shadow_unrelated,
|
||||||
|
reason = "Shadowing is intentional to use all fields"
|
||||||
|
)]
|
||||||
|
let AccountWithMetadata {
|
||||||
|
account: pre_account,
|
||||||
|
account_id: pre_account_id,
|
||||||
|
is_authorized: pre_is_authorized,
|
||||||
|
} = pre;
|
||||||
|
|
||||||
// Ensure that new pre state is the same as known post state
|
// Ensure that new pre state is the same as known post state
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
occupied.get(),
|
occupied.get(),
|
||||||
&pre.account,
|
&pre_account,
|
||||||
"Inconsistent pre state for account {pre_account_id:?}",
|
"Inconsistent pre state for account {pre_account_id}",
|
||||||
);
|
);
|
||||||
|
|
||||||
let previous_is_authorized = self
|
let previous_is_authorized = self
|
||||||
@ -167,7 +184,7 @@ impl ExecutionState {
|
|||||||
.find(|acc| acc.account_id == pre_account_id)
|
.find(|acc| acc.account_id == pre_account_id)
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|| panic!(
|
|| panic!(
|
||||||
"Pre state must exist in execution state for account {pre_account_id:?}",
|
"Pre state must exist in execution state for account {pre_account_id}",
|
||||||
),
|
),
|
||||||
|acc| acc.is_authorized
|
|acc| acc.is_authorized
|
||||||
);
|
);
|
||||||
@ -176,22 +193,57 @@ impl ExecutionState {
|
|||||||
previous_is_authorized || authorized_pdas.contains(&pre_account_id);
|
previous_is_authorized || authorized_pdas.contains(&pre_account_id);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pre.is_authorized, is_authorized,
|
pre_is_authorized, is_authorized,
|
||||||
"Inconsistent authorization for account {pre_account_id:?}",
|
"Inconsistent authorization for account {pre_account_id}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Entry::Vacant(_) => {
|
Entry::Vacant(_) => {
|
||||||
|
// Pre state for the initial call
|
||||||
self.pre_states.push(pre);
|
self.pre_states.push(pre);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if post.requires_claim() {
|
if let Some(claim) = post.required_claim() {
|
||||||
// The invoked program can only claim accounts with default program id.
|
// The invoked program can only claim accounts with default program id.
|
||||||
if post.account().program_owner == DEFAULT_PROGRAM_ID {
|
assert_eq!(
|
||||||
post.account_mut().program_owner = program_id;
|
post.account().program_owner,
|
||||||
|
DEFAULT_PROGRAM_ID,
|
||||||
|
"Cannot claim an initialized account {pre_account_id}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let pre_state_position = self
|
||||||
|
.pre_states
|
||||||
|
.iter()
|
||||||
|
.position(|acc| acc.account_id == pre_account_id)
|
||||||
|
.expect("Pre state must exist at this point");
|
||||||
|
|
||||||
|
let is_public_account = visibility_mask[pre_state_position] == 0;
|
||||||
|
if is_public_account {
|
||||||
|
match claim {
|
||||||
|
Claim::Authorized => {
|
||||||
|
// Note: no need to check authorized pdas because we have already
|
||||||
|
// checked consistency of authorization above.
|
||||||
|
assert!(
|
||||||
|
pre_is_authorized,
|
||||||
|
"Cannot claim unauthorized account {pre_account_id}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Claim::Pda(seed) => {
|
||||||
|
let pda = AccountId::from((&program_id, &seed));
|
||||||
|
assert_eq!(
|
||||||
|
pre_account_id, pda,
|
||||||
|
"Invalid PDA claim for account {pre_account_id} which does not match derived PDA {pda}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Cannot claim an initialized account {pre_account_id:?}");
|
// We don't care about the exact claim mechanism for private accounts.
|
||||||
|
// This is because the main reason to have it is to protect against PDA griefing
|
||||||
|
// attacks in public execution, while private PDA doesn't make much sense
|
||||||
|
// anyway.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
post.account_mut().program_owner = program_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
post_states_entry.insert_entry(post.into_account());
|
post_states_entry.insert_entry(post.into_account());
|
||||||
@ -408,7 +460,8 @@ fn main() {
|
|||||||
program_id,
|
program_id,
|
||||||
} = env::read();
|
} = env::read();
|
||||||
|
|
||||||
let execution_state = ExecutionState::derive_from_outputs(program_id, program_outputs);
|
let execution_state =
|
||||||
|
ExecutionState::derive_from_outputs(&visibility_mask, program_id, program_outputs);
|
||||||
|
|
||||||
let output = compute_circuit_output(
|
let output = compute_circuit_output(
|
||||||
execution_state,
|
execution_state,
|
||||||
|
|||||||
@ -2,11 +2,11 @@ use std::num::NonZeroU128;
|
|||||||
|
|
||||||
use amm_core::{
|
use amm_core::{
|
||||||
PoolDefinition, compute_liquidity_token_pda, compute_liquidity_token_pda_seed,
|
PoolDefinition, compute_liquidity_token_pda, compute_liquidity_token_pda_seed,
|
||||||
compute_pool_pda, compute_vault_pda,
|
compute_pool_pda, compute_pool_pda_seed, compute_vault_pda, compute_vault_pda_seed,
|
||||||
};
|
};
|
||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata, Data},
|
account::{Account, AccountWithMetadata, Data},
|
||||||
program::{AccountPostState, ChainedCall, ProgramId},
|
program::{AccountPostState, ChainedCall, Claim, ProgramId},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
|
#[expect(clippy::too_many_arguments, reason = "TODO: Fix later")]
|
||||||
@ -108,36 +108,52 @@ pub fn new_definition(
|
|||||||
};
|
};
|
||||||
|
|
||||||
pool_post.data = Data::from(&pool_post_definition);
|
pool_post.data = Data::from(&pool_post_definition);
|
||||||
let pool_post = AccountPostState::new_claimed_if_default(pool_post);
|
let pool_pda_seed = compute_pool_pda_seed(definition_token_a_id, definition_token_b_id);
|
||||||
|
let pool_post = AccountPostState::new_claimed_if_default(pool_post, Claim::Pda(pool_pda_seed));
|
||||||
|
|
||||||
let token_program_id = user_holding_a.account.program_owner;
|
let token_program_id = user_holding_a.account.program_owner;
|
||||||
|
|
||||||
// Chain call for Token A (user_holding_a -> Vault_A)
|
// Chain call for Token A (user_holding_a -> Vault_A)
|
||||||
|
let vault_a_seed = compute_vault_pda_seed(pool.account_id, definition_token_a_id);
|
||||||
|
let vault_a_authorized = AccountWithMetadata {
|
||||||
|
is_authorized: true,
|
||||||
|
..vault_a.clone()
|
||||||
|
};
|
||||||
let call_token_a = ChainedCall::new(
|
let call_token_a = ChainedCall::new(
|
||||||
token_program_id,
|
token_program_id,
|
||||||
vec![user_holding_a.clone(), vault_a.clone()],
|
vec![user_holding_a.clone(), vault_a_authorized],
|
||||||
&token_core::Instruction::Transfer {
|
&token_core::Instruction::Transfer {
|
||||||
amount_to_transfer: token_a_amount.into(),
|
amount_to_transfer: token_a_amount.into(),
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
.with_pda_seeds(vec![vault_a_seed]);
|
||||||
|
|
||||||
// Chain call for Token B (user_holding_b -> Vault_B)
|
// Chain call for Token B (user_holding_b -> Vault_B)
|
||||||
|
let vault_b_seed = compute_vault_pda_seed(pool.account_id, definition_token_b_id);
|
||||||
|
let vault_b_authorized = AccountWithMetadata {
|
||||||
|
is_authorized: true,
|
||||||
|
..vault_b.clone()
|
||||||
|
};
|
||||||
let call_token_b = ChainedCall::new(
|
let call_token_b = ChainedCall::new(
|
||||||
token_program_id,
|
token_program_id,
|
||||||
vec![user_holding_b.clone(), vault_b.clone()],
|
vec![user_holding_b.clone(), vault_b_authorized],
|
||||||
&token_core::Instruction::Transfer {
|
&token_core::Instruction::Transfer {
|
||||||
amount_to_transfer: token_b_amount.into(),
|
amount_to_transfer: token_b_amount.into(),
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
.with_pda_seeds(vec![vault_b_seed]);
|
||||||
let mut pool_lp_auth = pool_definition_lp.clone();
|
|
||||||
pool_lp_auth.is_authorized = true;
|
|
||||||
|
|
||||||
|
let pool_lp_pda_seed = compute_liquidity_token_pda_seed(pool.account_id);
|
||||||
|
let pool_lp_authorized = AccountWithMetadata {
|
||||||
|
is_authorized: true,
|
||||||
|
..pool_definition_lp.clone()
|
||||||
|
};
|
||||||
let call_token_lp = ChainedCall::new(
|
let call_token_lp = ChainedCall::new(
|
||||||
token_program_id,
|
token_program_id,
|
||||||
vec![pool_lp_auth, user_holding_lp.clone()],
|
vec![pool_lp_authorized, user_holding_lp.clone()],
|
||||||
&instruction,
|
&instruction,
|
||||||
)
|
)
|
||||||
.with_pda_seeds(vec![compute_liquidity_token_pda_seed(pool.account_id)]);
|
.with_pda_seeds(vec![pool_lp_pda_seed]);
|
||||||
|
|
||||||
let chained_calls = vec![call_token_lp, call_token_b, call_token_a];
|
let chained_calls = vec![call_token_lp, call_token_b, call_token_a];
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::num::NonZero;
|
use std::{num::NonZero, vec};
|
||||||
|
|
||||||
use amm_core::{
|
use amm_core::{
|
||||||
PoolDefinition, compute_liquidity_token_pda, compute_liquidity_token_pda_seed,
|
PoolDefinition, compute_liquidity_token_pda, compute_liquidity_token_pda_seed,
|
||||||
@ -1756,7 +1756,7 @@ impl AccountsForExeTests {
|
|||||||
definition_id: IdForExeTests::token_lp_definition_id(),
|
definition_id: IdForExeTests::token_lp_definition_id(),
|
||||||
balance: BalanceForExeTests::lp_supply_init(),
|
balance: BalanceForExeTests::lp_supply_init(),
|
||||||
}),
|
}),
|
||||||
nonce: 0_u128.into(),
|
nonce: 1_u128.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1801,7 +1801,7 @@ impl AccountsForExeTests {
|
|||||||
definition_id: IdForExeTests::token_lp_definition_id(),
|
definition_id: IdForExeTests::token_lp_definition_id(),
|
||||||
balance: 0,
|
balance: 0,
|
||||||
}),
|
}),
|
||||||
nonce: 0_u128.into(),
|
nonce: 1.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2799,7 +2799,7 @@ fn simple_amm_new_definition_inactive_initialized_pool_and_uninit_user_lp() {
|
|||||||
IdForExeTests::user_token_b_id(),
|
IdForExeTests::user_token_b_id(),
|
||||||
IdForExeTests::user_token_lp_id(),
|
IdForExeTests::user_token_lp_id(),
|
||||||
],
|
],
|
||||||
vec![0_u128.into(), 0_u128.into()],
|
vec![0_u128.into(), 0_u128.into(), 0_u128.into()],
|
||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -2809,6 +2809,7 @@ fn simple_amm_new_definition_inactive_initialized_pool_and_uninit_user_lp() {
|
|||||||
&[
|
&[
|
||||||
&PrivateKeysForTests::user_token_a_key(),
|
&PrivateKeysForTests::user_token_a_key(),
|
||||||
&PrivateKeysForTests::user_token_b_key(),
|
&PrivateKeysForTests::user_token_b_key(),
|
||||||
|
&PrivateKeysForTests::user_token_lp_key(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2955,7 +2956,7 @@ fn simple_amm_new_definition_uninitialized_pool() {
|
|||||||
IdForExeTests::user_token_b_id(),
|
IdForExeTests::user_token_b_id(),
|
||||||
IdForExeTests::user_token_lp_id(),
|
IdForExeTests::user_token_lp_id(),
|
||||||
],
|
],
|
||||||
vec![0_u128.into(), 0_u128.into()],
|
vec![0_u128.into(), 0_u128.into(), 0_u128.into()],
|
||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -2965,6 +2966,7 @@ fn simple_amm_new_definition_uninitialized_pool() {
|
|||||||
&[
|
&[
|
||||||
&PrivateKeysForTests::user_token_a_key(),
|
&PrivateKeysForTests::user_token_a_key(),
|
||||||
&PrivateKeysForTests::user_token_b_key(),
|
&PrivateKeysForTests::user_token_b_key(),
|
||||||
|
&PrivateKeysForTests::user_token_lp_key(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata},
|
account::{Account, AccountWithMetadata},
|
||||||
program::{AccountPostState, ChainedCall, ProgramId},
|
program::{AccountPostState, ChainedCall, Claim, ProgramId},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn create_associated_token_account(
|
pub fn create_associated_token_account(
|
||||||
@ -11,7 +11,7 @@ pub fn create_associated_token_account(
|
|||||||
) -> (Vec<AccountPostState>, Vec<ChainedCall>) {
|
) -> (Vec<AccountPostState>, Vec<ChainedCall>) {
|
||||||
// No authorization check needed: create is idempotent, so anyone can call it safely.
|
// No authorization check needed: create is idempotent, so anyone can call it safely.
|
||||||
let token_program_id = token_definition.account.program_owner;
|
let token_program_id = token_definition.account.program_owner;
|
||||||
ata_core::verify_ata_and_get_seed(
|
let ata_seed = ata_core::verify_ata_and_get_seed(
|
||||||
&ata_account,
|
&ata_account,
|
||||||
&owner,
|
&owner,
|
||||||
token_definition.account_id,
|
token_definition.account_id,
|
||||||
@ -22,7 +22,7 @@ pub fn create_associated_token_account(
|
|||||||
if ata_account.account != Account::default() {
|
if ata_account.account != Account::default() {
|
||||||
return (
|
return (
|
||||||
vec![
|
vec![
|
||||||
AccountPostState::new_claimed_if_default(owner.account.clone()),
|
AccountPostState::new_claimed_if_default(owner.account.clone(), Claim::Authorized),
|
||||||
AccountPostState::new(token_definition.account.clone()),
|
AccountPostState::new(token_definition.account.clone()),
|
||||||
AccountPostState::new(ata_account.account.clone()),
|
AccountPostState::new(ata_account.account.clone()),
|
||||||
],
|
],
|
||||||
@ -31,14 +31,20 @@ pub fn create_associated_token_account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let post_states = vec![
|
let post_states = vec![
|
||||||
AccountPostState::new_claimed_if_default(owner.account.clone()),
|
AccountPostState::new_claimed_if_default(owner.account.clone(), Claim::Authorized),
|
||||||
AccountPostState::new(token_definition.account.clone()),
|
AccountPostState::new(token_definition.account.clone()),
|
||||||
AccountPostState::new(ata_account.account.clone()),
|
AccountPostState::new(ata_account.account.clone()),
|
||||||
];
|
];
|
||||||
|
let ata_account_auth = AccountWithMetadata {
|
||||||
|
is_authorized: true,
|
||||||
|
..ata_account.clone()
|
||||||
|
};
|
||||||
let chained_call = ChainedCall::new(
|
let chained_call = ChainedCall::new(
|
||||||
token_program_id,
|
token_program_id,
|
||||||
vec![token_definition.clone(), ata_account.clone()],
|
vec![token_definition.clone(), ata_account_auth],
|
||||||
&token_core::Instruction::InitializeAccount,
|
&token_core::Instruction::InitializeAccount,
|
||||||
);
|
)
|
||||||
|
.with_pda_seeds(vec![ata_seed]);
|
||||||
|
|
||||||
(post_states, vec![chained_call])
|
(post_states, vec![chained_call])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,23 +10,23 @@ pub enum Instruction {
|
|||||||
/// Transfer tokens from sender to recipient.
|
/// Transfer tokens from sender to recipient.
|
||||||
///
|
///
|
||||||
/// Required accounts:
|
/// Required accounts:
|
||||||
/// - Sender's Token Holding account (authorized),
|
/// - Sender's Token Holding account (initialized, authorized),
|
||||||
/// - Recipient's Token Holding account.
|
/// - Recipient's Token Holding account (initialized or authorized and uninitialized).
|
||||||
Transfer { amount_to_transfer: u128 },
|
Transfer { amount_to_transfer: u128 },
|
||||||
|
|
||||||
/// Create a new fungible token definition without metadata.
|
/// Create a new fungible token definition without metadata.
|
||||||
///
|
///
|
||||||
/// Required accounts:
|
/// Required accounts:
|
||||||
/// - Token Definition account (uninitialized),
|
/// - Token Definition account (uninitialized, authorized),
|
||||||
/// - Token Holding account (uninitialized).
|
/// - Token Holding account (uninitialized, authorized).
|
||||||
NewFungibleDefinition { name: String, total_supply: u128 },
|
NewFungibleDefinition { name: String, total_supply: u128 },
|
||||||
|
|
||||||
/// Create a new fungible or non-fungible token definition with metadata.
|
/// Create a new fungible or non-fungible token definition with metadata.
|
||||||
///
|
///
|
||||||
/// Required accounts:
|
/// Required accounts:
|
||||||
/// - Token Definition account (uninitialized),
|
/// - Token Definition account (uninitialized, authorized),
|
||||||
/// - Token Holding account (uninitialized),
|
/// - Token Holding account (uninitialized, authorized),
|
||||||
/// - Token Metadata account (uninitialized).
|
/// - Token Metadata account (uninitialized, authorized).
|
||||||
NewDefinitionWithMetadata {
|
NewDefinitionWithMetadata {
|
||||||
new_definition: NewTokenDefinition,
|
new_definition: NewTokenDefinition,
|
||||||
/// Boxed to avoid large enum variant size.
|
/// Boxed to avoid large enum variant size.
|
||||||
@ -36,29 +36,29 @@ pub enum Instruction {
|
|||||||
/// Initialize a token holding account for a given token definition.
|
/// Initialize a token holding account for a given token definition.
|
||||||
///
|
///
|
||||||
/// Required accounts:
|
/// Required accounts:
|
||||||
/// - Token Definition account (initialized),
|
/// - Token Definition account (initialized, any authorization),
|
||||||
/// - Token Holding account (uninitialized),
|
/// - Token Holding account (uninitialized, authorized),
|
||||||
InitializeAccount,
|
InitializeAccount,
|
||||||
|
|
||||||
/// Burn tokens from the holder's account.
|
/// Burn tokens from the holder's account.
|
||||||
///
|
///
|
||||||
/// Required accounts:
|
/// Required accounts:
|
||||||
/// - Token Definition account (initialized),
|
/// - Token Definition account (initialized, any authorization),
|
||||||
/// - Token Holding account (authorized).
|
/// - Token Holding account (initialized, authorized).
|
||||||
Burn { amount_to_burn: u128 },
|
Burn { amount_to_burn: u128 },
|
||||||
|
|
||||||
/// Mint new tokens to the holder's account.
|
/// Mint new tokens to the holder's account.
|
||||||
///
|
///
|
||||||
/// Required accounts:
|
/// Required accounts:
|
||||||
/// - Token Definition account (authorized),
|
/// - Token Definition account (initialized, authorized),
|
||||||
/// - Token Holding account (uninitialized or initialized).
|
/// - Token Holding account (uninitialized or authorized and initialized).
|
||||||
Mint { amount_to_mint: u128 },
|
Mint { amount_to_mint: u128 },
|
||||||
|
|
||||||
/// Print a new NFT from the master copy.
|
/// Print a new NFT from the master copy.
|
||||||
///
|
///
|
||||||
/// Required accounts:
|
/// Required accounts:
|
||||||
/// - NFT Master Token Holding account (authorized),
|
/// - NFT Master Token Holding account (authorized),
|
||||||
/// - NFT Printed Copy Token Holding account (uninitialized).
|
/// - NFT Printed Copy Token Holding account (uninitialized, authorized).
|
||||||
PrintNft,
|
PrintNft,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata, Data},
|
account::{Account, AccountWithMetadata, Data},
|
||||||
program::AccountPostState,
|
program::{AccountPostState, Claim},
|
||||||
};
|
};
|
||||||
use token_core::{TokenDefinition, TokenHolding};
|
use token_core::{TokenDefinition, TokenHolding};
|
||||||
|
|
||||||
@ -30,6 +30,6 @@ pub fn initialize_account(
|
|||||||
|
|
||||||
vec![
|
vec![
|
||||||
AccountPostState::new(definition_post),
|
AccountPostState::new(definition_post),
|
||||||
AccountPostState::new_claimed(account_to_initialize),
|
AccountPostState::new_claimed(account_to_initialize, Claim::Authorized),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata, Data},
|
account::{Account, AccountWithMetadata, Data},
|
||||||
program::AccountPostState,
|
program::{AccountPostState, Claim},
|
||||||
};
|
};
|
||||||
use token_core::{TokenDefinition, TokenHolding};
|
use token_core::{TokenDefinition, TokenHolding};
|
||||||
|
|
||||||
@ -67,6 +67,6 @@ pub fn mint(
|
|||||||
|
|
||||||
vec![
|
vec![
|
||||||
AccountPostState::new(definition_post),
|
AccountPostState::new(definition_post),
|
||||||
AccountPostState::new_claimed_if_default(holding_post),
|
AccountPostState::new_claimed_if_default(holding_post, Claim::Authorized),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata, Data},
|
account::{Account, AccountWithMetadata, Data},
|
||||||
program::AccountPostState,
|
program::{AccountPostState, Claim},
|
||||||
};
|
};
|
||||||
use token_core::{
|
use token_core::{
|
||||||
NewTokenDefinition, NewTokenMetadata, TokenDefinition, TokenHolding, TokenMetadata,
|
NewTokenDefinition, NewTokenMetadata, TokenDefinition, TokenHolding, TokenMetadata,
|
||||||
@ -42,8 +42,8 @@ pub fn new_fungible_definition(
|
|||||||
holding_target_account_post.data = Data::from(&token_holding);
|
holding_target_account_post.data = Data::from(&token_holding);
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
AccountPostState::new_claimed(definition_target_account_post),
|
AccountPostState::new_claimed(definition_target_account_post, Claim::Authorized),
|
||||||
AccountPostState::new_claimed(holding_target_account_post),
|
AccountPostState::new_claimed(holding_target_account_post, Claim::Authorized),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,8 +119,8 @@ pub fn new_definition_with_metadata(
|
|||||||
metadata_target_account_post.data = Data::from(&token_metadata);
|
metadata_target_account_post.data = Data::from(&token_metadata);
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
AccountPostState::new_claimed(definition_target_account_post),
|
AccountPostState::new_claimed(definition_target_account_post, Claim::Authorized),
|
||||||
AccountPostState::new_claimed(holding_target_account_post),
|
AccountPostState::new_claimed(holding_target_account_post, Claim::Authorized),
|
||||||
AccountPostState::new_claimed(metadata_target_account_post),
|
AccountPostState::new_claimed(metadata_target_account_post, Claim::Authorized),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata, Data},
|
account::{Account, AccountWithMetadata, Data},
|
||||||
program::AccountPostState,
|
program::{AccountPostState, Claim},
|
||||||
};
|
};
|
||||||
use token_core::TokenHolding;
|
use token_core::TokenHolding;
|
||||||
|
|
||||||
@ -50,6 +50,6 @@ pub fn print_nft(
|
|||||||
|
|
||||||
vec![
|
vec![
|
||||||
AccountPostState::new(master_account_post),
|
AccountPostState::new(master_account_post),
|
||||||
AccountPostState::new_claimed(printed_account_post),
|
AccountPostState::new_claimed(printed_account_post, Claim::Authorized),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,10 @@
|
|||||||
reason = "We don't care about it in tests"
|
reason = "We don't care about it in tests"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use nssa_core::account::{Account, AccountId, AccountWithMetadata, Data};
|
use nssa_core::{
|
||||||
|
account::{Account, AccountId, AccountWithMetadata, Data},
|
||||||
|
program::Claim,
|
||||||
|
};
|
||||||
use token_core::{
|
use token_core::{
|
||||||
MetadataStandard, NewTokenDefinition, NewTokenMetadata, TokenDefinition, TokenHolding,
|
MetadataStandard, NewTokenDefinition, NewTokenMetadata, TokenDefinition, TokenHolding,
|
||||||
};
|
};
|
||||||
@ -851,7 +854,7 @@ fn mint_uninit_holding_success() {
|
|||||||
*holding_post.account(),
|
*holding_post.account(),
|
||||||
AccountForTests::init_mint().account
|
AccountForTests::init_mint().account
|
||||||
);
|
);
|
||||||
assert!(holding_post.requires_claim());
|
assert_eq!(holding_post.required_claim(), Some(Claim::Authorized));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata, Data},
|
account::{Account, AccountWithMetadata, Data},
|
||||||
program::AccountPostState,
|
program::{AccountPostState, Claim},
|
||||||
};
|
};
|
||||||
use token_core::TokenHolding;
|
use token_core::TokenHolding;
|
||||||
|
|
||||||
@ -106,6 +106,6 @@ pub fn transfer(
|
|||||||
|
|
||||||
vec![
|
vec![
|
||||||
AccountPostState::new(sender_post),
|
AccountPostState::new(sender_post),
|
||||||
AccountPostState::new_claimed_if_default(recipient_post),
|
AccountPostState::new_claimed_if_default(recipient_post, Claim::Authorized),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@ fn main() {
|
|||||||
program_id: auth_transfer_id,
|
program_id: auth_transfer_id,
|
||||||
instruction_data: instruction_data.clone(),
|
instruction_data: instruction_data.clone(),
|
||||||
pre_states: vec![running_sender_pre.clone(), running_recipient_pre.clone()], /* <- Account order permutation here */
|
pre_states: vec![running_sender_pre.clone(), running_recipient_pre.clone()], /* <- Account order permutation here */
|
||||||
pda_seeds: pda_seed.iter().cloned().collect(),
|
pda_seeds: pda_seed.iter().copied().collect(),
|
||||||
};
|
};
|
||||||
chained_calls.push(new_chained_call);
|
chained_calls.push(new_chained_call);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use nssa_core::program::{AccountPostState, ProgramInput, ProgramOutput, read_nssa_inputs};
|
use nssa_core::program::{AccountPostState, Claim, ProgramInput, ProgramOutput, read_nssa_inputs};
|
||||||
|
|
||||||
type Instruction = (Option<Vec<u8>>, bool);
|
type Instruction = (Option<Vec<u8>>, bool);
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ fn main() {
|
|||||||
|
|
||||||
// Claim or not based on the boolean flag
|
// Claim or not based on the boolean flag
|
||||||
let post_state = if should_claim {
|
let post_state = if should_claim {
|
||||||
AccountPostState::new_claimed(account_post)
|
AccountPostState::new_claimed(account_post, Claim::Authorized)
|
||||||
} else {
|
} else {
|
||||||
AccountPostState::new(account_post)
|
AccountPostState::new(account_post)
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use nssa_core::program::{AccountPostState, ProgramInput, ProgramOutput, read_nssa_inputs};
|
use nssa_core::program::{AccountPostState, Claim, ProgramInput, ProgramOutput, read_nssa_inputs};
|
||||||
|
|
||||||
type Instruction = ();
|
type Instruction = ();
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ fn main() {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let account_post = AccountPostState::new_claimed(pre.account.clone());
|
let account_post = AccountPostState::new_claimed(pre.account.clone(), Claim::Authorized);
|
||||||
|
|
||||||
ProgramOutput::new(instruction_words, vec![pre], vec![account_post]).write();
|
ProgramOutput::new(instruction_words, vec![pre], vec![account_post]).write();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use nssa_core::program::{AccountPostState, ProgramInput, ProgramOutput, read_nssa_inputs};
|
use nssa_core::program::{AccountPostState, Claim, ProgramInput, ProgramOutput, read_nssa_inputs};
|
||||||
|
|
||||||
type Instruction = Vec<u8>;
|
type Instruction = Vec<u8>;
|
||||||
|
|
||||||
@ -25,7 +25,10 @@ fn main() {
|
|||||||
ProgramOutput::new(
|
ProgramOutput::new(
|
||||||
instruction_words,
|
instruction_words,
|
||||||
vec![pre],
|
vec![pre],
|
||||||
vec![AccountPostState::new_claimed(account_post)],
|
vec![AccountPostState::new_claimed(
|
||||||
|
account_post,
|
||||||
|
Claim::Authorized,
|
||||||
|
)],
|
||||||
)
|
)
|
||||||
.write();
|
.write();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,18 +58,21 @@ impl Amm<'_> {
|
|||||||
user_holding_lp,
|
user_holding_lp,
|
||||||
];
|
];
|
||||||
|
|
||||||
let nonces = self
|
let mut nonces = self
|
||||||
.0
|
.0
|
||||||
.get_accounts_nonces(vec![user_holding_a, user_holding_b])
|
.get_accounts_nonces(vec![user_holding_a, user_holding_b])
|
||||||
.await
|
.await
|
||||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
|
|
||||||
|
let mut private_keys = Vec::new();
|
||||||
|
|
||||||
let signing_key_a = self
|
let signing_key_a = self
|
||||||
.0
|
.0
|
||||||
.storage
|
.storage
|
||||||
.user_data
|
.user_data
|
||||||
.get_pub_account_signing_key(user_holding_a)
|
.get_pub_account_signing_key(user_holding_a)
|
||||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||||
|
private_keys.push(signing_key_a);
|
||||||
|
|
||||||
let signing_key_b = self
|
let signing_key_b = self
|
||||||
.0
|
.0
|
||||||
@ -77,6 +80,26 @@ impl Amm<'_> {
|
|||||||
.user_data
|
.user_data
|
||||||
.get_pub_account_signing_key(user_holding_b)
|
.get_pub_account_signing_key(user_holding_b)
|
||||||
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||||
|
private_keys.push(signing_key_b);
|
||||||
|
|
||||||
|
if let Some(signing_key_lp) = self
|
||||||
|
.0
|
||||||
|
.storage
|
||||||
|
.user_data
|
||||||
|
.get_pub_account_signing_key(user_holding_lp)
|
||||||
|
{
|
||||||
|
private_keys.push(signing_key_lp);
|
||||||
|
let lp_nonces = self
|
||||||
|
.0
|
||||||
|
.get_accounts_nonces(vec![user_holding_lp])
|
||||||
|
.await
|
||||||
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
|
nonces.extend(lp_nonces);
|
||||||
|
} 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(
|
let message = nssa::public_transaction::Message::try_new(
|
||||||
program.id(),
|
program.id(),
|
||||||
@ -86,10 +109,8 @@ impl Amm<'_> {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(
|
let witness_set =
|
||||||
&message,
|
nssa::public_transaction::WitnessSet::for_message(&message, &private_keys);
|
||||||
&[signing_key_a, signing_key_b],
|
|
||||||
);
|
|
||||||
|
|
||||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
|||||||
@ -23,24 +23,40 @@ impl NativeTokenTransfer<'_> {
|
|||||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
|
|
||||||
if balance >= balance_to_move {
|
if balance >= balance_to_move {
|
||||||
let nonces = self
|
let account_ids = vec![from, to];
|
||||||
|
let program_id = Program::authenticated_transfer_program().id();
|
||||||
|
|
||||||
|
let mut nonces = self
|
||||||
.0
|
.0
|
||||||
.get_accounts_nonces(vec![from])
|
.get_accounts_nonces(vec![from])
|
||||||
.await
|
.await
|
||||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
|
|
||||||
let account_ids = vec![from, to];
|
let mut private_keys = Vec::new();
|
||||||
let program_id = Program::authenticated_transfer_program().id();
|
let from_signing_key = self.0.storage.user_data.get_pub_account_signing_key(from);
|
||||||
let message =
|
let Some(from_signing_key) = from_signing_key else {
|
||||||
Message::try_new(program_id, account_ids, nonces, balance_to_move).unwrap();
|
|
||||||
|
|
||||||
let signing_key = self.0.storage.user_data.get_pub_account_signing_key(from);
|
|
||||||
|
|
||||||
let Some(signing_key) = signing_key else {
|
|
||||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||||
};
|
};
|
||||||
|
private_keys.push(from_signing_key);
|
||||||
|
|
||||||
let witness_set = WitnessSet::for_message(&message, &[signing_key]);
|
let to_signing_key = self.0.storage.user_data.get_pub_account_signing_key(to);
|
||||||
|
if let Some(to_signing_key) = to_signing_key {
|
||||||
|
private_keys.push(to_signing_key);
|
||||||
|
let to_nonces = self
|
||||||
|
.0
|
||||||
|
.get_accounts_nonces(vec![to])
|
||||||
|
.await
|
||||||
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
|
nonces.extend(to_nonces);
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"Receiver's account ({to}) private key not found in wallet. Proceeding with only sender's key."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let message =
|
||||||
|
Message::try_new(program_id, account_ids, nonces, balance_to_move).unwrap();
|
||||||
|
let witness_set = WitnessSet::for_message(&message, &private_keys);
|
||||||
|
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
|||||||
@ -19,15 +19,36 @@ impl Token<'_> {
|
|||||||
let account_ids = vec![definition_account_id, supply_account_id];
|
let account_ids = vec![definition_account_id, supply_account_id];
|
||||||
let program_id = nssa::program::Program::token().id();
|
let program_id = nssa::program::Program::token().id();
|
||||||
let instruction = Instruction::NewFungibleDefinition { name, total_supply };
|
let instruction = Instruction::NewFungibleDefinition { name, total_supply };
|
||||||
|
let nonces = self
|
||||||
|
.0
|
||||||
|
.get_accounts_nonces(account_ids.clone())
|
||||||
|
.await
|
||||||
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
let message = nssa::public_transaction::Message::try_new(
|
let message = nssa::public_transaction::Message::try_new(
|
||||||
program_id,
|
program_id,
|
||||||
account_ids,
|
account_ids,
|
||||||
vec![],
|
nonces,
|
||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
|
let def_private_key = self
|
||||||
|
.0
|
||||||
|
.storage
|
||||||
|
.user_data
|
||||||
|
.get_pub_account_signing_key(definition_account_id)
|
||||||
|
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||||
|
let supply_private_key = self
|
||||||
|
.0
|
||||||
|
.storage
|
||||||
|
.user_data
|
||||||
|
.get_pub_account_signing_key(supply_account_id)
|
||||||
|
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||||
|
|
||||||
|
let witness_set = nssa::public_transaction::WitnessSet::for_message(
|
||||||
|
&message,
|
||||||
|
&[def_private_key, supply_private_key],
|
||||||
|
);
|
||||||
|
|
||||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
@ -138,11 +159,40 @@ impl Token<'_> {
|
|||||||
let instruction = Instruction::Transfer {
|
let instruction = Instruction::Transfer {
|
||||||
amount_to_transfer: amount,
|
amount_to_transfer: amount,
|
||||||
};
|
};
|
||||||
let nonces = self
|
let mut nonces = self
|
||||||
.0
|
.0
|
||||||
.get_accounts_nonces(vec![sender_account_id])
|
.get_accounts_nonces(vec![sender_account_id])
|
||||||
.await
|
.await
|
||||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
|
|
||||||
|
let mut private_keys = Vec::new();
|
||||||
|
let sender_sk = self
|
||||||
|
.0
|
||||||
|
.storage
|
||||||
|
.user_data
|
||||||
|
.get_pub_account_signing_key(sender_account_id)
|
||||||
|
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||||
|
private_keys.push(sender_sk);
|
||||||
|
|
||||||
|
if let Some(recipient_sk) = self
|
||||||
|
.0
|
||||||
|
.storage
|
||||||
|
.user_data
|
||||||
|
.get_pub_account_signing_key(recipient_account_id)
|
||||||
|
{
|
||||||
|
private_keys.push(recipient_sk);
|
||||||
|
let recipient_nonces = self
|
||||||
|
.0
|
||||||
|
.get_accounts_nonces(vec![recipient_account_id])
|
||||||
|
.await
|
||||||
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
|
nonces.extend(recipient_nonces);
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"Receiver's account ({recipient_account_id}) private key not found in wallet. Proceeding with only sender's key."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let message = nssa::public_transaction::Message::try_new(
|
let message = nssa::public_transaction::Message::try_new(
|
||||||
program_id,
|
program_id,
|
||||||
account_ids,
|
account_ids,
|
||||||
@ -150,17 +200,8 @@ impl Token<'_> {
|
|||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let Some(signing_key) = self
|
|
||||||
.0
|
|
||||||
.storage
|
|
||||||
.user_data
|
|
||||||
.get_pub_account_signing_key(sender_account_id)
|
|
||||||
else {
|
|
||||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
|
||||||
};
|
|
||||||
let witness_set =
|
let witness_set =
|
||||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
nssa::public_transaction::WitnessSet::for_message(&message, &private_keys);
|
||||||
|
|
||||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
@ -477,11 +518,40 @@ impl Token<'_> {
|
|||||||
amount_to_mint: amount,
|
amount_to_mint: amount,
|
||||||
};
|
};
|
||||||
|
|
||||||
let nonces = self
|
let mut nonces = self
|
||||||
.0
|
.0
|
||||||
.get_accounts_nonces(vec![definition_account_id])
|
.get_accounts_nonces(vec![definition_account_id])
|
||||||
.await
|
.await
|
||||||
.map_err(ExecutionFailureKind::SequencerError)?;
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
|
|
||||||
|
let mut private_keys = Vec::new();
|
||||||
|
let definition_sk = self
|
||||||
|
.0
|
||||||
|
.storage
|
||||||
|
.user_data
|
||||||
|
.get_pub_account_signing_key(definition_account_id)
|
||||||
|
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
|
||||||
|
private_keys.push(definition_sk);
|
||||||
|
|
||||||
|
if let Some(holder_sk) = self
|
||||||
|
.0
|
||||||
|
.storage
|
||||||
|
.user_data
|
||||||
|
.get_pub_account_signing_key(holder_account_id)
|
||||||
|
{
|
||||||
|
private_keys.push(holder_sk);
|
||||||
|
let recipient_nonces = self
|
||||||
|
.0
|
||||||
|
.get_accounts_nonces(vec![holder_account_id])
|
||||||
|
.await
|
||||||
|
.map_err(ExecutionFailureKind::SequencerError)?;
|
||||||
|
nonces.extend(recipient_nonces);
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"Holder's account ({holder_account_id}) private key not found in wallet. Proceeding with only definition's key."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let message = nssa::public_transaction::Message::try_new(
|
let message = nssa::public_transaction::Message::try_new(
|
||||||
Program::token().id(),
|
Program::token().id(),
|
||||||
account_ids,
|
account_ids,
|
||||||
@ -489,17 +559,8 @@ impl Token<'_> {
|
|||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let Some(signing_key) = self
|
|
||||||
.0
|
|
||||||
.storage
|
|
||||||
.user_data
|
|
||||||
.get_pub_account_signing_key(definition_account_id)
|
|
||||||
else {
|
|
||||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
|
||||||
};
|
|
||||||
let witness_set =
|
let witness_set =
|
||||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
nssa::public_transaction::WitnessSet::for_message(&message, &private_keys);
|
||||||
|
|
||||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user