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

Commit 3260644

Browse files
authored
Incremental aggregation in Themis (#563)
* Move interactions PublicKey to User constructor * Permit incremental interaction submissions * Split InitializePoliciesAccount into two instructions * Clean up BPF instruction count output * Update Themis program * Get submit_interactions() working on testnet * Fix build
1 parent 2b07fc1 commit 3260644

File tree

10 files changed

+233
-151
lines changed

10 files changed

+233
-151
lines changed

themis/client/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

themis/client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ tokio = "0.2"
3030
url = "2.1"
3131

3232
[dev-dependencies]
33+
separator = "0.4.1"
3334
solana-banks-server = "1.3.14"
3435
solana-bpf-loader-program = "1.3.14"
3536
solana_rbpf = "=0.1.31"

themis/client/src/lib.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ async fn run_user_workflow(
3535
let user_keypair = Keypair::new();
3636
let user_pubkey = user_keypair.pubkey();
3737
let ixs =
38-
instruction::create_user_account(&sender_pubkey, &user_pubkey, sol_to_lamports(0.001));
38+
instruction::create_user_account(&sender_pubkey, &user_pubkey, sol_to_lamports(0.001), pk);
3939
let msg = Message::new(&ixs, Some(&sender_keypair.pubkey()));
4040
let recent_blockhash = client.get_recent_blockhash().await?;
4141
let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash);
@@ -51,21 +51,25 @@ async fn run_user_workflow(
5151
.unwrap();
5252
num_transactions += 1;
5353

54-
let ix = instruction::calculate_aggregate(&user_pubkey, &policies_pubkey, interactions, pk);
55-
let msg = Message::new(&[ix], Some(&sender_keypair.pubkey()));
56-
let recent_blockhash = client.get_recent_blockhash().await?;
57-
let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash);
58-
let tx_size = bincode::serialize(&tx).unwrap().len();
59-
assert!(
60-
tx_size <= 1200,
61-
"transaction over 1200 bytes: {} bytes",
62-
tx_size
63-
);
64-
client
65-
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
66-
.await
67-
.unwrap();
68-
num_transactions += 1;
54+
// Send one interaction at a time to stay under the BPF instruction limit
55+
for (i, interaction) in interactions.into_iter().enumerate() {
56+
let interactions = vec![(i as u8, interaction)];
57+
let ix = instruction::submit_interactions(&user_pubkey, &policies_pubkey, interactions);
58+
let msg = Message::new(&[ix], Some(&sender_keypair.pubkey()));
59+
let recent_blockhash = client.get_recent_blockhash().await?;
60+
let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash);
61+
let tx_size = bincode::serialize(&tx).unwrap().len();
62+
assert!(
63+
tx_size <= 1200,
64+
"transaction over 1200 bytes: {} bytes",
65+
tx_size
66+
);
67+
client
68+
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
69+
.await
70+
.unwrap();
71+
num_transactions += 1;
72+
}
6973

