Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 9588472

Browse files
committed
create an ElGamal registry program
1 parent c9fe408 commit 9588472

File tree

7 files changed

+223
-0
lines changed

7 files changed

+223
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ members = [
7575
"token/confidential-transfer/proof-extraction",
7676
"token/confidential-transfer/proof-generation",
7777
"token/confidential-transfer/proof-tests",
78+
"token/confidential-transfer/elgamal-registry",
7879
"token/client",
7980
"utils/cgen",
8081
"utils/test-client",
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "spl-elgamal-registry"
3+
version = "0.1.0"
4+
description = "Solana ElGamal Registry"
5+
authors = ["Solana Labs Maintainers <[email protected]>"]
6+
repository = "https://github.com/solana-labs/solana-program-library"
7+
license = "Apache-2.0"
8+
edition = "2021"
9+
10+
[features]
11+
no-entrypoint = []
12+
test-sbf = []
13+
14+
[dependencies]
15+
bytemuck = { version = "1.18.0", features = ["derive"] }
16+
solana-program = "2.0.3"
17+
solana-zk-sdk = "2.0.3"
18+
spl-pod = { version = "0.4.0", path = "../../../libraries/pod" }
19+
spl-token-confidential-transfer-proof-extraction = { version = "0.1.0", path = "../proof-extraction" }
20+
21+
[lib]
22+
crate-type = ["cdylib", "lib"]
23+
24+
[package.metadata.docs.rs]
25+
targets = ["x86_64-unknown-linux-gnu"]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//! Program entrypoint
2+
3+
#![cfg(all(target_os = "solana", not(feature = "no-entrypoint")))]
4+
5+
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
6+
7+
solana_program::entrypoint!(process_instruction);
8+
fn process_instruction(
9+
program_id: &Pubkey,
10+
accounts: &[AccountInfo],
11+
instruction_data: &[u8],
12+
) -> ProgramResult {
13+
crate::processor::process_instruction(program_id, accounts, instruction_data)
14+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use solana_program::{
2+
program_error::ProgramError,
3+
pubkey::{Pubkey, PUBKEY_BYTES},
4+
};
5+
6+
#[derive(Clone, Debug, PartialEq)]
7+
#[repr(u8)]
8+
pub enum RegistryInstruction {
9+
/// Initialize an ElGamal public key registry for an account.
10+
///
11+
/// 0. `[writable]` The account to initialize
12+
/// 1. `[]` Instructions sysvar if `VerifyPubkeyValidity` is included in the
13+
/// same transaction or context state account if `VerifyPubkeyValidity`
14+
/// is pre-verified into a context state account.
15+
/// 2. `[]` (Optional) Record account if the accompanying proof is to be
16+
/// read from a record account.
17+
CreateRegistry {
18+
/// The owner of the ElGamal registry account
19+
owner: Pubkey,
20+
/// Relative location of the `ProofInstruction::PubkeyValidityProof`
21+
/// instruction to the `CreateElGamalRegistry` instruction in the
22+
/// transaction. If the offset is `0`, then use a context state account
23+
/// for the proof.
24+
proof_instruction_offset: i8,
25+
},
26+
/// Update an ElGamal public key registry with a new ElGamal public key.
27+
///
28+
/// 0. `[writable]` The account to initialize
29+
/// 1. `[signer]` The owner of the ElGamal public key registry
30+
/// 2. `[]` Instructions sysvar if `VerifyPubkeyValidity` is included in the
31+
/// same transaction or context state account if `VerifyPubkeyValidity`
32+
/// is pre-verified into a context state account.
33+
/// 3. `[]` (Optional) Record account if the accompanying proof is to be
34+
/// read from a record account.
35+
UpdateRegistry {
36+
/// Relative location of the `ProofInstruction::PubkeyValidityProof`
37+
/// instruction to the `UpdateElGamalRegistry` instruction in the
38+
/// transaction. If the offset is `0`, then use a context state account
39+
/// for the proof.
40+
proof_instruction_offset: i8,
41+
},
42+
}
43+
44+
impl RegistryInstruction {
45+
/// Unpacks a byte buffer into a `RegistryInstruction`
46+
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
47+
let (&tag, rest) = input
48+
.split_first()
49+
.ok_or(ProgramError::InvalidInstructionData)?;
50+
51+
Ok(match tag {
52+
0 => {
53+
let owner = rest
54+
.get(..PUBKEY_BYTES)
55+
.and_then(|x| Pubkey::try_from(x).ok())
56+
.ok_or(ProgramError::InvalidInstructionData)?;
57+
let proof_instruction_offset =
58+
*rest.first().ok_or(ProgramError::InvalidInstructionData)?;
59+
Self::CreateRegistry {
60+
owner,
61+
proof_instruction_offset: proof_instruction_offset as i8,
62+
}
63+
}
64+
2 => {
65+
let proof_instruction_offset =
66+
*rest.first().ok_or(ProgramError::InvalidInstructionData)?;
67+
Self::UpdateRegistry {
68+
proof_instruction_offset: proof_instruction_offset as i8,
69+
}
70+
}
71+
_ => return Err(ProgramError::InvalidInstructionData),
72+
})
73+
}
74+
75+
/// Packs a `RegistryInstruction` into a byte buffer.
76+
pub fn pack(&self) -> Vec<u8> {
77+
let mut buf = vec![];
78+
match self {
79+
Self::CreateRegistry {
80+
owner,
81+
proof_instruction_offset,
82+
} => {
83+
buf.push(0);
84+
buf.extend_from_slice(owner.as_ref());
85+
buf.extend_from_slice(&proof_instruction_offset.to_le_bytes());
86+
}
87+
Self::UpdateRegistry {
88+
proof_instruction_offset,
89+
} => {
90+
buf.push(1);
91+
buf.extend_from_slice(&proof_instruction_offset.to_le_bytes());
92+
}
93+
};
94+
buf
95+
}
96+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mod entrypoint;
2+
pub mod error;
3+
pub mod instruction;
4+
pub mod processor;
5+
pub mod state;
6+
7+
solana_program::declare_id!("regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg");
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use {
2+
crate::{instruction::RegistryInstruction, state::ElGamalRegistry},
3+
solana_program::{
4+
account_info::{next_account_info, AccountInfo},
5+
entrypoint::ProgramResult,
6+
program_error::ProgramError,
7+
pubkey::Pubkey,
8+
},
9+
solana_zk_sdk::zk_elgamal_proof_program::proof_data::pubkey_validity::{
10+
PubkeyValidityProofContext, PubkeyValidityProofData,
11+
},
12+
spl_pod::bytemuck::pod_from_bytes_mut,
13+
spl_token_confidential_transfer_proof_extraction::verify_and_extract_context,
14+
};
15+
16+
/// Instruction processor
17+
pub fn process_instruction(
18+
_program_id: &Pubkey,
19+
accounts: &[AccountInfo],
20+
input: &[u8],
21+
) -> ProgramResult {
22+
let instruction = RegistryInstruction::unpack(input)?;
23+
let account_info_iter = &mut accounts.iter();
24+
let registry_account_info = next_account_info(account_info_iter)?;
25+
let registry_account_data = &mut registry_account_info.data.borrow_mut();
26+
let registry_account = pod_from_bytes_mut::<ElGamalRegistry>(registry_account_data)?;
27+
28+
let proof_instruction_offset = match instruction {
29+
RegistryInstruction::CreateRegistry {
30+
owner,
31+
proof_instruction_offset,
32+
} => {
33+
// set the owner; ElGamal pubkey is set after the zkp verification below
34+
registry_account.owner = owner;
35+
proof_instruction_offset
36+
}
37+
RegistryInstruction::UpdateRegistry {
38+
proof_instruction_offset,
39+
} => {
40+
// check the owner; ElGamal pubkey is set after the zkp verification below
41+
let owner_info = next_account_info(account_info_iter)?;
42+
validate_owner(owner_info, &registry_account.owner)?;
43+
proof_instruction_offset
44+
}
45+
};
46+
// zero-knowledge proof certifies that the supplied ElGamal public key is valid
47+
let proof_context = verify_and_extract_context::<
48+
PubkeyValidityProofData,
49+
PubkeyValidityProofContext,
50+
>(account_info_iter, proof_instruction_offset as i64, None)?;
51+
registry_account.elgamal_pubkey = proof_context.pubkey;
52+
53+
Ok(())
54+
}
55+
56+
fn validate_owner(owner_info: &AccountInfo, expected_owner: &Pubkey) -> ProgramResult {
57+
if expected_owner != owner_info.key {
58+
return Err(ProgramError::InvalidAccountOwner);
59+
}
60+
if !owner_info.is_signer {
61+
return Err(ProgramError::MissingRequiredSignature);
62+
}
63+
Ok(())
64+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use {
2+
bytemuck::{Pod, Zeroable},
3+
solana_program::pubkey::Pubkey,
4+
solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey,
5+
};
6+
7+
/// ElGamal public key registry. It contains an ElGamal public key that is
8+
/// associated with a wallet account, but independent of any specific mint.
9+
#[repr(C)]
10+
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
11+
pub struct ElGamalRegistry {
12+
/// The owner of the registry
13+
pub owner: Pubkey,
14+
/// The ElGamal public key associated with an account
15+
pub elgamal_pubkey: PodElGamalPubkey,
16+
}

0 commit comments

Comments
 (0)