Hello World
The Arcium tooling suite for writing MXEs (MPC eXecution Environments) is built on top of Anchor, so if you’re familiar with Anchor, you should find Arcium to be a familiar experience, except that you’re using thearcium
CLI instead of anchor
.
To initialize a new MXE project, you can therefore simply run:
- 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 Arcis. This will already be populated with a simple example calledadd_together.rs
. Let’s take a closer look at it.
Our first encrypted instruction
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>
. Let’s break this down:
Enc<Owner, Data>
is Arcium’s encrypted data typeShared
means the data is encrypted with a shared secret between the client and MXE (both can decrypt it)InputValues
is the actual data structure being encrypted (our struct with v1 and v2)- The alternative to
Shared
isMxe
, where only the MXE can decrypt the data
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.
Calling it from Solana
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 theprograms/
directory. Let’s take a closer look at it too:
InitAddTogetherCompDef
, AddTogether
, and AddTogetherCallback
account structs here, but they’re automatically generated when you run arcium init
. Here’s a simplified version of what AddTogether
looks like:
#[arcium_program]
macro (which replaces anchor’s #[program]
macro) and that for every confidential instruction, we generally have three instructions in our solana program:
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 here.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 here.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 here.
Building and testing
Similar to anchor, we can build the confidential instructions and Solana programs usingarcium build
. Testing is done using the @arcium-hq/client
typescript library (more info on it can be found here) 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. Note that some helper functions and imports are excluded for brevity, but you can find the complete examples in your generated project:
initAddTogetherCompDef
: Call theinit_add_together_comp_def
instruction to initialize the confidential instruction definition. (only need to be called once after the program is deployed)getMXEPublicKeyWithRetry
: Fetch the MXE’s x25519 public key.x25519.utils.randomSecretKey
: 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 = 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 here)cipher.encrypt
: Encrypt the inputs for the confidential instruction.awaitEvent
: Wait for thesumEvent
event to be emitted by the program on finalization of the computation (in the callback instruction).addTogether
: Call theadd_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.