Hello World with Arcium
Last updated
Last updated
The Arcium tooling suite for writing MXEs (MPC eXecution Environments) is built on top of , so if you're familiar with Anchor, you should find Arcium to be a familiar experience, except that you're using the arcium
CLI instead of anchor
.
To initialize a new MXE project, you can therefore simply run:
This will create a new project with the given name, and initialize it with a basic structure. The structure is the same as in an Anchor project with two differences, so we won't repeat it here (for an explanation of the Anchor project structure, see the ). The two differences are:
The Arcium.toml
file, which contains the configuration for the Arcium tooling suite.
The encrypted-ixs
directory. This is where we write all our code that is meant to operate on encrypted data and therefore runs in MPC. This code is written using our own Rust framework called . This will already be populated with a simple example called add_together.rs
. Let's take a closer look at it.
Let's go through it line by line. use arcis_imports::*;
imports all the necessary types and functions for writing encrypted instructions with Arcis. The #[encrypted]
attribute marks a module that contains encrypted instructions. Inside this module, we define a struct InputValues
that contains the two values we want to encrypt and pass to the encrypted instruction.The #[instruction]
macro marks the function as an entry point for MPC execution - while you can write helper functions without this attribute, only functions marked with #[instruction]
will be compiled into individual circuits that can be called onchain. The function add_together
takes an encrypted input parameter of type Enc<Shared, InputValues>
, which represents the encrypted form of our InputValues struct. The Shared
type parameter indicates this is encrypted using a shared secret between the client and the MXE.
Inside the function:
input_ctxt.to_arcis()
converts the input into a form we can operate on within the MPC environment.
We perform the addition operation, casting the u8 values to u16 to prevent overflow.
input_ctxt.owner.from_arcis(sum)
converts the encrypted sum into an encrypted format that can be stored onchain, while maintaining encryption with the shared secret between the client and the MXE.
Now that we've written our first confidential instruction, let's see how can use it from within a Solana program. Our default project already contains a Solana program in the programs/
directory. Let's take a closer look at it too:
The key things to note here are that every mxe program is identified by the #[arcium_program]
macro (which replaces anchor's #[program]
macro) and that for every confidential instruction, we generally have three instructions in our solana program:
Again, here we exclude some helper functions below and the imports at the top for brevity. This test visualizes the general flow of computations throughout Arcium on the client side quite well too:
initAddTogetherCompDef
: Call the init_add_together_comp_def
instruction to initialize the confidential instruction definition. (only need to be called once after the program is deployed)
x25519.utils.randomPrivateKey
: Generate a random private key for the x25519 key exchange.
x25519.getPublicKey
: Generate the public key corresponding to the private key we generated above.
x25519.getSharedSecret
: Generate the shared secret with the MXE cluster using a x25519 key exchange.
cipher.encrypt
: Encrypt the inputs for the confidential instruction.
awaitEvent
: Wait for the sumEvent
event to be emitted by the program on finalization of the computation (in the callback instruction).
addTogether
: Call the add_together
instruction to invoke the confidential instruction.
awaitComputationFinalization
: Since waiting for an Arcium computation isn't the same as waiting for one Solana transaction (since we need to wait for the MPC cluster to finish the computation and invoke the callback), we wait using this function, which is provided by the Arcium typescript library.
If you would like to deploy your MXE to Solana devnet, you can do so by running the following command
where cluster-offset
is one of 2326510165
, 2260723535
, 768109697
and keypair-path
is just your local keypair which has devnet SOL to deploy the program and initialize MXE account, the -ud
flag represents that the deployment should be done on devnet. Once your program is deployed, it is usually a good idea to run the tests by changing the clusterAccount
to be fetched using getClusterAcc(cluster_offset)
so that the computation definitions are also initialized once.
For the sake of brevity, we don't include the InitAddTogetherCompDef
, AddTogether
, and AddTogetherCallback
account structs here (the generated project will generate these for you). You can read more about them and the invokation of confidential instructions inside solana programs .
init_add_together_comp_def
: This is the instruction that initializes the confidential instruction definition. It is used to set up the computation definition and is therefore only called once prior to the first invocation of the confidential instruction. More info on this can be found .
add_together
: This is the instruction that invokes the confidential instruction. It takes in the arguments for the confidential instruction and queues it for execution using the Arcium program. More info on this can be found .
add_together_callback
: This is the instruction that is called by the MPC cluster when the confidential instruction has finished executing which returns our result. More info on this can be found .
This is due to the general flow of computations throughout Arcium, which you can read more about .
Similar to anchor, we can build the confidential instructions and Solana programs using arcium build
. Testing is done using the @arcium-hq/client
typescript library (more info on it can be found ) by default and can be run using arcium test
(make sure you have installed the npm dependencies prior by running yarn
or npm install
in your project directory). Let's take a quick look at the default test file as well:
cipher = new RescueCipher(sharedSecret)
: Initialize the Rescue cipher (the constructor internally performs a HKDF with HMAC based on the Rescue-Prime hash function, you can learn more )
This concludes our introduction of the Arcium tooling suite. Browse our sample projects in the to see Arcium in action. Check out the rest of this documentation to learn more about the different features and capabilities of Arcium. We hope you'll find it useful and look forward to seeing what you build with it!
If you have any questions or feedback, please don't hesitate to reach out to us on !