Migrate from v0.1.x to v0.2.x
1. Update Arcium Rust dependencies
# Update program dependencies
cd programs/*
cargo update --package arcium-client --precise 0.2.0
cargo update --package arcium-macros --precise 0.2.0
cargo update --package arcium-anchor --precise 0.2.0
# Update encrypted-ixs dependencies
cd ../../encrypted-ixs
cargo update --package arcis-imports --precise 0.2.0
2. Update Arcium TS dependencies
npm install @arcium-hq/[email protected]
3. (Optional) Update your Arcium.toml
Arcium.toml
We no longer need the clusters
field under localnet
in Arcium.toml
. New file Arcium.toml
should look like this:
[localnet]
# number of nodes in the single cluster of the localnet
nodes = 2
# number of seconds to wait for the localnet to come online
localnet_timeout_secs = 60
This change is optional, and leaving clusters
field as is will still work.
4. Simplify your Arcium crate imports
No more 10 line import statements from arcium_anchor
, arcium_client
, arcium_macros
, etc. All of them can be compressed such that the following:
use arcium_anchor::{
comp_def_offset, derive_cluster_pda, derive_comp_def_pda, derive_comp_pda, derive_execpool_pda,
derive_mempool_pda, derive_mxe_pda, init_comp_def, queue_computation, ComputationOutputs,
ARCIUM_CLOCK_ACCOUNT_ADDRESS, ARCIUM_STAKING_POOL_ACCOUNT_ADDRESS, CLUSTER_PDA_SEED,
COMP_DEF_PDA_SEED, COMP_PDA_SEED, EXECPOOL_PDA_SEED, MEMPOOL_PDA_SEED, MXE_PDA_SEED,
};
use arcium_client::idl::arcium::{
accounts::{
ClockAccount, Cluster, ComputationDefinitionAccount, PersistentMXEAccount,
StakingPoolAccount,
},
program::Arcium,
types::Argument,
ID_CONST as ARCIUM_PROG_ID,
};
use arcium_macros::{
arcium_callback, arcium_program, callback_accounts, init_computation_definition_accounts,
queue_computation_accounts,
};
now becomes just
use arcium_anchor::prelude::*;
This includes all the basic imports required to setup and run the basic Arcium example program. For additional types such as CircuitSource
, CallbackAccount
, they will still need to be imported separately from arcium_client::idl::arcium::types
module.
5. Update your Arcium CPI calls
In your init_comp_def
calls, you need to add an additional parameter at the third position. So, it used to look like:
init_comp_def(ctx.accounts, true, None, None)?;
Now it should look like:
init_comp_def(ctx.accounts, true, 0, None, None)?;
You don't need to care about the 0
parameter, it's just a placeholder for the new parameter.
6. Update your callback functions
No more manually handling all output bytes from an Arcium computation. The new #[arcium_callback]
macro will handle all the deserialization of bytes for you based on your circuit's generated interface file.
#[arcium_callback(encrypted_ix = "add_together")]
pub fn add_together_callback(
ctx: Context<AddTogetherCallback>,
output: ComputationOutputs,
) -> Result<()> {
let bytes = if let ComputationOutputs::Bytes(bytes) = output {
bytes
} else {
return Err(ErrorCode::AbortedComputation.into());
};
emit!(SumEvent {
sum: bytes[48..80].try_into().unwrap(),
nonce: bytes[32..48].try_into().unwrap(),
});
Ok(())
}
becomes
#[arcium_callback(encrypted_ix = "add_together")]
pub fn add_together_callback(
ctx: Context<AddTogetherCallback>,
output: ComputationOutputs<AddTogetherOutput>,
) -> Result<()> {
let o = match output {
ComputationOutputs::Success(AddTogetherOutput { field_0 }) => field_0,
_ => return Err(ErrorCode::AbortedComputation.into()),
};
emit!(SumEvent {
sum: o.ciphertexts[0],
nonce: o.nonce.to_le_bytes(),
});
Ok(())
}
For a comprehensive guide on how the callback type generation system works, see Callback Type Generation.
7. Update your Context structs
First, all references to PersistentMXEAccount
becomes just MXEAccount
. This has to be done in both queue_computation_accounts
and init_computation_definition_accounts
context structs.
Second, we need to update the StakingPoolAccount
to FeePool
in your queue_computation_accounts
Context structs.
#[account(
mut,
address = ARCIUM_STAKING_POOL_ACCOUNT_ADDRESS,
)]
pub pool_account: Account<'info, StakingPoolAccount>,
now becomes
#[account(
mut,
address = ARCIUM_FEE_POOL_ACCOUNT_ADDRESS,
)]
pub pool_account: Account<'info, FeePool>,
8. Add a new error code
In your program ErrorCode
enum, add a new error code:
#[error_code]
pub enum ErrorCode {
...
#[msg("The cluster is not set")]
ClusterNotSet,
}
9. Replace static mxePublicKey
mxePublicKey
No more constant MXE public key definition in your tests or client. Instead you can import getMXEPublicKey
function from @arcium-hq/client
and use it to get the MXE public key.
const mxePublicKey = await getMXEPublicKey(
provider as anchor.AnchorProvider,
program.programId
);
This might cause some issues in your tests as the function might be called before the MXE keys are fully set. In which case, we recommend using the following helper function as a wrapper around getMXEPublicKey
to fetch MXE public key with retries:
async function getMXEPublicKeyWithRetry(
provider: anchor.AnchorProvider,
programId: PublicKey,
maxRetries: number = 10,
retryDelayMs: number = 500
): Promise<Uint8Array> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const mxePublicKey = await getMXEPublicKey(provider, programId);
if (mxePublicKey) {
return mxePublicKey;
}
} catch (error) {
console.log(`Attempt ${attempt} failed to fetch MXE public key:`, error);
}
if (attempt < maxRetries) {
console.log(
`Retrying in ${retryDelayMs}ms... (attempt ${attempt}/${maxRetries})`
);
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
}
}
throw new Error(
`Failed to fetch MXE public key after ${maxRetries} attempts`
);
}
And voila, you should have a working program that is compatible with Arcium tooling v0.2.0!
Last updated