282 lines
12 KiB
Rust
282 lines
12 KiB
Rust
// file: ks-lib/src/executor/spl/elgamal_registry/builder.rs
|
|
// version: 9
|
|
|
|
//! Official registry builders and conservative plan validation.
|
|
|
|
use base64::Engine; // rust-rules: trait-import
|
|
use std::str::FromStr; // rust-rules: trait-import
|
|
|
|
pub(crate) fn executor_spl_elgamal_registry_build_prepared_plan(
|
|
intent: &crate::ExSplElgamalRegistryExecutionIntent,
|
|
) -> ks_core::Result<crate::ExApiPreparedExecutionPlan> {
|
|
if intent.intent_id.trim().is_empty() {
|
|
return std::result::Result::Err(ks_core::Error::new(
|
|
"execution_spl_elgamal_registry_intent_id_empty",
|
|
"ElGamal registry execution intent id must not be empty",
|
|
));
|
|
}
|
|
if intent.policy.simulation != crate::ExApiExecutionSimulationPolicy::Required {
|
|
return std::result::Result::Err(ks_core::Error::new(
|
|
"execution_spl_elgamal_registry_simulation_required",
|
|
"ElGamal registry execution requires simulation",
|
|
));
|
|
}
|
|
match intent.policy.cost_limit.max_fee_lamports {
|
|
std::option::Option::Some(value) if value > 0 => {},
|
|
std::option::Option::Some(_) | std::option::Option::None => {
|
|
return std::result::Result::Err(ks_core::Error::new(
|
|
"execution_spl_elgamal_registry_fee_limit_missing",
|
|
"ElGamal registry execution requires a positive fee ceiling",
|
|
));
|
|
},
|
|
}
|
|
let validation = &intent.policy.post_execution_validation;
|
|
if !validation.canonical_insert_required
|
|
|| !validation.core_extraction_required
|
|
|| !validation.decode_replay_required
|
|
|| !validation.materialization_required
|
|
{
|
|
return std::result::Result::Err(ks_core::Error::new(
|
|
"execution_spl_elgamal_registry_post_validation_required",
|
|
"ElGamal registry execution requires canonical insert, core extraction, decode replay and materialization",
|
|
));
|
|
}
|
|
let fee_payer = match parse_address(&intent.fee_payer, "fee_payer") {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let (owner_model, proof, operation_code, create) = match &intent.operation {
|
|
crate::ExSplElgamalRegistryOperation::CreateRegistry { owner, proof } => {
|
|
(owner, proof, crate::EX_SPL_ELGAMAL_REGISTRY_CREATE_OPERATION, true)
|
|
},
|
|
crate::ExSplElgamalRegistryOperation::UpdateRegistry { owner, proof } => {
|
|
(owner, proof, crate::EX_SPL_ELGAMAL_REGISTRY_UPDATE_OPERATION, false)
|
|
},
|
|
};
|
|
let owner = match parse_address(owner_model, "owner") {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let instructions = match proof {
|
|
crate::ExSplElgamalRegistryProofLocation::ContextStateAccount { account } => {
|
|
let context = match parse_address(account, "context_state_account") {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let location = spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&context);
|
|
build_official(create, &owner, location)
|
|
},
|
|
crate::ExSplElgamalRegistryProofLocation::Inline { proof_data_base64 } => {
|
|
let bytes = match base64::engine::general_purpose::STANDARD
|
|
.decode(proof_data_base64.as_bytes())
|
|
{
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => {
|
|
return std::result::Result::Err(ks_core::Error::new(
|
|
"execution_spl_elgamal_registry_proof_base64_invalid",
|
|
error.to_string(),
|
|
));
|
|
},
|
|
};
|
|
let expected = std::mem::size_of::<
|
|
solana_zk_elgamal_proof_interface::proof_data::PubkeyValidityProofData,
|
|
>();
|
|
if bytes.len() != expected {
|
|
return std::result::Result::Err(ks_core::Error::new(
|
|
"execution_spl_elgamal_registry_proof_size_invalid",
|
|
format!(
|
|
"PubkeyValidityProofData requires {expected} bytes, received {}",
|
|
bytes.len()
|
|
),
|
|
));
|
|
}
|
|
let proof_data = bytemuck::pod_read_unaligned::<
|
|
solana_zk_elgamal_proof_interface::proof_data::PubkeyValidityProofData,
|
|
>(bytes.as_slice());
|
|
let offset = match std::num::NonZeroI8::new(1) {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(ks_core::Error::new(
|
|
"execution_spl_elgamal_registry_internal_offset_invalid",
|
|
"inline proof offset must be one",
|
|
));
|
|
},
|
|
};
|
|
let location = spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &proof_data);
|
|
build_official(create, &owner, location)
|
|
},
|
|
};
|
|
let instructions: Vec<solana_instruction::Instruction> = match instructions {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let planned: Vec<crate::ExApiPlannedInstruction> = instructions
|
|
.iter()
|
|
.map(|instruction| return planned_instruction(operation_code, instruction))
|
|
.collect();
|
|
let mut required_signers = vec![crate::ExApiRequiredSigner {
|
|
pubkey: intent.fee_payer.clone(),
|
|
role: std::string::String::from("fee_payer"),
|
|
}];
|
|
if intent.fee_payer != *owner_model {
|
|
required_signers.push(crate::ExApiRequiredSigner {
|
|
pubkey: owner_model.clone(),
|
|
role: std::string::String::from("registry_owner"),
|
|
});
|
|
}
|
|
tracing::debug!(
|
|
target: crate::TRACING_TARGET_EXECUTOR_SPL_ELGAMAL_REGISTRY,
|
|
action = "build_prepared_plan",
|
|
intent_id = %intent.intent_id,
|
|
operation_code,
|
|
owner = %owner_model.0,
|
|
instruction_count = planned.len(),
|
|
"built SPL ElGamal registry execution plan"
|
|
);
|
|
return std::result::Result::Ok(crate::ExApiPreparedExecutionPlan {
|
|
executor_name: std::string::String::from("ks-lib-executor.spl.elgamal_registry"),
|
|
executor_version: std::string::String::from(env!("CARGO_PKG_VERSION")),
|
|
intent_id: intent.intent_id.clone(),
|
|
operation_code: std::string::String::from(operation_code),
|
|
fee_payer: crate::MdPubkey(fee_payer.to_string()),
|
|
instructions: planned,
|
|
required_signers,
|
|
policy: intent.policy.clone(),
|
|
requested_spend_lamports: 0,
|
|
requested_compute_unit_price_micro_lamports: std::option::Option::None,
|
|
});
|
|
}
|
|
|
|
fn build_official(
|
|
create: bool,
|
|
owner: &solana_pubkey::Pubkey,
|
|
location: spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation<
|
|
solana_zk_elgamal_proof_interface::proof_data::PubkeyValidityProofData,
|
|
>,
|
|
) -> ks_core::Result<std::vec::Vec<solana_instruction::Instruction>> {
|
|
let result = if create {
|
|
spl_elgamal_registry_interface::instruction::create_registry(owner, location)
|
|
} else {
|
|
spl_elgamal_registry_interface::instruction::update_registry(owner, location)
|
|
};
|
|
return match result {
|
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
|
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new(
|
|
"execution_spl_elgamal_registry_builder_failed",
|
|
error.to_string(),
|
|
)),
|
|
};
|
|
}
|
|
|
|
fn parse_address(value: &crate::MdPubkey, field: &str) -> ks_core::Result<solana_pubkey::Pubkey> {
|
|
return match solana_pubkey::Pubkey::from_str(value.0.as_str()) {
|
|
std::result::Result::Ok(address) => std::result::Result::Ok(address),
|
|
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new(
|
|
"execution_spl_elgamal_registry_pubkey_invalid",
|
|
format!("{field}: {error}"),
|
|
)),
|
|
};
|
|
}
|
|
|
|
fn planned_instruction(
|
|
operation_code: &str,
|
|
instruction: &solana_instruction::Instruction,
|
|
) -> crate::ExApiPlannedInstruction {
|
|
return crate::ExApiPlannedInstruction {
|
|
program_id: crate::MdProgramId(instruction.program_id.to_string()),
|
|
operation_code: std::string::String::from(operation_code),
|
|
accounts: instruction
|
|
.accounts
|
|
.iter()
|
|
.map(|account| {
|
|
return crate::ExApiPlannedAccount {
|
|
pubkey: crate::MdPubkey(account.pubkey.to_string()),
|
|
is_signer: account.is_signer,
|
|
is_writable: account.is_writable,
|
|
};
|
|
})
|
|
.collect(),
|
|
data: instruction.data.clone(),
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
fn pubkey(value: &str) -> crate::MdPubkey {
|
|
return crate::MdPubkey(std::string::String::from(value));
|
|
}
|
|
|
|
fn intent(create: bool) -> crate::ExSplElgamalRegistryExecutionIntent {
|
|
let owner = pubkey(ks_program_ids::SYSTEM_PROGRAM_ID);
|
|
return crate::ExSplElgamalRegistryExecutionIntent {
|
|
intent_id: std::string::String::from("registry-intent"),
|
|
fee_payer: owner.clone(),
|
|
policy: crate::ExApiExecutionPolicy {
|
|
cost_limit: crate::ExApiExecutionCostLimit {
|
|
max_spend_lamports: std::option::Option::Some(0),
|
|
max_fee_lamports: std::option::Option::Some(20_000),
|
|
max_compute_unit_price_micro_lamports: std::option::Option::None,
|
|
},
|
|
authorized_signers: vec![owner.clone()],
|
|
dry_run: true,
|
|
post_execution_validation: crate::ExApiPostExecutionValidationPolicy {
|
|
canonical_insert_required: true,
|
|
core_extraction_required: true,
|
|
decode_replay_required: true,
|
|
materialization_required: true,
|
|
},
|
|
..crate::ExApiExecutionPolicy::default()
|
|
},
|
|
operation: if create {
|
|
crate::ExSplElgamalRegistryOperation::CreateRegistry {
|
|
owner,
|
|
proof: crate::ExSplElgamalRegistryProofLocation::ContextStateAccount {
|
|
account: pubkey(ks_program_ids::VOTE_PROGRAM_ID),
|
|
},
|
|
}
|
|
} else {
|
|
crate::ExSplElgamalRegistryOperation::UpdateRegistry {
|
|
owner,
|
|
proof: crate::ExSplElgamalRegistryProofLocation::ContextStateAccount {
|
|
account: pubkey(ks_program_ids::VOTE_PROGRAM_ID),
|
|
},
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn create_and_update_use_official_registry_wire_and_accounts() {
|
|
for create in [true, false] {
|
|
let plan = crate::executor_spl_elgamal_registry_build_prepared_plan(&intent(create))
|
|
.unwrap_or_else(|error| panic!("registry plan failed: {error}"));
|
|
assert_eq!(plan.instructions.len(), 1);
|
|
assert_eq!(
|
|
plan.instructions[0].program_id.0,
|
|
ks_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID
|
|
);
|
|
assert_eq!(plan.instructions[0].data, if create { vec![0, 0] } else { vec![1, 0] });
|
|
assert_eq!(plan.required_signers.len(), 1);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn optional_simulation_and_invalid_inline_proof_fail_closed() {
|
|
let mut value = intent(true);
|
|
value.policy.simulation = crate::ExApiExecutionSimulationPolicy::Optional;
|
|
let error = crate::executor_spl_elgamal_registry_build_prepared_plan(&value)
|
|
.expect_err("optional simulation must fail");
|
|
assert_eq!(error.code(), "execution_spl_elgamal_registry_simulation_required");
|
|
value = intent(true);
|
|
value.operation = crate::ExSplElgamalRegistryOperation::CreateRegistry {
|
|
owner: pubkey(ks_program_ids::SYSTEM_PROGRAM_ID),
|
|
proof: crate::ExSplElgamalRegistryProofLocation::Inline {
|
|
proof_data_base64: std::string::String::from("AA=="),
|
|
},
|
|
};
|
|
let error = crate::executor_spl_elgamal_registry_build_prepared_plan(&value)
|
|
.expect_err("invalid proof size must fail");
|
|
assert_eq!(error.code(), "execution_spl_elgamal_registry_proof_size_invalid");
|
|
}
|
|
}
|