7074
//let user_account = client
7175
// .get_account_with_commitment_and_context(
@@ -135,12 +139,22 @@ pub async fn test_e2e(
135139
let policies_len = policies.len();
136140

137141
// Create the policies account
138-
let ixs = instruction::create_policies_account(
142+
let mut ixs = instruction::create_policies_account(
139143
&sender_pubkey,
140144
&policies_pubkey,
141145
sol_to_lamports(0.01),
142-
policies,
146+
policies.len() as u8,
143147
);
148+
let policies_slice: Vec<_> = policies
149+
.iter()
150+
.enumerate()
151+
.map(|(i, x)| (i as u8, *x))
152+
.collect();
153+
ixs.push(instruction::store_policies(
154+
&policies_pubkey,
155+
policies_slice,
156+
));
157+
144158
let msg = Message::new(&ixs, Some(&sender_keypair.pubkey()));
145159
let recent_blockhash = client.get_recent_blockhash().await?;
146160
let tx = Transaction::new(&[&sender_keypair, &policies_keypair], msg, recent_blockhash);

themis/client/tests/assert_instruction_count.rs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use bn::{Fr, Group, G1};
22
use borsh::BorshSerialize;
33
use elgamal_bn::ciphertext::Ciphertext;
4+
use separator::Separatable;
45
use solana_bpf_loader_program::{
56
create_vm,
67
serialization::{deserialize_parameters, serialize_parameters},
@@ -184,25 +185,27 @@ fn assert_instruction_count() {
184185
let num_scalars = scalars.len();
185186

186187
let (sk, pk) = generate_keys();
187-
let encrypted_interactions: Vec<_> = scalars
188-
.iter()
189-
.map(|_| pk.encrypt(&G1::one()).points)
188+
let encrypted_interactions: Vec<_> = (0..num_scalars)
189+
.map(|i| (i as u8, pk.encrypt(&G1::one()).points))
190190
.collect();
191191

192192
let policies_account = SolanaAccount::new_ref(
193193
0,
194194
Policies {
195195
is_initialized: true,
196+
num_scalars: num_scalars as u8,
196197
scalars: scalars.clone(),
197198
}
198199
.try_to_vec()
199200
.unwrap()
200201
.len(),
201202
&program_id,
202203
);
203-
let instruction_data = ThemisInstruction::InitializePoliciesAccount { scalars }
204-
.serialize()
205-
.unwrap();
204+
let instruction_data = ThemisInstruction::InitializePoliciesAccount {
205+
num_scalars: num_scalars as u8,
206+
}
207+
.serialize()
208+
.unwrap();
206209
let parameter_accounts = vec![KeyedAccount::new(&policies_key, false, &policies_account)];
207210
let initialize_policies_count =
208211
run_program(&program_id, &parameter_accounts[..], &instruction_data).unwrap();
@@ -211,17 +214,16 @@ fn assert_instruction_count() {
211214
let user_key = Pubkey::new_rand();
212215
let user_account =
213216
SolanaAccount::new_ref(0, User::default().try_to_vec().unwrap().len(), &program_id);
214-
let instruction_data = ThemisInstruction::InitializeUserAccount
217+
let instruction_data = ThemisInstruction::InitializeUserAccount { public_key: pk }
215218
.serialize()
216219
.unwrap();
217220
let parameter_accounts = vec![KeyedAccount::new(&user_key, false, &user_account)];
218221
let initialize_user_count =
219222
run_program(&program_id, &parameter_accounts[..], &instruction_data).unwrap();
220223

221224
// Calculate Aggregate
222-
let instruction_data = ThemisInstruction::CalculateAggregate {
225+
let instruction_data = ThemisInstruction::SubmitInteractions {
223226
encrypted_interactions,
224-
public_key: pk,
225227
}
226228
.serialize()
227229
.unwrap();
@@ -251,7 +253,7 @@ fn assert_instruction_count() {
251253

252254
let instruction_data = ThemisInstruction::SubmitProofDecryption {
253255
plaintext: decrypted_aggregate,
254-
announcement,
256+
announcement: Box::new(announcement),
255257
response,
256258
}
257259
.serialize()
@@ -267,20 +269,25 @@ fn assert_instruction_count() {
267269

268270
println!("BPF instructions executed");
269271
println!(
270-
" InitializePolicies({}): {:?} ({:?})",
271-
num_scalars, initialize_policies_count, BASELINE_NEW_POLICIES_COUNT
272+
" InitializePolicies({}): {} ({:?})",
273+
num_scalars,
274+
initialize_policies_count.separated_string(),
275+
BASELINE_NEW_POLICIES_COUNT
272276
);
273277
println!(
274-
" InitializeUserAccount: {:?} ({:?})",
275-
initialize_user_count, BASELINE_INITIALIZE_USER_COUNT
278+
" InitializeUserAccount: {} ({:?})",
279+
initialize_user_count.separated_string(),
280+
BASELINE_INITIALIZE_USER_COUNT
276281
);
277282
println!(
278-
" CalculateAggregate: {:?} ({:?})",
279-
calculate_aggregate_count, BASELINE_CALCULATE_AGGREGATE_COUNT
283+
" CalculateAggregate: {} ({:?})",
284+
calculate_aggregate_count.separated_string(),
285+
BASELINE_CALCULATE_AGGREGATE_COUNT
280286
);
281287
println!(
282-
" SubmitProofDecryption: {:?} ({:?})",
283-
proof_decryption_count, BASELINE_PROOF_DECRYPTION_COUNT
288+
" SubmitProofDecryption: {} ({:?})",
289+
proof_decryption_count.separated_string(),
290+
BASELINE_PROOF_DECRYPTION_COUNT
284291
);
285292

286293
assert!(initialize_policies_count <= BASELINE_NEW_POLICIES_COUNT);

themis/program/program-id.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6q92wS9neZdex1jT72zqeWEPJ6YYNSqYjF6PKrqbVUwJ
1+
F3FWeYPjD1jeR6UykMj1GRbCcmoxtJnDiPuFdTLRGvb6

themis/program/src/error.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Error types
22
33
use num_derive::FromPrimitive;
4+
use num_traits::FromPrimitive;
5+
use solana_sdk::program_error::PrintProgramError;
46
use solana_sdk::{decode_error::DecodeError, program_error::ProgramError};
57
use thiserror::Error;
68

@@ -25,3 +27,15 @@ impl<T> DecodeError<T> for ThemisError {
2527
"ThemisError"
2628
}
2729
}
30+
31+
impl PrintProgramError for ThemisError {
32+
fn print<E>(&self)
33+
where
34+
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
35+
{
36+
match self {
37+
ThemisError::InvalidInstruction => println!("Error: Invalid instruction"),
38+
ThemisError::AccountInUse => println!("Error: Account in use"),
39+
}
40+
}
41+
}

themis/program/src/instruction.rs

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ pub enum ThemisInstruction {
2323
/// Accounts expected by this instruction:
2424
///
2525
/// 0. `[writable]` The account to initialize.
26-
InitializeUserAccount,
26+
InitializeUserAccount {
27+
/// Public key for all encrypted interations
28+
public_key: PublicKey,
29+
},
2730

2831
/// Initialize a new policies account
2932
///
@@ -35,8 +38,20 @@ pub enum ThemisInstruction {
3538
///
3639
/// 0. `[writable]` The account to initialize.
3740
InitializePoliciesAccount {
41+
/// Number of policies to be added
42+
num_scalars: u8,
43+
},
44+
45+
/// Store policies
46+
///
47+
/// The `StorePolices` instruction is used to set individual policies.
48+
///
49+
/// Accounts expected by this instruction:
50+
///
51+
/// 0. `[writable, signer]` The policies account.
52+
StorePolicies {
3853
/// Policies to be added
39-
scalars: Vec<Fr>,
54+
scalars: Vec<(u8, Fr)>,
4055
},
4156

4257
/// Calculate aggregate. The length of the `input` vector must equal the
@@ -46,12 +61,9 @@ pub enum ThemisInstruction {
4661
///
4762
/// 0. `[writable, signer]` The user account
4863
/// 1. `[]` The policies account
49-
CalculateAggregate {
64+
SubmitInteractions {
5065
/// Encrypted interactions
51-
encrypted_interactions: Vec<(G1, G1)>,
52-
53-
/// Public key for all encrypted interations
54-
public_key: PublicKey,
66+
encrypted_interactions: Vec<(u8, (G1, G1))>,
5567
},
5668

5769
/// Submit proof decryption
@@ -64,7 +76,7 @@ pub enum ThemisInstruction {
6476
plaintext: G1,
6577

6678
/// (announcement_g, announcement_ctx)
67-
announcement: (G1, G1),
79+
announcement: Box<(G1, G1)>,
6880

6981
/// response
7082
response: Fr,
@@ -99,8 +111,8 @@ impl ThemisInstruction {
99111
}
100112

101113
/// Return an `InitializeUserAccount` instruction.
102-
fn initialize_user_account(user_pubkey: &Pubkey) -> Instruction {
103-
let data = ThemisInstruction::InitializeUserAccount;
114+
fn initialize_user_account(user_pubkey: &Pubkey, public_key: PublicKey) -> Instruction {
115+
let data = ThemisInstruction::InitializeUserAccount { public_key };
104116

105117
let accounts = vec![AccountMeta::new(*user_pubkey, false)];
106118

@@ -112,17 +124,22 @@ fn initialize_user_account(user_pubkey: &Pubkey) -> Instruction {
112124
}
113125

114126
/// Return two instructions that create and initialize a user account.
115-
pub fn create_user_account(from: &Pubkey, user_pubkey: &Pubkey, lamports: u64) -> Vec<Instruction> {
127+
pub fn create_user_account(
128+
from: &Pubkey,
129+
user_pubkey: &Pubkey,
130+
lamports: u64,
131+
public_key: PublicKey,
132+
) -> Vec<Instruction> {
116133
let space = User::default().try_to_vec().unwrap().len() as u64;
117134
vec![
118135
system_instruction::create_account(from, user_pubkey, lamports, space, &crate::id()),
119-
initialize_user_account(user_pubkey),
136+
initialize_user_account(user_pubkey, public_key),
120137
]
121138
}
122139

123140
/// Return an `InitializePoliciesAccount` instruction.
124-
fn initialize_policies_account(policies_pubkey: &Pubkey, scalars: Vec<Fr>) -> Instruction {
125-
let data = ThemisInstruction::InitializePoliciesAccount { scalars };
141+
fn initialize_policies_account(policies_pubkey: &Pubkey, num_scalars: u8) -> Instruction {
142+
let data = ThemisInstruction::InitializePoliciesAccount { num_scalars };
126143
let accounts = vec![AccountMeta::new(*policies_pubkey, false)];
127144
Instruction {
128145
program_id: crate::id(),
@@ -136,31 +153,34 @@ pub fn create_policies_account(
136153
from: &Pubkey,
137154
policies_pubkey: &Pubkey,
138155
lamports: u64,
139-
scalars: Vec<Fr>,
156+
num_scalars: u8,
140157
) -> Vec<Instruction> {
141-
let space = Policies {
142-
scalars: scalars.clone(),
143-
..Policies::default()
144-
}
145-
.try_to_vec()
146-
.unwrap()
147-
.len() as u64;
158+
let space = Policies::new(num_scalars).try_to_vec().unwrap().len() as u64;
148159
vec![
149160
system_instruction::create_account(from, policies_pubkey, lamports, space, &crate::id()),
150-
initialize_policies_account(policies_pubkey, scalars),
161+
initialize_policies_account(policies_pubkey, num_scalars),
151162
]
152163
}
153164

154-
/// Return a `CalculateAggregate` instruction.
155-
pub fn calculate_aggregate(
165+
/// Return an `InitializePoliciesAccount` instruction.
166+
pub fn store_policies(policies_pubkey: &Pubkey, scalars: Vec<(u8, Fr)>) -> Instruction {
167+
let data = ThemisInstruction::StorePolicies { scalars };
168+
let accounts = vec![AccountMeta::new(*policies_pubkey, true)];
169+
Instruction {
170+
program_id: crate::id(),
171+
accounts,
172+
data: data.serialize().unwrap(),
173+
}
174+
}
175+
176+
/// Return a `SubmitInteractions` instruction.
177+
pub fn submit_interactions(
156178
user_pubkey: &Pubkey,
157179
policies_pubkey: &Pubkey,
158-
encrypted_interactions: Vec<(G1, G1)>,
159-
public_key: PublicKey,
180+
encrypted_interactions: Vec<(u8, (G1, G1))>,
160181
) -> Instruction {
161-
let data = ThemisInstruction::CalculateAggregate {
182+
let data = ThemisInstruction::SubmitInteractions {
162183
encrypted_interactions,
163-
public_key,
164184
};
165185
let accounts = vec![
166186
AccountMeta::new(*user_pubkey, true),
@@ -183,7 +203,7 @@ pub fn submit_proof_decryption(
183203
) -> Instruction {
184204
let data = ThemisInstruction::SubmitProofDecryption {
185205
plaintext,
186-
announcement: (announcement_g, announcement_ctx),
206+
announcement: Box::new((announcement_g, announcement_ctx)),
187207
response,
188208
};
189209
let accounts = vec![AccountMeta::new(*user_pubkey, true)];

themis/program/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ pub mod state;
1111
// solana-sdk version
1212
pub use solana_sdk;
1313

14-
solana_sdk::declare_id!("6q92wS9neZdex1jT72zqeWEPJ6YYNSqYjF6PKrqbVUwJ");
14+
solana_sdk::declare_id!("F3FWeYPjD1jeR6UykMj1GRbCcmoxtJnDiPuFdTLRGvb6");

0 commit comments

Comments
 (0)