use crate::{
idl::arcium::{
accounts::{ArxNode, MXEAccount},
types::{
ArgumentList,
BN254G2BLSPublicKey,
CallbackInstruction,
CircuitSource,
MempoolSize,
NodeMetadata,
Output,
Parameter,
},
ID as ARCIUM_PROG_ID,
},
instruction::{
activate_cluster_ix,
bind_recovery_peer_migration_ix,
claim_computation_rent_ix,
claim_failure_append_ix,
claim_failure_finalize_ix,
claim_failure_init_ix,
claim_node_fees_ix,
close_aborted_key_recovery_ix,
close_successful_key_recovery_ix,
deactivate_arx_ix,
finalize_mxe_keys_ix,
increase_mempool_size_ix,
init_arx_node_acc_ix,
init_cluster_ix,
init_computation_definition_ix,
init_key_recovery_execution_part1_ix,
init_key_recovery_execution_part2_ix,
init_mxe_part1_ix,
init_mxe_part2_ix,
init_network_program_ix,
init_node_operator_acc_ix,
init_recovery_peer_acc_ix,
join_cluster_ix,
migrate_arx_node_ix,
migrate_cluster_ix,
migrate_mxe_account_ix,
migrate_recovery_peer_ix,
propose_fee_ix,
propose_join_cluster_ix,
queue_computation_ix,
queue_key_recovery_finalize_ix,
queue_key_recovery_init_ix,
reclaim_expired_computation_fee_ix,
reclaim_failure_rent_idempotent_ix,
register_account_in_lut_ix,
requeue_mxe_keygen_ix,
set_active_ix,
set_arx_node_metadata_ix,
set_cluster_td_info_ix,
set_migration_ix,
staking::{
add_primary_stake_to_legacy_acc_ix,
init_cluster_stake_state_ix,
init_mxe_recovery_stake_state_ix,
},
submit_aggregated_bls_pubkey_ix,
submit_key_recovery_share_ix,
vote_fee_ix,
},
pda::{arx_acc, mxe_acc},
utils::MAX_RECOVERY_PEERS,
};
use anchor_client::{Client, Cluster as SolanaCluster, Program, RequestBuilder, ThreadSafeSigner};
use anchor_lang::{prelude::*, solana_program::instruction::Instruction as SolanaInstruction};
use rand::RngCore;
use solana_keypair::Keypair;
use solana_rpc_client::nonblocking::rpc_client::RpcClient as AsyncRpcClient;
use solana_rpc_client_api::config::RpcSendTransactionConfig;
use solana_signature::Signature;
use solana_signer::Signer;
use solana_transaction::Transaction;
use std::{ops::Deref, result::Result, sync::Arc, vec};
pub(crate) const MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE: usize = 587;
#[derive(::thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum ClaimFailureError {
#[error(
"Multi-transaction failure claims not yet supported: callback data size {size} exceeds single-transaction limit of {limit} bytes"
)]
CallbackDataTooLarge { size: usize, limit: usize },
}
type ClaimResult<T> = Result<T, ClaimFailureError>;
pub const DEFAULT_PRIM_STAKE_AMOUNT: u64 = 1000;
pub const DEFAULT_FEE_BASIS_POINTS: u16 = 0;
pub const DEFAULT_LOCKUP_EPOCHS: u64 = 10;
pub const DEFAULT_URL: &str = "https://arcium.com";
pub const DEFAULT_LOCATION: u8 = 0;
pub const DEFAULT_CU_CLAIM: u64 = 1000;
pub const DEFAULT_MAX_CLUSTERS: u32 = 1;
pub const DEFAULT_MAX_SIZE: u32 = 1;
pub const DEFAULT_CLUSTER_ENCRYPTION_PUBKEY: [u8; 32] = [0; 32];
pub const DEFAULT_CU_PRICE: u64 = 1;
pub const DEFAULT_COMPILED_CIRCUIT: [u8; 24] = [0; 24];
pub const DEFAULT_AUTHORITY_PUBKEYS: Option<Vec<Pubkey>> = None;
pub const DEFAULT_PARAMS: Vec<Parameter> = vec![];
pub const DEFAULT_CALLBACK_DATA_OBJS: Vec<Pubkey> = vec![];
pub const DEFAULT_OUTPUT_SCALAR_LEN: u8 = 0;
pub const DEFAULT_DUMMY_CALLBACK_DISC: [u8; 8] = [0; 8];
pub const DEFAULT_DUMMY_ENCRYPTION_PUBKEY: [u8; 32] = [0; 32];
pub const DEFAULT_DUMMY_PROGRAM_ID: Pubkey =
pubkey!("Bzabqe5qowkb54kQ96frT6WY7KNLQiMhj4pGGmrZMHRa");
pub async fn init_network_program(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
config: RpcSendTransactionConfig,
) -> Signature {
let current_time = current_unix_timestamp(arcium_program.internal_rpc()).await;
let ix = init_network_program_ix(current_time, &signer.pubkey());
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn submit_key_recovery_share(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
original_mxe_program: &Pubkey,
backup_mxe_program: &Pubkey,
peer_offset: u32,
peer_index: u32,
share: [[u8; 32]; 5],
config: RpcSendTransactionConfig,
) -> Signature {
let ix = submit_key_recovery_share_ix(
&signer.pubkey(),
original_mxe_program,
backup_mxe_program,
peer_offset,
peer_index,
share,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn init_node_operator_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
url: String,
location: u8,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = init_node_operator_acc_ix(&signer.pubkey(), url, location);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn init_arx_node_acc(
arcium_program: &Program<Arc<Keypair>>,
operator_signer: Arc<Keypair>,
node_signer: Arc<Keypair>,
node_offset: u32,
callback_authority: Pubkey,
cu_claim: u64,
metadata: NodeMetadata,
bls_pubkey: BN254G2BLSPublicKey,
bls_pop_sig: [u8; 64],
x25519_pubkey: [u8; 32],
primary_stake_account: Pubkey,
staker: Arc<Keypair>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = init_arx_node_acc_ix(
&operator_signer.pubkey(),
&node_signer.pubkey(),
node_offset,
&callback_authority,
cu_claim,
bls_pubkey,
bls_pop_sig,
metadata,
x25519_pubkey,
&primary_stake_account,
&staker.pubkey(),
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![operator_signer, node_signer, staker], tx, config).await
}
pub async fn set_arx_node_metadata(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
node_offset: u32,
metadata: NodeMetadata,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = set_arx_node_metadata_ix(&signer.pubkey(), node_offset, metadata);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn deactivate_arx(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
node_offset: u32,
cluster_offset: Option<u32>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = deactivate_arx_ix(&signer.pubkey(), node_offset, cluster_offset);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn init_cluster(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
cluster_authority: Pubkey,
cluster_offset: u32,
cluster_size: u16,
cu_price: u64,
mempool_size: MempoolSize,
td_info: Option<NodeMetadata>,
config: RpcSendTransactionConfig,
) -> Signature {
let tx = init_cluster_instructions(
&payer.pubkey(),
cluster_authority,
cluster_offset,
cluster_size,
cu_price,
mempool_size,
td_info,
)
.into_iter()
.fold(arcium_program.request(), |tx, ix| tx.instruction(ix));
send_tx(vec![payer], tx, config).await
}
fn init_cluster_instructions(
payer: &Pubkey,
cluster_authority: Pubkey,
cluster_offset: u32,
cluster_size: u16,
cu_price: u64,
mempool_size: MempoolSize,
td_info: Option<NodeMetadata>,
) -> [SolanaInstruction; 2] {
[
init_cluster_ix(
payer,
cluster_authority,
cluster_offset,
cluster_size,
cu_price,
mempool_size,
td_info,
),
init_cluster_stake_state_ix(payer, cluster_offset),
]
}
pub async fn init_recovery_peer_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
peer_offset: u32,
x25519_pubkey: [u8; 32],
primary_stake_account: &Pubkey,
staker: Arc<Keypair>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = init_recovery_peer_acc_ix(
&signer.pubkey(),
peer_offset,
x25519_pubkey,
primary_stake_account,
&staker.pubkey(),
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer, staker], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn init_mxe(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
mxe_program: &Pubkey,
mxe_authority: Option<Pubkey>,
keygen_comp_offset: u64,
key_recovery_init_comp_offset: u64,
cluster_offset: u32,
recovery_peers: [u32; MAX_RECOVERY_PEERS],
recent_slot: u64,
skip_init_mxe_part1: bool,
config: RpcSendTransactionConfig,
) -> Signature {
if !skip_init_mxe_part1 {
let ix_pt1 = init_mxe_part1_ix(&payer.pubkey(), mxe_program, recovery_peers);
let tx_1 = arcium_program.request().instruction(ix_pt1);
let tx_1 = send_tx(vec![payer.clone()], tx_1, config);
tx_1.await;
}
let ix_pt2 = init_mxe_part2_ix(
&payer.pubkey(),
mxe_program,
cluster_offset,
keygen_comp_offset,
key_recovery_init_comp_offset,
mxe_authority,
recent_slot,
);
let ix_pt3 = init_mxe_recovery_stake_state_ix(&payer.pubkey(), mxe_program);
let tx_2 = arcium_program
.request()
.instruction(ix_pt2)
.instruction(ix_pt3);
let tx_2 = send_tx(vec![payer], tx_2, config);
tx_2.await
}
pub async fn register_account_in_lut(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
mxe_program: &Pubkey,
account: &Pubkey,
config: RpcSendTransactionConfig,
) -> Signature {
let mxe_account: MXEAccount = arcium_program
.account::<MXEAccount>(mxe_acc(mxe_program))
.await
.expect("failed to fetch MXE account when adding account to MXE LUT");
let lut_slot = mxe_account.lut_offset_slot;
let ix = register_account_in_lut_ix(&signer.pubkey(), mxe_program, lut_slot, *account);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn init_ephemeral_mxe(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
mxe_program: &Pubkey,
mxe_authority: Option<Pubkey>,
keygen_comp_offset: u64,
cluster_offset: u32,
recent_slot: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let recovery_peers = [0u32; MAX_RECOVERY_PEERS];
let key_recovery_init_comp_offset = rand::thread_rng().next_u64();
let ix_pt1 = init_mxe_part1_ix(&payer.pubkey(), mxe_program, recovery_peers);
let ix_pt2 = init_mxe_part2_ix(
&payer.pubkey(),
mxe_program,
cluster_offset,
keygen_comp_offset,
key_recovery_init_comp_offset,
mxe_authority,
recent_slot,
);
let ix_pt3 = init_mxe_recovery_stake_state_ix(&payer.pubkey(), mxe_program);
let tx_1 = arcium_program.request().instruction(ix_pt1);
let tx_1 = send_tx(vec![payer.clone()], tx_1, config);
tx_1.await;
let tx_2 = arcium_program
.request()
.instruction(ix_pt2)
.instruction(ix_pt3);
let tx_2 = send_tx(vec![payer], tx_2, config);
tx_2.await
}
pub async fn finalize_mxe_keys(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
mxe_program: &Pubkey,
cluster_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = finalize_mxe_keys_ix(&payer.pubkey(), mxe_program, cluster_offset);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![payer], tx, config).await
}
pub async fn migrate_cluster_account(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
cluster_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let tx = arcium_program
.request()
.instruction(migrate_cluster_ix(&payer.pubkey(), cluster_offset));
send_tx(vec![payer], tx, config).await
}
pub async fn migrate_cluster_full(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
cluster_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let tx = arcium_program
.request()
.instruction(migrate_cluster_ix(&payer.pubkey(), cluster_offset))
.instruction(init_cluster_stake_state_ix(&payer.pubkey(), cluster_offset));
send_tx(vec![payer], tx, config).await
}
pub async fn migrate_mxe_account(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
mxe_program: &Pubkey,
config: RpcSendTransactionConfig,
) -> Signature {
let tx = arcium_program
.request()
.instruction(migrate_mxe_account_ix(&payer.pubkey(), mxe_program));
send_tx(vec![payer], tx, config).await
}
pub async fn migrate_mxe_account_full(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
mxe_program: &Pubkey,
config: RpcSendTransactionConfig,
) -> Signature {
let tx = arcium_program
.request()
.instruction(migrate_mxe_account_ix(&payer.pubkey(), mxe_program))
.instruction(init_mxe_recovery_stake_state_ix(
&payer.pubkey(),
mxe_program,
));
send_tx(vec![payer], tx, config).await
}
pub async fn migrate_recovery_peer(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
peer_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = migrate_recovery_peer_ix(&payer.pubkey(), peer_offset);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![payer], tx, config).await
}
pub async fn migrate_arx_node(
arcium_program: &Program<Arc<Keypair>>,
authority: Arc<Keypair>,
node_offset: u32,
amount: u64,
fee_basis_points: u16,
config: RpcSendTransactionConfig,
) -> Signature {
let tx = arcium_program
.request()
.instruction(add_primary_stake_to_legacy_acc_ix(
&authority.pubkey(),
amount,
fee_basis_points,
))
.instruction(migrate_arx_node_ix(&authority.pubkey(), node_offset));
send_tx(vec![authority], tx, config).await
}
pub async fn migrate_recovery_peer_bind(
arcium_program: &Program<Arc<Keypair>>,
authority: Arc<Keypair>,
peer_offset: u32,
amount: u64,
fee_basis_points: u16,
config: RpcSendTransactionConfig,
) -> Signature {
let tx = arcium_program
.request()
.instruction(migrate_recovery_peer_ix(&authority.pubkey(), peer_offset))
.instruction(add_primary_stake_to_legacy_acc_ix(
&authority.pubkey(),
amount,
fee_basis_points,
))
.instruction(bind_recovery_peer_migration_ix(
&authority.pubkey(),
peer_offset,
));
send_tx(vec![authority], tx, config).await
}
pub async fn set_migration(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
mxe_program: &Pubkey,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = set_migration_ix(&signer.pubkey(), mxe_program);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer.clone()], tx, config).await
}
pub async fn set_active(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
mxe_program: &Pubkey,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = set_active_ix(&signer.pubkey(), mxe_program);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer.clone()], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn init_key_recovery_execution(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
original_mxe_program: &Pubkey,
backup_mxe_program: &Pubkey,
cluster_offset: u32,
key_recovery_finalize_offset: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix_pt1 = init_key_recovery_execution_part1_ix(
&payer.pubkey(),
&payer.pubkey(),
original_mxe_program,
backup_mxe_program,
);
let ix_pt2 = init_key_recovery_execution_part2_ix(
&payer.pubkey(),
original_mxe_program,
backup_mxe_program,
cluster_offset,
key_recovery_finalize_offset,
);
let tx = arcium_program
.request()
.instruction(ix_pt1)
.instruction(ix_pt2);
send_tx(vec![payer], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn queue_key_recovery_finalize(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
original_mxe_program: &Pubkey,
backup_mxe_program: &Pubkey,
backup_cluster_offset: u32,
key_recovery_finalize_offset: u64,
lut_slot: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = queue_key_recovery_finalize_ix(
&signer.pubkey(),
&signer.pubkey(),
original_mxe_program,
backup_mxe_program,
backup_cluster_offset,
key_recovery_finalize_offset,
lut_slot,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn close_successful_key_recovery(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
original_mxe_program: &Pubkey,
backup_mxe_program: &Pubkey,
backup_cluster_offset: u32,
key_recovery_finalize_offset: u64,
config: RpcSendTransactionConfig,
) -> Signature {
try_close_successful_key_recovery(
arcium_program,
signer,
original_mxe_program,
backup_mxe_program,
backup_cluster_offset,
key_recovery_finalize_offset,
config,
)
.await
.unwrap()
}
pub async fn try_close_successful_key_recovery(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
original_mxe_program: &Pubkey,
backup_mxe_program: &Pubkey,
backup_cluster_offset: u32,
key_recovery_finalize_offset: u64,
config: RpcSendTransactionConfig,
) -> Result<Signature, anchor_client::ClientError> {
let ix = close_successful_key_recovery_ix(
&signer.pubkey(),
original_mxe_program,
backup_mxe_program,
backup_cluster_offset,
key_recovery_finalize_offset,
);
let tx = arcium_program.request().instruction(ix);
try_send_tx(vec![signer], tx, config).await
}
pub async fn close_aborted_key_recovery(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
original_mxe_program: &Pubkey,
backup_mxe_program: &Pubkey,
backup_cluster_offset: u32,
key_recovery_finalize_offset: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = close_aborted_key_recovery_ix(
&signer.pubkey(),
original_mxe_program,
backup_mxe_program,
backup_cluster_offset,
key_recovery_finalize_offset,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn requeue_mxe_keygen(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
mxe_program: &Pubkey,
keygen_offset: u64,
cluster_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = requeue_mxe_keygen_ix(&payer.pubkey(), mxe_program, cluster_offset, keygen_offset);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![payer], tx, config).await
}
pub async fn queue_key_recovery_init(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
mxe_program: &Pubkey,
cluster_offset: u32,
key_recovery_init_offset: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = queue_key_recovery_init_ix(
&payer.pubkey(),
mxe_program,
cluster_offset,
key_recovery_init_offset,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![payer], tx, config).await
}
pub async fn claim_computation_rent(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
computation_offset: u64,
cluster_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = claim_computation_rent_ix(&signer.pubkey(), computation_offset, cluster_offset);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn reclaim_expired_computation_fee(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
mxe_program: &Pubkey,
computation_offset: u64,
cluster_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = reclaim_expired_computation_fee_ix(
&signer.pubkey(),
mxe_program,
cluster_offset,
computation_offset,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn init_computation_definition(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
computation_def_offset: u32,
mxe_program: &Pubkey,
circuit_len: u32,
params: Vec<Parameter>,
outputs: Vec<Output>,
circuit_source_override: Option<CircuitSource>,
cu_amount: u64,
lut_slot: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = init_computation_definition_ix(
&payer.pubkey(),
computation_def_offset,
mxe_program,
circuit_len,
params,
outputs,
cu_amount,
circuit_source_override,
lut_slot,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![payer], tx, config).await
}
pub async fn increase_mempool_size(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
cluster_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = increase_mempool_size_ix(&signer.pubkey(), cluster_offset);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn activate_cluster(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
cluster_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = activate_cluster_ix(&signer.pubkey(), cluster_offset);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn set_cluster_td_info(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
cluster_offset: u32,
td_info: Option<NodeMetadata>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = set_cluster_td_info_ix(&signer.pubkey(), cluster_offset, td_info);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn propose_join_cluster(
arcium_program: &Program<Arc<Keypair>>,
cluster_auth_signer: Arc<Keypair>,
cluster_offset: u32,
node_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let node = arcium_program
.account::<ArxNode>(arx_acc(node_offset))
.await
.expect("failed to fetch ArxNode for propose_join_cluster");
let ix = propose_join_cluster_ix(
&cluster_auth_signer.pubkey(),
cluster_offset,
node_offset,
&node.primary_staking_account,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![cluster_auth_signer], tx, config).await
}
pub async fn join_cluster(
arcium_program: &Program<Arc<Keypair>>,
node_auth_signer: Arc<Keypair>,
cluster_offset: u32,
node_offset: u32,
join: bool,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = join_cluster_ix(
&node_auth_signer.pubkey(),
cluster_offset,
node_offset,
join,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![node_auth_signer], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn queue_computation(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
mxe_program: &Pubkey,
computation_offset: u64,
comp_def_offset: u32,
args: ArgumentList,
cu_price_micro: u64,
callback_cu_limit: u32,
callback_instructions: Vec<CallbackInstruction>,
callback_transactions_required: u8,
config: RpcSendTransactionConfig,
) -> Result<Signature, anchor_lang::error::Error> {
let mxe_raw_data = arcium_program
.internal_rpc()
.get_account_data(&mxe_acc(mxe_program))
.await
.expect("Failed to fetch MXE");
let mxe_data = MXEAccount::try_deserialize(&mut mxe_raw_data.as_slice())
.expect("Failed to deserialize MXE");
let ix = queue_computation_ix(
&payer.pubkey(),
mxe_program,
computation_offset,
comp_def_offset,
args,
cu_price_micro,
callback_cu_limit,
mxe_data,
callback_instructions,
callback_transactions_required,
)?;
let tx = arcium_program.request().instruction(ix);
Ok(send_tx(vec![payer], tx, config).await)
}
pub async fn propose_fee(
arcium_program: &Program<Arc<Keypair>>,
node_auth_signer: Arc<Keypair>,
cluster_offset: u32,
node_offset: u32,
proposed_fee: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = propose_fee_ix(
node_auth_signer.pubkey(),
cluster_offset,
node_offset,
proposed_fee,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![node_auth_signer], tx, config).await
}
pub async fn vote_fee(
arcium_program: &Program<Arc<Keypair>>,
node_auth_signer: Arc<Keypair>,
cluster_offset: u32,
node_offset: u32,
proposed_fee: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = vote_fee_ix(
node_auth_signer.pubkey(),
cluster_offset,
node_offset,
proposed_fee,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![node_auth_signer], tx, config).await
}
pub async fn claim_node_fees(
arcium_program: &Program<Arc<Keypair>>,
staking_pool_signer: Arc<Keypair>,
primary_stake_account: Pubkey,
cluster_offset: u32,
node_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = claim_node_fees_ix(
&staking_pool_signer.pubkey(),
&primary_stake_account,
cluster_offset,
node_offset,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![staking_pool_signer], tx, config).await
}
pub async fn submit_aggregated_bls_pubkey(
arcium_program: &Program<Arc<Keypair>>,
node_authority: Arc<Keypair>,
cluster_offset: u32,
node_offset: u32,
aggregated_bls_pubkey: BN254G2BLSPublicKey,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = submit_aggregated_bls_pubkey_ix(
&node_authority.pubkey(),
cluster_offset,
node_offset,
aggregated_bls_pubkey,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![node_authority], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn claim_failure(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
mxe_program: &Pubkey,
cluster_offset: u32,
comp_def_offset: u32,
computation_offset: u64,
node_offset: u32,
callback_data: Vec<u8>,
failure_claim_offset: u32,
config: RpcSendTransactionConfig,
) -> ClaimResult<Vec<Signature>> {
let mut txs = build_claim_failure_txs(
arcium_program,
&signer.pubkey(),
mxe_program,
cluster_offset,
comp_def_offset,
computation_offset,
node_offset,
callback_data,
failure_claim_offset,
)?
.into_iter();
let txs_len = txs.len();
let mut signatures = Vec::with_capacity(txs_len);
if let Some(first) = txs.next() {
signatures.push(send_tx(vec![signer.clone()], first, config).await);
}
for tx in txs.by_ref().take(txs_len.saturating_sub(2)) {
signatures.push(send_tx(vec![signer.clone()], tx, config).await);
}
if let Some(finalize_tx) = txs.next() {
signatures.push(send_tx(vec![signer], finalize_tx, config).await);
}
Ok(signatures)
}
pub async fn reclaim_failure_rent_idempotent(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
recipient: &Pubkey,
mxe_program: &Pubkey,
computation_offset: u64,
config: RpcSendTransactionConfig,
) -> Result<Signature, anchor_client::ClientError> {
let ix = reclaim_failure_rent_idempotent_ix(
&signer.pubkey(),
recipient,
mxe_program,
computation_offset,
);
arcium_program
.request()
.instruction(ix)
.signer(signer)
.send_with_spinner_and_config(config)
.await
}
pub async fn reclaim_failure_rent_idempotent_batch(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
recipient: &Pubkey,
claims: &[(Pubkey, u64)],
config: RpcSendTransactionConfig,
) -> Result<Signature, anchor_client::ClientError> {
let mut request = arcium_program.request();
for (mxe_program, computation_offset) in claims {
request = request.instruction(reclaim_failure_rent_idempotent_ix(
&signer.pubkey(),
recipient,
mxe_program,
*computation_offset,
));
}
request
.signer(signer)
.send_with_spinner_and_config(config)
.await
}
pub async fn submit_reclaim_failure_rent_batch(
rpc: &AsyncRpcClient,
signer: &Keypair,
recipient: &Pubkey,
claims: &[(Pubkey, u64)],
recent_blockhash: solana_program::hash::Hash,
) -> Result<Signature, solana_rpc_client_api::client_error::Error> {
let ixs: Vec<_> = claims
.iter()
.map(|(mxe_program, computation_offset)| {
reclaim_failure_rent_idempotent_ix(
&signer.pubkey(),
recipient,
mxe_program,
*computation_offset,
)
})
.collect();
let tx = Transaction::new_signed_with_payer(
&ixs,
Some(&signer.pubkey()),
&[signer],
recent_blockhash,
);
rpc.send_transaction_with_config(
&tx,
RpcSendTransactionConfig {
skip_preflight: true,
..Default::default()
},
)
.await
}
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn build_claim_failure_txs<'a>(
arcium_program: &'a Program<Arc<Keypair>>,
signer_pubkey: &Pubkey,
mxe_program_id: &Pubkey,
cluster_offset: u32,
comp_def_offset: u32,
computation_offset: u64,
node_offset: u32,
callback_data: Vec<u8>,
failure_claim_offset: u32,
) -> ClaimResult<Vec<RequestBuilder<'a, Arc<Keypair>, Arc<dyn ThreadSafeSigner>>>> {
if callback_data.len() <= MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE {
let init_ix = claim_failure_init_ix(
signer_pubkey,
mxe_program_id,
cluster_offset,
comp_def_offset,
computation_offset,
node_offset,
callback_data.len() as u32,
);
let append_ix = claim_failure_append_ix(
signer_pubkey,
mxe_program_id,
computation_offset,
callback_data,
failure_claim_offset,
);
let finalize_ix = claim_failure_finalize_ix(
signer_pubkey,
mxe_program_id,
cluster_offset,
computation_offset,
node_offset,
);
let tx = arcium_program
.request()
.instruction(init_ix)
.instruction(append_ix)
.instruction(finalize_ix);
return Ok(vec![tx]);
}
Err(ClaimFailureError::CallbackDataTooLarge {
size: callback_data.len(),
limit: MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE,
})
}
pub async fn deactivate_computation_definition(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
comp_offset: u32,
mxe_program: &Pubkey,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = crate::instruction::deactivate_computation_definition_ix(
&payer.pubkey(),
comp_offset,
mxe_program,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![payer], tx, config).await
}
pub async fn close_computation_definition(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
comp_offset: u32,
mxe_program: &Pubkey,
cluster_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = crate::instruction::close_computation_definition_ix(
&payer.pubkey(),
comp_offset,
mxe_program,
cluster_offset,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![payer], tx, config).await
}
pub async fn close_computation_definition_buffers(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
comp_offset: u32,
mxe_program: &Pubkey,
raw_circuit_index: u8,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = crate::instruction::close_computation_definition_buffers_ix(
&payer.pubkey(),
comp_offset,
mxe_program,
raw_circuit_index,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![payer], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn close_mxe(
arcium_program: &Program<Arc<Keypair>>,
payer: Arc<Keypair>,
mxe_program: &Pubkey,
cluster_offset: u32,
keygen_offset: u64,
key_recovery_init_offset: u64,
kr_init_comp_def_exists: bool,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = crate::instruction::close_mxe_ix(
&payer.pubkey(),
mxe_program,
cluster_offset,
keygen_offset,
key_recovery_init_offset,
kr_init_comp_def_exists,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![payer], tx, config).await
}
pub fn arcium_program_client(
cluster: SolanaCluster,
signer: Arc<Keypair>,
rpc: AsyncRpcClient,
) -> Program<Arc<Keypair>> {
let client = Client::new(cluster, signer);
client
.program(ARCIUM_PROG_ID, rpc)
.unwrap_or_else(|err| panic!("Failed to create program: {}", err))
}
async fn send_tx<C: Deref<Target = impl Signer> + Clone>(
signers: Vec<Arc<Keypair>>,
ix: RequestBuilder<'_, C, Arc<dyn ThreadSafeSigner>>,
config: RpcSendTransactionConfig,
) -> Signature {
try_send_tx(signers, ix, config)
.await
.unwrap()
}
async fn try_send_tx<C: Deref<Target = impl Signer> + Clone>(
signers: Vec<Arc<Keypair>>,
ix: RequestBuilder<'_, C, Arc<dyn ThreadSafeSigner>>,
config: RpcSendTransactionConfig,
) -> Result<Signature, anchor_client::ClientError> {
let signed = signers.into_iter().fold(ix, |ix, signer| ix.signer(signer));
signed.send_with_spinner_and_config(config).await
}
async fn current_unix_timestamp(client: &AsyncRpcClient) -> u64 {
let latest_slot = client
.get_slot()
.await
.unwrap_or_else(|err| panic!("Failed to fetch slot: {}", err));
client
.get_block_time(latest_slot)
.await
.unwrap_or_else(|err| panic!("Failed to fetch block time: {}", err))
.try_into()
.unwrap_or_else(|err| panic!("Failed to convert block time to u64: {}", err))
}
pub mod staking {
use super::*;
use crate::{
idl::arcium_staking::types::RewardClaim,
instruction::staking::{
activate_primary_stake_acc_ix,
claim_primary_stake_rewards_ix,
close_delegated_stake_ix,
deactivate_primary_stake_acc_ix,
delegate_stake_ix,
finalize_epoch_rewards_ix,
init_delegated_stake_acc_ix,
init_delegated_stake_master_acc_ix,
init_primary_stake_acc_ix,
merge_delegated_stake_account_ix,
modify_primary_stake_ix,
split_delegated_stake_account_ix,
undelegate_stake_ix,
REWARDS_MT_HEIGHT,
},
};
const FINALIZE_COMPUTE_UNIT_LIMIT: u32 = 1_400_000;
fn set_compute_unit_limit_ix(
limit: u32,
) -> anchor_lang::solana_program::instruction::Instruction {
let mut data = Vec::with_capacity(5);
data.push(2u8);
data.extend_from_slice(&limit.to_le_bytes());
anchor_lang::solana_program::instruction::Instruction {
program_id: Pubkey::from_str_const("ComputeBudget111111111111111111111111111111"),
accounts: Vec::new(),
data,
}
}
pub async fn init_primary_stake_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
amount: u64,
fee_basis_points: u16,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = init_primary_stake_acc_ix(&signer.pubkey(), amount, fee_basis_points);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn activate_primary_stake_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
lockup_epochs: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = activate_primary_stake_acc_ix(&signer.pubkey(), lockup_epochs);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn deactivate_primary_stake_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
arx_node_offset: Option<u32>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = deactivate_primary_stake_acc_ix(&signer.pubkey(), arx_node_offset);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn modify_primary_stake_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
diff: i64,
arx_node: Option<Pubkey>,
mempool: Option<Pubkey>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = modify_primary_stake_ix(&signer.pubkey(), diff, arx_node, mempool);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn finalize_epoch_rewards(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
primary_stake_owner: &Pubkey,
cluster_offset: u32,
node_offset: u32,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = finalize_epoch_rewards_ix(
&signer.pubkey(),
primary_stake_owner,
cluster_offset,
node_offset,
);
let tx = arcium_program
.request()
.instruction(set_compute_unit_limit_ix(FINALIZE_COMPUTE_UNIT_LIMIT))
.instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn claim_primary_stake_rewards(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
destination: &Pubkey,
leaf_index: u16,
opening: [[u8; 32]; REWARDS_MT_HEIGHT],
claim: RewardClaim,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = claim_primary_stake_rewards_ix(
&signer.pubkey(),
destination,
leaf_index,
opening,
claim,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn init_delegated_stake_master_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
owner: &Pubkey,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = init_delegated_stake_master_acc_ix(&signer.pubkey(), owner);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn init_delegated_stake_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
stake_offset: u128,
amount: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = init_delegated_stake_acc_ix(&signer.pubkey(), stake_offset, amount);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn delegate_stake(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
stake_offset: u128,
primary_stake_owner: &Pubkey,
lockup_epochs: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = delegate_stake_ix(
&signer.pubkey(),
stake_offset,
primary_stake_owner,
lockup_epochs,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn undelegate_stake(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
stake_offset: u128,
primary_stake_owner: &Pubkey,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = undelegate_stake_ix(&signer.pubkey(), stake_offset, primary_stake_owner);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
#[allow(clippy::too_many_arguments)]
pub async fn split_delegated_stake_account(
arcium_program: &Program<Arc<Keypair>>,
primary_stake_owner_target: &Pubkey,
delegation_authority: Arc<Keypair>,
withdrawal_authority: Arc<Keypair>,
stake_offset: u128,
stake_offset_new: u128,
new_acc_balance: u64,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = split_delegated_stake_account_ix(
primary_stake_owner_target,
&delegation_authority.pubkey(),
&withdrawal_authority.pubkey(),
stake_offset,
stake_offset_new,
new_acc_balance,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![delegation_authority, withdrawal_authority], tx, config).await
}
pub async fn merge_delegated_stake_account(
arcium_program: &Program<Arc<Keypair>>,
primary_stake_owner_target: &Pubkey,
delegation_authority: Arc<Keypair>,
withdrawal_authority: Arc<Keypair>,
stake_offset_keep: u128,
stake_offset_close: u128,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = merge_delegated_stake_account_ix(
primary_stake_owner_target,
&delegation_authority.pubkey(),
&withdrawal_authority.pubkey(),
stake_offset_keep,
stake_offset_close,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![delegation_authority, withdrawal_authority], tx, config).await
}
pub async fn close_delegated_stake(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
delegation_owner: &Pubkey,
stake_offset: u128,
primary_stake_owner: Option<&Pubkey>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = close_delegated_stake_ix(
&signer.pubkey(),
delegation_owner,
stake_offset,
primary_stake_owner,
);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
}
#[cfg(feature = "permissioned-mainnet")]
pub mod permissioned_mainnet {
use crate::{
instruction::permissioned_mainnet::{
add_permissioned_recovery_peers_ix,
close_permissioned_recovery_peers_acc_ix,
init_permissioned_recovery_peers_acc_ix,
remove_permissioned_recovery_peers_ix,
},
transactions::send_tx,
};
use anchor_client::Program;
use anchor_lang::prelude::Pubkey;
use solana_keypair::Keypair;
use solana_rpc_client_api::config::RpcSendTransactionConfig;
use solana_signature::Signature;
use solana_signer::Signer;
use std::sync::Arc;
pub async fn init_permissioned_recovery_peers_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = init_permissioned_recovery_peers_acc_ix(&signer.pubkey());
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn close_permissioned_recovery_peers_acc(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = close_permissioned_recovery_peers_acc_ix(&signer.pubkey());
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn add_permissioned_recovery_peers(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
peers: Vec<Pubkey>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = add_permissioned_recovery_peers_ix(&signer.pubkey(), peers);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
pub async fn remove_permissioned_recovery_peers(
arcium_program: &Program<Arc<Keypair>>,
signer: Arc<Keypair>,
peers: Vec<Pubkey>,
config: RpcSendTransactionConfig,
) -> Signature {
let ix = remove_permissioned_recovery_peers_ix(&signer.pubkey(), peers);
let tx = arcium_program.request().instruction(ix);
send_tx(vec![signer], tx, config).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
idl::{arcium::ID as ARCIUM_ID, arcium_staking::ID as ARCIUM_STAKING_ID},
pda::staking::cluster_stake_state_acc,
};
use solana_keypair::Keypair;
use solana_program::hash::Hash;
#[test]
fn init_cluster_instructions_include_cluster_stake_state() {
let payer = Pubkey::new_unique();
let cluster_authority = Pubkey::new_unique();
let cluster_offset = 42;
let [cluster_ix, stake_state_ix] = init_cluster_instructions(
&payer,
cluster_authority,
cluster_offset,
3,
DEFAULT_CU_PRICE,
MempoolSize::Tiny,
None,
);
assert_eq!(cluster_ix.program_id, ARCIUM_ID);
assert_eq!(stake_state_ix.program_id, ARCIUM_STAKING_ID);
assert_eq!(stake_state_ix.accounts[0].pubkey, payer);
assert_eq!(
stake_state_ix.accounts[1].pubkey,
cluster_stake_state_acc(cluster_offset)
);
}
#[test]
fn test_build_claim_failure_txs_single_tx_at_max_size() {
let payer = Arc::new(Keypair::new());
let rpc = AsyncRpcClient::new("http://localhost:8899".to_string());
let arcium_program = arcium_program_client(SolanaCluster::Localnet, payer.clone(), rpc);
let signer_pubkey = payer.pubkey();
let mxe_program_id = Pubkey::new_unique();
let callback_data = vec![0u8; MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE];
let txs = build_claim_failure_txs(
&arcium_program,
&signer_pubkey,
&mxe_program_id,
0,
0,
0,
0,
callback_data,
0,
)
.expect("Should succeed for data within single-tx limit");
assert_eq!(
txs.len(),
1,
"Expected exactly 1 transaction for data at MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE"
);
let mut tx = txs[0].transaction();
tx.sign(&[payer], Hash::new_unique());
let serialized = bincode::serialize(&tx).unwrap();
assert_eq!(
serialized.len(),
1232,
"Expected max size of serialized transaction to be 1232 bytes but got {}",
serialized.len()
);
}
#[test]
fn test_build_claim_failure_txs_oversized_returns_error() {
let payer = Arc::new(Keypair::new());
let rpc = AsyncRpcClient::new("http://localhost:8899".to_string());
let arcium_program = arcium_program_client(SolanaCluster::Localnet, payer.clone(), rpc);
let signer_pubkey = payer.pubkey();
let mxe_program_id = Pubkey::new_unique();
let callback_data = vec![0u8; MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE + 1];
let result = build_claim_failure_txs(
&arcium_program,
&signer_pubkey,
&mxe_program_id,
0,
0,
0,
0,
callback_data,
0,
);
assert!(matches!(
result,
Err(ClaimFailureError::CallbackDataTooLarge { size, limit })
if size == MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE + 1
&& limit == MAX_SINGLE_TX_FAILURE_CLAIM_DATA_SIZE
));
}
}