Skip to content

Commit 291ce96

Browse files
authored
elgamal-registry-interface: Introduce light crate for token-2022 (#799)
* elgamal-registry-interface: Introduce light crate for token-2022 #### Problem Token-2022 depends on the ElGamal registry program, which makes it impossible to run LTO on the final program build. #### Summary of changes Similar to the other break-ups that have been done, move out the instruction / state / address derivation functions into an interface crate, and then depend on that in token-2022. * Enable bincode features where required now * Run rustfmt * Add program-id
1 parent 7f060af commit 291ce96

File tree

17 files changed

+300
-240
lines changed

17 files changed

+300
-240
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ on:
99
env:
1010
JS_PACKAGES: "['clients-js', 'clients-js-legacy']"
1111
SBPF_PROGRAM_PACKAGES: "['confidential-elgamal-registry', 'program']"
12-
RUST_PACKAGES: "['clients-cli', 'clients-rust-legacy', 'interface', 'program', 'confidential-ciphertext-arithmetic', 'confidential-elgamal-registry', 'confidential-proof-extraction', 'confidential-proof-generation', 'confidential-proof-tests']"
13-
WASM_PACKAGES: "['interface', 'program']"
12+
RUST_PACKAGES: "['clients-cli', 'clients-rust-legacy', 'interface', 'program', 'confidential-ciphertext-arithmetic', 'confidential-elgamal-registry', 'confidential-elgamal-registry-interface', 'confidential-proof-extraction', 'confidential-proof-generation', 'confidential-proof-tests']"
13+
WASM_PACKAGES: "['confidential-elgamal-registry-interface', 'interface', 'program']"
1414

1515
jobs:
1616
set_env:

.github/workflows/publish-rust.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ on:
1313
- clients/rust-legacy
1414
- confidential-transfer/ciphertext-arithmetic
1515
- confidential-transfer/elgamal-registry
16+
- confidential-transfer/elgamal-registry-interface
1617
- confidential-transfer/proof-extraction
1718
- confidential-transfer/proof-generation
1819
- confidential-transfer/proof-tests

Cargo.lock

Lines changed: 15 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ members = [
1616
"clients/rust-legacy",
1717
"confidential/ciphertext-arithmetic",
1818
"confidential/elgamal-registry",
19+
"confidential/elgamal-registry-interface",
1920
"confidential/proof-extraction",
2021
"confidential/proof-generation",
2122
"confidential/proof-tests",
@@ -38,7 +39,7 @@ check-cfg = [
3839
]
3940

4041
[workspace.metadata.cli]
41-
solana = "2.3.4"
42+
solana = "3.0.0"
4243

4344
# Specify Rust toolchains for rustfmt, clippy, and build.
4445
# Any unprovided toolchains default to stable.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[package]
2+
name = "spl-elgamal-registry-interface"
3+
version = "0.1.0"
4+
description = "SPL ElGamal Registry Interface"
5+
authors = { workspace = true }
6+
repository = { workspace = true }
7+
homepage = { workspace = true }
8+
license = { workspace = true }
9+
edition = { workspace = true }
10+
11+
[dependencies]
12+
bytemuck = { version = "1.23.1", features = ["derive"] }
13+
solana-instruction = "3.0.0"
14+
solana-program-error = "3.0.0"
15+
solana-pubkey = { version = "3.0.0", features = ["curve25519"] }
16+
solana-sdk-ids = "3.0.0"
17+
solana-zk-sdk = "4.0.0"
18+
spl-token-confidential-transfer-proof-extraction = { version = "0.5.0", path = "../proof-extraction" }
19+
20+
[lib]
21+
crate-type = ["lib"]
22+
23+
[package.metadata.docs.rs]
24+
targets = ["x86_64-unknown-linux-gnu"]
25+
26+
[package.metadata.solana]
27+
program-id = "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg"
28+
29+
[lints]
30+
workspace = true
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
use {
2+
crate::{get_elgamal_registry_address, id},
3+
solana_instruction::{AccountMeta, Instruction},
4+
solana_program_error::ProgramError,
5+
solana_pubkey::Pubkey,
6+
solana_zk_sdk::zk_elgamal_proof_program::{
7+
instruction::ProofInstruction, proof_data::PubkeyValidityProofData,
8+
},
9+
spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation,
10+
};
11+
12+
#[derive(Clone, Debug, PartialEq)]
13+
#[repr(u8)]
14+
pub enum RegistryInstruction {
15+
/// Initialize an ElGamal public key registry.
16+
///
17+
/// 0. `[writable]` The account to be created
18+
/// 1. `[signer]` The wallet address (will also be the owner address for the
19+
/// registry account)
20+
/// 2. `[]` System program
21+
/// 3. `[]` Instructions sysvar if `VerifyPubkeyValidity` is included in the
22+
/// same transaction or context state account if `VerifyPubkeyValidity`
23+
/// is pre-verified into a context state account.
24+
CreateRegistry {
25+
/// Relative location of the `ProofInstruction::PubkeyValidityProof`
26+
/// instruction to the `CreateElGamalRegistry` instruction in the
27+
/// transaction. If the offset is `0`, then use a context state account
28+
/// for the proof.
29+
proof_instruction_offset: i8,
30+
},
31+
/// Update an ElGamal public key registry with a new ElGamal public key.
32+
///
33+
/// 0. `[writable]` The ElGamal registry account
34+
/// 1. `[]` Instructions sysvar if `VerifyPubkeyValidity` is included in the
35+
/// same transaction or context state account if `VerifyPubkeyValidity`
36+
/// is pre-verified into a context state account.
37+
/// 2. `[signer]` The owner of the ElGamal public key registry
38+
UpdateRegistry {
39+
/// Relative location of the `ProofInstruction::PubkeyValidityProof`
40+
/// instruction to the `UpdateElGamalRegistry` instruction in the
41+
/// transaction. If the offset is `0`, then use a context state account
42+
/// for the proof.
43+
proof_instruction_offset: i8,
44+
},
45+
}
46+
47+
impl RegistryInstruction {
48+
/// Unpacks a byte buffer into a `RegistryInstruction`
49+
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
50+
let (&tag, rest) = input
51+
.split_first()
52+
.ok_or(ProgramError::InvalidInstructionData)?;
53+
54+
Ok(match tag {
55+
0 => {
56+
let proof_instruction_offset =
57+
*rest.first().ok_or(ProgramError::InvalidInstructionData)?;
58+
Self::CreateRegistry {
59+
proof_instruction_offset: proof_instruction_offset as i8,
60+
}
61+
}
62+
1 => {
63+
let proof_instruction_offset =
64+
*rest.first().ok_or(ProgramError::InvalidInstructionData)?;
65+
Self::UpdateRegistry {
66+
proof_instruction_offset: proof_instruction_offset as i8,
67+
}
68+
}
69+
_ => return Err(ProgramError::InvalidInstructionData),
70+
})
71+
}
72+
73+
/// Packs a `RegistryInstruction` into a byte buffer.
74+
pub fn pack(&self) -> Vec<u8> {
75+
let mut buf = vec![];
76+
match self {
77+
Self::CreateRegistry {
78+
proof_instruction_offset,
79+
} => {
80+
buf.push(0);
81+
buf.extend_from_slice(&proof_instruction_offset.to_le_bytes());
82+
}
83+
Self::UpdateRegistry {
84+
proof_instruction_offset,
85+
} => {
86+
buf.push(1);
87+
buf.extend_from_slice(&proof_instruction_offset.to_le_bytes());
88+
}
89+
};
90+
buf
91+
}
92+
}
93+
94+
/// Create a `RegistryInstruction::CreateRegistry` instruction
95+
pub fn create_registry(
96+
owner_address: &Pubkey,
97+
proof_location: ProofLocation<PubkeyValidityProofData>,
98+
) -> Result<Vec<Instruction>, ProgramError> {
99+
let elgamal_registry_address = get_elgamal_registry_address(owner_address, &id());
100+
101+
let mut accounts = vec![
102+
AccountMeta::new(elgamal_registry_address, false),
103+
AccountMeta::new_readonly(*owner_address, true),
104+
AccountMeta::new_readonly(solana_sdk_ids::system_program::id(), false),
105+
];
106+
let proof_instruction_offset = proof_instruction_offset(&mut accounts, proof_location);
107+
108+
let mut instructions = vec![Instruction {
109+
program_id: id(),
110+
accounts,
111+
data: RegistryInstruction::CreateRegistry {
112+
proof_instruction_offset,
113+
}
114+
.pack(),
115+
}];
116+
append_zk_elgamal_proof(&mut instructions, proof_location)?;
117+
Ok(instructions)
118+
}
119+
120+
/// Create a `RegistryInstruction::UpdateRegistry` instruction
121+
pub fn update_registry(
122+
owner_address: &Pubkey,
123+
proof_location: ProofLocation<PubkeyValidityProofData>,
124+
) -> Result<Vec<Instruction>, ProgramError> {
125+
let elgamal_registry_address = get_elgamal_registry_address(owner_address, &id());
126+
127+
let mut accounts = vec![AccountMeta::new(elgamal_registry_address, false)];
128+
let proof_instruction_offset = proof_instruction_offset(&mut accounts, proof_location);
129+
accounts.push(AccountMeta::new_readonly(*owner_address, true));
130+
131+
let mut instructions = vec![Instruction {
132+
program_id: id(),
133+
accounts,
134+
data: RegistryInstruction::UpdateRegistry {
135+
proof_instruction_offset,
136+
}
137+
.pack(),
138+
}];
139+
append_zk_elgamal_proof(&mut instructions, proof_location)?;
140+
Ok(instructions)
141+
}
142+
143+
/// Takes a `ProofLocation`, updates the list of accounts, and returns a
144+
/// suitable proof location
145+
fn proof_instruction_offset(
146+
accounts: &mut Vec<AccountMeta>,
147+
proof_location: ProofLocation<PubkeyValidityProofData>,
148+
) -> i8 {
149+
match proof_location {
150+
ProofLocation::InstructionOffset(proof_instruction_offset, _) => {
151+
accounts.push(AccountMeta::new_readonly(
152+
solana_sdk_ids::sysvar::instructions::id(),
153+
false,
154+
));
155+
proof_instruction_offset.into()
156+
}
157+
ProofLocation::ContextStateAccount(context_state_account) => {
158+
accounts.push(AccountMeta::new_readonly(*context_state_account, false));
159+
0
160+
}
161+
}
162+
}
163+
164+
/// Takes a `RegistryInstruction` and appends the pubkey validity proof
165+
/// instruction
166+
fn append_zk_elgamal_proof(
167+
instructions: &mut Vec<Instruction>,
168+
proof_data_location: ProofLocation<PubkeyValidityProofData>,
169+
) -> Result<(), ProgramError> {
170+
if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) =
171+
proof_data_location
172+
{
173+
let proof_instruction_offset: i8 = proof_instruction_offset.into();
174+
if proof_instruction_offset != 1 {
175+
return Err(ProgramError::InvalidArgument);
176+
}
177+
instructions
178+
.push(ProofInstruction::VerifyPubkeyValidity.encode_verify_proof(None, proof_data));
179+
}
180+
Ok(())
181+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
pub mod instruction;
2+
pub mod state;
3+
4+
use solana_pubkey::Pubkey;
5+
6+
/// Seed for the ElGamal registry program-derived address
7+
pub const REGISTRY_ADDRESS_SEED: &[u8] = b"elgamal-registry";
8+
9+
/// Derives the ElGamal registry account address and seed for the given wallet
10+
/// address
11+
pub fn get_elgamal_registry_address_and_bump_seed(
12+
wallet_address: &Pubkey,
13+
program_id: &Pubkey,
14+
) -> (Pubkey, u8) {
15+
Pubkey::find_program_address(
16+
&[REGISTRY_ADDRESS_SEED, wallet_address.as_ref()],
17+
program_id,
18+
)
19+
}
20+
21+
/// Derives the ElGamal registry account address for the given wallet address
22+
pub fn get_elgamal_registry_address(wallet_address: &Pubkey, program_id: &Pubkey) -> Pubkey {
23+
get_elgamal_registry_address_and_bump_seed(wallet_address, program_id).0
24+
}
25+
26+
solana_pubkey::declare_id!("regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use {
2+
bytemuck::{Pod, Zeroable},
3+
solana_pubkey::Pubkey,
4+
solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey,
5+
};
6+
7+
pub const ELGAMAL_REGISTRY_ACCOUNT_LEN: usize = 64;
8+
9+
/// ElGamal public key registry. It contains an ElGamal public key that is
10+
/// associated with a wallet account, but independent of any specific mint.
11+
#[repr(C)]
12+
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
13+
pub struct ElGamalRegistry {
14+
/// The owner of the registry
15+
pub owner: Pubkey,
16+
/// The ElGamal public key associated with an account
17+
pub elgamal_pubkey: PodElGamalPubkey,
18+
}

confidential/elgamal-registry/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "spl-elgamal-registry"
33
version = "0.3.0"
4-
description = "Solana ElGamal Registry Program"
4+
description = "SPL ElGamal Registry Program"
55
authors = { workspace = true }
66
repository = { workspace = true }
77
homepage = { workspace = true }
@@ -10,7 +10,6 @@ edition = { workspace = true }
1010

1111
[features]
1212
no-entrypoint = []
13-
test-sbf = []
1413

1514
[dependencies]
1615
bytemuck = { version = "1.24.0", features = ["derive"] }
@@ -27,6 +26,7 @@ solana-security-txt = "1.1.1"
2726
solana-system-interface = { version = "2.0.0", features = ["bincode"] }
2827
solana-sysvar = { version = "3.0.0", features = ["bincode"] }
2928
solana-zk-sdk = "4.0.0"
29+
spl-elgamal-registry-interface = { version = "0.1.0", path = "../elgamal-registry-interface" }
3030
spl-pod = "0.7.1"
3131
spl-token-confidential-transfer-proof-extraction = { version = "0.5.0", path = "../proof-extraction" }
3232

confidential/elgamal-registry/src/entrypoint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ security_txt! {
2525

2626
// Optional Fields
2727
preferred_languages: "en",
28-
source_code: "https://github.com/solana-program/token-2022/tree/master/confidential-transfer/elgamal-registry"
28+
source_code: "https://github.com/solana-program/token-2022/tree/master/confidential/elgamal-registry"
2929
}

0 commit comments

Comments
 (0)