0.1.0
This commit is contained in:
@@ -0,0 +1,406 @@
|
||||
// file: kb_pipeline/src/solana_elgamal_registry_stateful.rs
|
||||
// version: 3
|
||||
|
||||
//! Contextual SPL ElGamal registry account-state validation and materialization routing.
|
||||
|
||||
use std::str::FromStr; // rust-rules: trait-import
|
||||
|
||||
/// Exact byte length of one SPL ElGamal registry account.
|
||||
pub const ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES: usize = 64;
|
||||
|
||||
/// One bounded SPL ElGamal registry RPC read request.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ElGamalRegistryStatefulReadRequest {
|
||||
/// Endpoint role used for the HTTP RPC request.
|
||||
pub query_role: std::string::String,
|
||||
/// Canonical registry PDA.
|
||||
pub registry_account: kb_model::Pubkey,
|
||||
/// Optional minimum RPC context slot.
|
||||
pub min_context_slot: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
/// One bounded registry RPC read and validated administrative projection.
|
||||
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
pub struct ElGamalRegistryStatefulReadResult {
|
||||
/// Commitment used for the RPC read.
|
||||
pub commitment: std::string::String,
|
||||
/// Context slot returned by the endpoint.
|
||||
pub context_slot: u64,
|
||||
/// Complete validated registry snapshot.
|
||||
pub snapshot: crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot,
|
||||
}
|
||||
|
||||
/// Reads, validates, and routes one exact SPL ElGamal registry account.
|
||||
pub async fn read_elgamal_registry_stateful_snapshot(
|
||||
pool: &kb_rpc::HttpEndpointPool,
|
||||
request: &crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest,
|
||||
) -> kb_core::Result<crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult> {
|
||||
if request.query_role.trim().is_empty() {
|
||||
return std::result::Result::Err(kb_core::Error::config(
|
||||
"SPL ElGamal registry stateful read query_role must not be empty",
|
||||
));
|
||||
}
|
||||
let config = match kb_rpc::GetAccountInfoConfig::new_with_data(
|
||||
kb_rpc::RpcCommitmentLevel::Confirmed,
|
||||
request.min_context_slot,
|
||||
crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let result = match pool
|
||||
.get_account_info_for_role(request.query_role.as_str(), &request.registry_account, &config)
|
||||
.await
|
||||
{
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(
|
||||
request,
|
||||
&result,
|
||||
);
|
||||
}
|
||||
|
||||
/// Validates one complete RPC registry response before parsing and routing.
|
||||
pub fn materialize_elgamal_registry_account_info_result(
|
||||
request: &crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest,
|
||||
result: &kb_rpc::AccountInfoResult,
|
||||
) -> kb_core::Result<crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult> {
|
||||
if let std::option::Option::Some(min_context_slot) = request.min_context_slot {
|
||||
if result.context.slot < min_context_slot {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"elgamal_registry_stateful_context_slot_too_old",
|
||||
format!(
|
||||
"SPL ElGamal registry context slot {} is below requested minimum {min_context_slot}",
|
||||
result.context.slot
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
let account = match result.account.as_ref() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"elgamal_registry_stateful_account_missing",
|
||||
format!(
|
||||
"SPL ElGamal registry account {} does not exist",
|
||||
request.registry_account.0
|
||||
),
|
||||
));
|
||||
},
|
||||
};
|
||||
if account.executable {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"elgamal_registry_stateful_account_executable",
|
||||
format!(
|
||||
"SPL ElGamal registry state account {} must not be executable",
|
||||
request.registry_account.0
|
||||
),
|
||||
));
|
||||
}
|
||||
if account.owner.0 != kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"elgamal_registry_stateful_owner_mismatch",
|
||||
format!(
|
||||
"SPL ElGamal registry owner must be {}, got {}",
|
||||
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
|
||||
account.owner.0
|
||||
),
|
||||
));
|
||||
}
|
||||
if account.space
|
||||
!= crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES as u64
|
||||
|| account.data.len()
|
||||
!= crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES
|
||||
{
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"elgamal_registry_stateful_account_length_invalid",
|
||||
format!(
|
||||
"SPL ElGamal registry account must report and return exactly {} bytes, got space {} and data {}",
|
||||
crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES,
|
||||
account.space,
|
||||
account.data.len()
|
||||
),
|
||||
));
|
||||
}
|
||||
let snapshot = match crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
|
||||
request.registry_account.0.as_str(),
|
||||
account.owner.0.as_str(),
|
||||
result.context.slot,
|
||||
account.data.as_slice(),
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(kb_core::Error::new(
|
||||
"elgamal_registry_stateful_projection_failed",
|
||||
error,
|
||||
));
|
||||
},
|
||||
};
|
||||
return std::result::Result::Ok(
|
||||
crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult {
|
||||
commitment: "confirmed".to_string(),
|
||||
context_slot: result.context.slot,
|
||||
snapshot,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// One contextually validated SPL ElGamal registry state snapshot.
|
||||
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
pub struct ElGamalRegistryStatefulSnapshot {
|
||||
/// Canonical registry PDA identity.
|
||||
pub registry_account_key: std::string::String,
|
||||
/// Wallet address stored as the registry owner.
|
||||
pub owner: std::string::String,
|
||||
/// Context slot associated with the account read.
|
||||
pub slot: u64,
|
||||
/// Processor-owned administrative projection.
|
||||
pub output: kb_materializer_api::MaterializedOutput,
|
||||
}
|
||||
|
||||
/// Parse, validate the official owner-derived PDA, and materialize one registry snapshot.
|
||||
pub fn materialize_elgamal_registry_stateful_snapshot(
|
||||
registry_account_key: &str,
|
||||
owner_program_id: &str,
|
||||
slot: u64,
|
||||
data: &[u8],
|
||||
) -> std::result::Result<
|
||||
crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot,
|
||||
String,
|
||||
> {
|
||||
if owner_program_id != kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID {
|
||||
return std::result::Result::Err(format!(
|
||||
"SPL ElGamal registry snapshot owner must be {}, got {owner_program_id}",
|
||||
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID
|
||||
));
|
||||
}
|
||||
let state = match kb_decoder_spl_elgamal_registry::state::parse_elgamal_registry_state(data) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let owner = match solana_pubkey::Pubkey::from_str(state.owner.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(format!(
|
||||
"SPL ElGamal registry owner is not a valid address: {error}"
|
||||
));
|
||||
},
|
||||
};
|
||||
let program_id = match solana_pubkey::Pubkey::from_str(
|
||||
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(format!(
|
||||
"Configured SPL ElGamal registry program ID is invalid: {error}"
|
||||
));
|
||||
},
|
||||
};
|
||||
let expected =
|
||||
spl_elgamal_registry_interface::get_elgamal_registry_address(&owner, &program_id);
|
||||
if expected.to_string() != registry_account_key {
|
||||
return std::result::Result::Err(format!(
|
||||
"SPL ElGamal registry account {registry_account_key} does not match owner-derived PDA {expected}"
|
||||
));
|
||||
}
|
||||
let output = match kb_materializer_admin::materialize_elgamal_registry_state_snapshot(
|
||||
registry_account_key,
|
||||
slot,
|
||||
&state,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(
|
||||
crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot {
|
||||
registry_account_key: registry_account_key.to_string(),
|
||||
owner: state.owner,
|
||||
slot,
|
||||
output,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr; // rust-rules: trait-import
|
||||
|
||||
fn registry_fixture(owner: &solana_pubkey::Pubkey) -> (std::string::String, [u8; 64]) {
|
||||
let program_id = match solana_pubkey::Pubkey::from_str(
|
||||
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => solana_pubkey::Pubkey::default(),
|
||||
};
|
||||
let registry =
|
||||
spl_elgamal_registry_interface::get_elgamal_registry_address(owner, &program_id);
|
||||
let mut data = [0u8; 64];
|
||||
data[..32].copy_from_slice(owner.as_ref());
|
||||
data[32..].copy_from_slice(&[9u8; 32]);
|
||||
return (registry.to_string(), data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exact_owner_derived_registry_pda_routes_one_admin_snapshot() {
|
||||
let owner = solana_pubkey::Pubkey::new_from_array([7u8; 32]);
|
||||
let (registry, data) =
|
||||
crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner);
|
||||
let snapshot =
|
||||
crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
|
||||
registry.as_str(),
|
||||
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
|
||||
55,
|
||||
&data,
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.as_ref().map(|value| return value.owner.clone()),
|
||||
std::result::Result::Ok(owner.to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot
|
||||
.as_ref()
|
||||
.map(|value| return value.output.payload_json["finalAccountStateCaptured"].clone()),
|
||||
std::result::Result::Ok(serde_json::json!(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_owner_program_wrong_pda_and_invalid_length_fail_closed() {
|
||||
let owner = solana_pubkey::Pubkey::new_from_array([8u8; 32]);
|
||||
let (registry, data) =
|
||||
crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner);
|
||||
let foreign =
|
||||
crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
|
||||
registry.as_str(),
|
||||
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
|
||||
55,
|
||||
&data,
|
||||
);
|
||||
assert!(foreign.is_err());
|
||||
let wrong =
|
||||
crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
|
||||
solana_pubkey::Pubkey::new_from_array([10u8; 32]).to_string().as_str(),
|
||||
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
|
||||
55,
|
||||
&data,
|
||||
);
|
||||
assert!(wrong.is_err());
|
||||
let invalid =
|
||||
crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot(
|
||||
registry.as_str(),
|
||||
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID,
|
||||
55,
|
||||
&data[..63],
|
||||
);
|
||||
assert!(invalid.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exact_rpc_registry_routes_after_context_owner_and_length_validation() {
|
||||
let owner = solana_pubkey::Pubkey::new_from_array([13u8; 32]);
|
||||
let (registry, data) =
|
||||
crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner);
|
||||
let request = crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest {
|
||||
query_role: "execution".to_string(),
|
||||
registry_account: kb_model::Pubkey(registry),
|
||||
min_context_slot: std::option::Option::Some(70),
|
||||
};
|
||||
let result = kb_rpc::AccountInfoResult {
|
||||
context: kb_rpc::RpcResponseContext {
|
||||
slot: 71,
|
||||
api_version: std::option::Option::None,
|
||||
},
|
||||
account: std::option::Option::Some(kb_rpc::AccountInfoValue {
|
||||
lamports: 1,
|
||||
owner: kb_model::ProgramId(
|
||||
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID.to_string(),
|
||||
),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
space: 64,
|
||||
data: data.to_vec(),
|
||||
}),
|
||||
};
|
||||
let materialized = crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(
|
||||
&request,
|
||||
&result,
|
||||
);
|
||||
assert_eq!(
|
||||
materialized.as_ref().map(|value| return value.context_slot),
|
||||
std::result::Result::Ok(71)
|
||||
);
|
||||
assert_eq!(
|
||||
materialized.as_ref().map(|value| return value.snapshot.owner.clone()),
|
||||
std::result::Result::Ok(owner.to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stale_missing_foreign_executable_and_wrong_length_registry_reads_fail_closed() {
|
||||
let owner = solana_pubkey::Pubkey::new_from_array([14u8; 32]);
|
||||
let (registry, data) =
|
||||
crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner);
|
||||
let request = crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest {
|
||||
query_role: "execution".to_string(),
|
||||
registry_account: kb_model::Pubkey(registry),
|
||||
min_context_slot: std::option::Option::Some(80),
|
||||
};
|
||||
let base = kb_rpc::AccountInfoValue {
|
||||
lamports: 1,
|
||||
owner: kb_model::ProgramId(
|
||||
kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID.to_string(),
|
||||
),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
space: 64,
|
||||
data: data.to_vec(),
|
||||
};
|
||||
let stale = kb_rpc::AccountInfoResult {
|
||||
context: kb_rpc::RpcResponseContext {
|
||||
slot: 79,
|
||||
api_version: std::option::Option::None,
|
||||
},
|
||||
account: std::option::Option::Some(base.clone()),
|
||||
};
|
||||
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &stale).is_err());
|
||||
let missing = kb_rpc::AccountInfoResult {
|
||||
context: kb_rpc::RpcResponseContext {
|
||||
slot: 80,
|
||||
api_version: std::option::Option::None,
|
||||
},
|
||||
account: std::option::Option::None,
|
||||
};
|
||||
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &missing).is_err());
|
||||
let mut foreign = base.clone();
|
||||
foreign.owner = kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string());
|
||||
let foreign = kb_rpc::AccountInfoResult {
|
||||
context: kb_rpc::RpcResponseContext {
|
||||
slot: 80,
|
||||
api_version: std::option::Option::None,
|
||||
},
|
||||
account: std::option::Option::Some(foreign),
|
||||
};
|
||||
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &foreign).is_err());
|
||||
let mut executable = base.clone();
|
||||
executable.executable = true;
|
||||
let executable = kb_rpc::AccountInfoResult {
|
||||
context: kb_rpc::RpcResponseContext {
|
||||
slot: 80,
|
||||
api_version: std::option::Option::None,
|
||||
},
|
||||
account: std::option::Option::Some(executable),
|
||||
};
|
||||
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &executable).is_err());
|
||||
let mut wrong_length = base;
|
||||
wrong_length.data.pop();
|
||||
let wrong_length = kb_rpc::AccountInfoResult {
|
||||
context: kb_rpc::RpcResponseContext {
|
||||
slot: 80,
|
||||
api_version: std::option::Option::None,
|
||||
},
|
||||
account: std::option::Option::Some(wrong_length),
|
||||
};
|
||||
assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &wrong_length).is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user