The Quantum Vault
A vault is a fundamental building block in DeFi that provides a secure way for users to store their assets.
In this challenge, we'll build a vault that uses Winternitz signatures for transaction verification. This is particularly interesting because Winternitz Signature is the first post-quantum signature integrated on Solana.
In this challenge, we'll update the simple lamport vault that we built in the Pinocchio Vault Challenge to allow Winternitz Signatures as a verification method for transactions.
Installation
Before you begin, make sure Rust and Pinocchio are installed. Then, run the following in your terminal:
# create workspace
cargo new blueshift-pinocchio-quantum-vault --lib --edition 2021
cd blueshift-pinocchio-quantum-vault
Add pinocchio
, pinocchio-system
, and solana-nostd-sha256
and solana-winternitz
:
cargo add pinocchio pinocchio-system solana-nostd-sha256 solana-winternitz
Declare the crate types in Cargo.toml
to generate deployment artifacts in target/deploy
:
[lib]
crate-type = ["lib", "cdylib"]
You're now ready to write your quantum vault program.
Template
This time, we'll split the program into small, focused modules instead of placing everything in lib.rs
. The folder tree will look roughly like this:
src
├── instructions
│ ├── close.rs
│ ├── open.rs
│ ├── mod.rs
│ └── split.rs
└── lib.rs
Note: Remember to change the program ID to 22222222222222222222222222222222222222222222
since we use this under the hood to test your program.
The entrypoint in lib.rs
is very similar to what we covered in the Introduction to Pinocchio Course.
pub mod instructions;
use instructions::*;
use pinocchio::{
account_info::AccountInfo, program_entrypoint, program_error::ProgramError,
pubkey::Pubkey, ProgramResult,
};
program_entrypoint!(process_instruction);
// 22222222222222222222222222222222222222222222
pub const ID: Pubkey = [
0x0f, 0x1e, 0x6b, 0x14, 0x21, 0xc0, 0x4a, 0x07,
0x04, 0x31, 0x26, 0x5c, 0x19, 0xc5, 0xbb, 0xee,
0x19, 0x92, 0xba, 0xe8, 0xaf, 0xd1, 0xcd, 0x07,
0x8e, 0xf8, 0xaf, 0x70, 0x47, 0xdc, 0x11, 0xf7,
];
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
match instruction_data.split_first() {
Some((OpenVault::DISCRIMINATOR, data)) => OpenVault::try_from((data, accounts))?.process(),
Some((SplitVault::DISCRIMINATOR, data)) => SplitVault::try_from((data, accounts))?.process(),
Some((CloseVault::DISCRIMINATOR, data)) => CloseVault::try_from((data, accounts))?.process(),
_ => Err(ProgramError::InvalidInstructionData)
}
}
We don't need any state setup for this, so we're just going to move into creating our instructions.