Based on the https://github.com/arcium-hq/examples 'voting' app with a significant number of fixes:
- Replaced legacy web3.js with Solana Kit
- Fixed race conditions in tests
- Improved CI/CD with GitHub Actions
- Better error handling and validation
- Consistent naming conventions throughout (poll authority, choice, vote_counts). Avoid re-using the word 'vote' in multiple contexts.
- Program ID is now read from IDL instead of hardcoded
- Comprehensive test coverage with multiple users
- Build and dependency management improvements
- Better documentation and code comments
Prerequisites:
- Install Arcium
- Install Docker: https://docs.docker.com/desktop/setup/install/mac-install/
- Start Docker desktop app (if you get
docker daemon not runningyou haven't started Docker desktop app)
Run npm test. That script will:
- Set the correct Anchor version (0.32.1) if avm is available
- Unset RUSTUP_TOOLCHAIN to use Rust 1.92.0 (from rust-toolchain.toml)
- Kill any running Solana validator and remove test ledger for clean state
- Build the program and generate Codama client
- Run
arcium testto test with Arcium 0.6.6
This is all explained beautifully in the video (see below), but also it's nice to have things written down:
create_poll(programs/election/src/handlers/create_poll.rs) - creates the poll PDA, initializing the poll (programs/election/src/state/poll.rs) with its regular values, but leavingvote_countsempty. Create poll then usesqueue_computationto invoke...create_poll(encrypted-ixs/src/lib.rs) to create the initial encrypted value ofvote_counts(i.e,[0, 0, 0], encrypted), which will be received by...create_poll_callback- receives the encrypted[0, 0, 0]and saves them to thevote_counts
- Clients get a
sharedSecret(tests/helpers.ts) they can use to encrypt values they sent to instruction handlers - Client invoke the instruction handlers like normal (using Anchor JS or a Codama client - see
tests/election.ts) specifying the encrypted value as theirchoiceand specifying the address of thepoll vote(programs/election/src/handlers/vote.rs) gets thechoiceand the current value ofvote_countsfrom thepolland then usesqueue_computationto invoke...vote(encrypted-ixs/src/lib.rs) which decrypts thechoiceand the current value ofvote_counts, increments the choice invote_counts, and encrypts the newvote_counts, which will be received by...vote_callback(programs/election/src/handlers/vote.rs) which saves the newvote_countsto thepoll
- Only the poll authority can call
reveal_result(programs/election/src/handlers/reveal_result.rs) to decrypt and reveal the vote tallies. The handler usesqueue_computationto invoke... reveal_result(encrypted-ixs/src/lib.rs) which decrypts thevote_counts, compares all three vote counts, and returns the winning option (0, 1, or 2), which will be received by...reveal_result_callback(programs/election/src/handlers/reveal_result.rs) which emits aRevealResultEventwith the winning option. We could instead save the winning option to a PDA, log it, or do whatever else we want.
Since we have 3 encrypted instruction handlers in encrypted-ixs/src/lib.rs, we have 3 matching Solana instruction handlers to deploy the compiled code to Solana PDAs - these are called init_create_poll_comp_def, init_vote_comp_def, and init_reveal_result_comp_def. These are called once when deploying our program, see the before hook in tests/election.ts.
