Files
khadhroony-solana-project/crates/ksp-wallet-lib/src/runtime.rs

218 lines
10 KiB
Rust

// file: crates/ksp-wallet-lib/src/runtime.rs
// version: 2
//! Version-neutral unlocked Wallet state dispatch used by stable OWNER/VIEW handles.
/// One staged native envelope produced by an authenticated administration operation.
pub(crate) enum StagedEnvelope {
V1(crate::KspWalletEnvelopeV1),
V2(crate::KspWalletEnvelopeV2),
}
/// Version-neutral OWNER runtime state.
pub(crate) enum OwnerState {
V1(crate::OwnerStateV1),
V2(crate::OwnerStateV2),
}
impl OwnerState {
/// Exports the Solana identity through the requested external transfer format.
pub(crate) fn export_transfer(&self, format: crate::WalletTransferFormat) -> ksp_core_lib::Result<std::vec::Vec<u8>> {
return match self {
Self::V1(state) => state.export_transfer(format),
Self::V2(state) => state.export_transfer(format),
};
}
/// Signs one message with the OWNER-authorized Solana identity.
pub(crate) fn sign_message(&self, message: &[u8]) -> ksp_core_lib::Result<[u8; crate::KSPWALLET_SOLANA_SIGNATURE_BYTES]> {
return match self {
Self::V1(state) => state.sign_message(message),
Self::V2(state) => state.sign_message(message),
};
}
/// Stages an authenticated metadata replacement without publishing it.
pub(crate) fn stage_metadata_payload(&self, payload: crate::MetadataPayloadV1) -> ksp_core_lib::Result<(StagedEnvelope, crate::WalletInfo)> {
return match self {
Self::V1(state) => state.stage_metadata_payload(payload).map(|(envelope, info)| return (StagedEnvelope::V1(envelope), info)),
Self::V2(state) => state.stage_metadata_payload(payload).map(|(envelope, info)| return (StagedEnvelope::V2(envelope), info)),
};
}
/// Stages an OWNER credential rotation in the state native format.
pub(crate) async fn stage_owner_password_rotation(&self, password: crate::OwnerPassword) -> ksp_core_lib::Result<StagedEnvelope> {
return match self {
Self::V1(state) => state.stage_owner_password_rotation(password).await.map(StagedEnvelope::V1),
Self::V2(state) => state.stage_owner_password_rotation(password).await.map(StagedEnvelope::V2),
};
}
/// Stages a VIEW credential rotation in the state native format.
pub(crate) async fn stage_view_password_rotation(&self, password: crate::ViewPassword) -> ksp_core_lib::Result<StagedEnvelope> {
return match self {
Self::V1(state) => state.stage_view_password_rotation(password).await.map(StagedEnvelope::V1),
Self::V2(state) => state.stage_view_password_rotation(password).await.map(StagedEnvelope::V2),
};
}
/// Stages strong VIEW disable while retaining OWNER authority.
pub(crate) fn stage_disable_view(&self) -> ksp_core_lib::Result<(StagedEnvelope, crate::SecretKeyV1)> {
return match self {
Self::V1(state) => state.stage_disable_view().map(|(envelope, key)| return (StagedEnvelope::V1(envelope), key)),
Self::V2(state) => state.stage_disable_view().map(|(envelope, key)| return (StagedEnvelope::V2(envelope), key)),
};
}
/// Stages strong VIEW recreation under a new credential.
pub(crate) async fn stage_recreate_view(&self, password: crate::ViewPassword) -> ksp_core_lib::Result<(StagedEnvelope, crate::SecretKeyV1)> {
return match self {
Self::V1(state) => state.stage_recreate_view(password).await.map(|(envelope, key)| return (StagedEnvelope::V1(envelope), key)),
Self::V2(state) => state.stage_recreate_view(password).await.map(|(envelope, key)| return (StagedEnvelope::V2(envelope), key)),
};
}
/// Atomically publishes a staged envelope after format-specific state-conflict verification.
pub(crate) async fn persist_staged(&self, destination: std::path::PathBuf, envelope: &StagedEnvelope) -> ksp_core_lib::Result<()> {
return match (self, envelope) {
(Self::V1(state), StagedEnvelope::V1(staged)) => {
let serialized = match staged.to_json_bytes() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
crate::replace_wallet_file_v1(destination, state.envelope().clone(), serialized).await
},
(Self::V2(state), StagedEnvelope::V2(staged)) => {
let serialized = match staged.to_binary_bytes() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
crate::replace_wallet_file_v2(destination, state.envelope().clone(), serialized).await
},
_ => std::result::Result::Err(version_state_error()),
};
}
/// Replaces the in-memory authenticated envelope after successful persistence.
pub(crate) fn apply_envelope(&mut self, envelope: StagedEnvelope) -> ksp_core_lib::Result<()> {
return match (self, envelope) {
(Self::V1(state), StagedEnvelope::V1(value)) => {
state.apply_envelope(value);
std::result::Result::Ok(())
},
(Self::V2(state), StagedEnvelope::V2(value)) => {
state.apply_envelope(value);
std::result::Result::Ok(())
},
_ => std::result::Result::Err(version_state_error()),
};
}
/// Replaces the envelope and metadata key after a strong VIEW administration operation.
pub(crate) fn apply_strong_view_state(&mut self, envelope: StagedEnvelope, metadata_key: crate::SecretKeyV1) -> ksp_core_lib::Result<()> {
return match (self, envelope) {
(Self::V1(state), StagedEnvelope::V1(value)) => {
state.apply_strong_view_state(value, metadata_key);
std::result::Result::Ok(())
},
(Self::V2(state), StagedEnvelope::V2(value)) => {
state.apply_strong_view_state(value, metadata_key);
std::result::Result::Ok(())
},
_ => std::result::Result::Err(version_state_error()),
};
}
/// Serializes the authenticated state in its native V1 or V2 wire format.
pub(crate) fn native_bytes(&self) -> ksp_core_lib::Result<std::vec::Vec<u8>> {
return match self {
Self::V1(state) => state.envelope().to_json_bytes(),
Self::V2(state) => state.envelope().to_binary_bytes(),
};
}
/// Serializes only V1 state as JSON and rejects V2 instead of converting formats.
pub(crate) fn json_bytes_v1(&self) -> ksp_core_lib::Result<std::vec::Vec<u8>> {
return match self {
Self::V1(state) => state.envelope().to_json_bytes(),
Self::V2(_) => std::result::Result::Err(v1_serialization_error()),
};
}
}
/// Version-neutral VIEW runtime state.
pub(crate) enum ViewState {
V1(crate::ViewStateV1),
V2(crate::ViewStateV2),
}
impl ViewState {
/// Stages a VIEW credential rotation in the state native format.
pub(crate) async fn stage_view_password_rotation(&self, password: crate::ViewPassword) -> ksp_core_lib::Result<StagedEnvelope> {
return match self {
Self::V1(state) => state.stage_view_password_rotation(password).await.map(StagedEnvelope::V1),
Self::V2(state) => state.stage_view_password_rotation(password).await.map(StagedEnvelope::V2),
};
}
/// Atomically publishes a staged envelope after format-specific state-conflict verification.
pub(crate) async fn persist_staged(&self, destination: std::path::PathBuf, envelope: &StagedEnvelope) -> ksp_core_lib::Result<()> {
return match (self, envelope) {
(Self::V1(state), StagedEnvelope::V1(staged)) => {
let serialized = match staged.to_json_bytes() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
crate::replace_wallet_file_v1(destination, state.envelope().clone(), serialized).await
},
(Self::V2(state), StagedEnvelope::V2(staged)) => {
let serialized = match staged.to_binary_bytes() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
crate::replace_wallet_file_v2(destination, state.envelope().clone(), serialized).await
},
_ => std::result::Result::Err(version_state_error()),
};
}
/// Replaces the in-memory authenticated envelope after successful persistence.
pub(crate) fn apply_envelope(&mut self, envelope: StagedEnvelope) -> ksp_core_lib::Result<()> {
return match (self, envelope) {
(Self::V1(state), StagedEnvelope::V1(value)) => {
state.apply_envelope(value);
std::result::Result::Ok(())
},
(Self::V2(state), StagedEnvelope::V2(value)) => {
state.apply_envelope(value);
std::result::Result::Ok(())
},
_ => std::result::Result::Err(version_state_error()),
};
}
/// Serializes the authenticated state in its native V1 or V2 wire format.
pub(crate) fn native_bytes(&self) -> ksp_core_lib::Result<std::vec::Vec<u8>> {
return match self {
Self::V1(state) => state.envelope().to_json_bytes(),
Self::V2(state) => state.envelope().to_binary_bytes(),
};
}
/// Serializes only V1 state as JSON and rejects V2 instead of converting formats.
pub(crate) fn json_bytes_v1(&self) -> ksp_core_lib::Result<std::vec::Vec<u8>> {
return match self {
Self::V1(state) => state.envelope().to_json_bytes(),
Self::V2(_) => std::result::Result::Err(v1_serialization_error()),
};
}
}
fn version_state_error() -> ksp_core_lib::Error {
return ksp_core_lib::Error::new(crate::ERROR_CODE_FORMAT_INVALID, "Wallet runtime state and staged format are inconsistent");
}
fn v1_serialization_error() -> ksp_core_lib::Error {
return ksp_core_lib::Error::new(crate::ERROR_CODE_FORMAT_INVALID, "Wallet is not a V1 JSON document");
}