9.4 KiB
Utilisation de ksp-wallet-lib
Ce guide présente les principales surfaces publiques de Wallet V1. La spécification cryptographique du fichier reste ../../docs/formats/KSPWALLET_V1.md.
Les exemples utilisent des chemins explicites : Wallet ne lit ni Config ni environnement pour découvrir un répertoire.
1. Créer un nouveau .kspwallet
async fn create_example() -> ksp_core_lib::Result<()> {
let metadata = ksp_wallet_lib::WalletCreateMetadataV1::new(
Some(std::string::String::from("devnet-main")),
vec![std::string::String::from("wallet de test")],
);
let owner_password = ksp_wallet_lib::OwnerPassword::new(std::string::String::from("OWNER-PASSWORD"));
let view_password = ksp_wallet_lib::ViewPassword::new(std::string::String::from("VIEW-PASSWORD"));
let created = ksp_wallet_lib::create_wallet_file_v1(
"wallets/devnet-main.kspwallet",
owner_password,
Some(view_password),
metadata,
)
.await;
let owner = match created {
Ok(value) => value,
Err(error) => return Err(error),
};
println!("{}", owner.pubkey());
return Ok(());
}
La destination doit avoir un parent existant. Une destination existante n'est jamais remplacée par une création.
Pour créer uniquement en mémoire, utiliser create_wallet_v1 puis WalletOwner::to_json_bytes() si le caller possède lui-même une autre boundary de stockage.
2. Inspecter un wallet verrouillé
async fn inspect_example() -> ksp_core_lib::Result<()> {
let inspected = ksp_wallet_lib::inspect_locked_wallet_file_v1("wallets/devnet-main.kspwallet").await;
let locked = match inspected {
Ok(value) => value,
Err(error) => return Err(error),
};
println!("format={}", locked.format_version());
println!("view_enabled={}", locked.view_enabled());
return Ok(());
}
Cette projection ne contient volontairement ni Pubkey, ni alias, ni notes.
3. Ouvrir avec VIEW
async fn open_view_example() -> ksp_core_lib::Result<()> {
let password = ksp_wallet_lib::ViewPassword::new(std::string::String::from("VIEW-PASSWORD"));
let opened = ksp_wallet_lib::open_wallet_view_file_v1("wallets/devnet-main.kspwallet", password).await;
let mut view = match opened {
Ok(value) => value,
Err(error) => return Err(error),
};
println!("pubkey={}", view.pubkey());
println!("alias={:?}", view.alias());
for note in view.notes() {
println!("note={} text={}", note.id(), note.text());
}
let rotated = view
.rotate_view_password(
"wallets/devnet-main.kspwallet",
ksp_wallet_lib::ViewPassword::new(std::string::String::from("NEW-VIEW-PASSWORD")),
)
.await;
if let Err(error) = rotated {
return Err(error);
}
return Ok(());
}
VIEW ne possède aucune API sign, export_transfer, update_alias, add_note, rotate_owner_password, disable_view ou recreate_view.
4. Ouvrir avec OWNER et signer
async fn sign_example(message: &[u8]) -> ksp_core_lib::Result<[u8; ksp_wallet_lib::KSPWALLET_SOLANA_SIGNATURE_BYTES]> {
let password = ksp_wallet_lib::OwnerPassword::new(std::string::String::from("OWNER-PASSWORD"));
let opened = ksp_wallet_lib::open_wallet_owner_file_v1("wallets/devnet-main.kspwallet", password).await;
let owner = match opened {
Ok(value) => value,
Err(error) => return Err(error),
};
return owner.sign(message);
}
La signature retournée contient 64 octets Ed25519. Aucun getter public ne retourne la keypair Solana.
5. Administrer alias et notes
async fn metadata_example() -> ksp_core_lib::Result<()> {
let password = ksp_wallet_lib::OwnerPassword::new(std::string::String::from("OWNER-PASSWORD"));
let opened = ksp_wallet_lib::open_wallet_owner_file_v1("wallets/devnet-main.kspwallet", password).await;
let mut owner = match opened {
Ok(value) => value,
Err(error) => return Err(error),
};
let alias_result = owner
.update_alias("wallets/devnet-main.kspwallet", Some(std::string::String::from("primary-devnet")))
.await;
if let Err(error) = alias_result {
return Err(error);
}
let added = owner
.add_note("wallets/devnet-main.kspwallet", std::string::String::from("rotation trimestrielle"))
.await;
let note_id = match added {
Ok(value) => value,
Err(error) => return Err(error),
};
let updated = owner
.update_note(
"wallets/devnet-main.kspwallet",
note_id.as_str(),
std::string::String::from("rotation contrôlée"),
)
.await;
if let Err(error) = updated {
return Err(error);
}
return Ok(());
}
Chaque mutation vérifie que le fichier courant correspond encore à l'état authentifié attendu par le handle. Un handle stale reçoit wallet.state_conflict.
6. Rotations de credentials
OWNER peut changer son password :
let result = owner
.rotate_owner_password(
"wallets/devnet-main.kspwallet",
ksp_wallet_lib::OwnerPassword::new(std::string::String::from("NEW-OWNER-PASSWORD")),
)
.await;
OWNER peut aussi changer le password VIEW sans connaître l'ancien :
let result = owner
.rotate_view_password(
"wallets/devnet-main.kspwallet",
ksp_wallet_lib::ViewPassword::new(std::string::String::from("NEW-VIEW-PASSWORD")),
)
.await;
Ces rotations de credentials ne changent pas la keypair Solana.
7. Révocation forte VIEW
La simple rotation VIEW remplace le credential courant mais ne peut pas retirer des metadata déjà connues d'un ancien détenteur VIEW. OWNER peut effectuer une révocation forte pour les metadata futures :
let disabled = owner.disable_view("wallets/devnet-main.kspwallet").await;
if let Err(error) = disabled {
return Err(error);
}
let recreated = owner
.recreate_view(
"wallets/devnet-main.kspwallet",
ksp_wallet_lib::ViewPassword::new(std::string::String::from("FRESH-VIEW-PASSWORD")),
)
.await;
disable_view rekey les metadata. recreate_view crée ensuite un nouveau slot VIEW autour du nouveau K_metadata. La keypair Solana reste inchangée.
8. Inspecter et importer une keypair externe
Le format doit être choisi explicitement ; Wallet ne fait pas d'auto-détection heuristique.
async fn inspect_transfer_example(source: &[u8]) -> ksp_core_lib::Result<()> {
let inspected = ksp_wallet_lib::inspect_wallet_transfer(
source,
ksp_wallet_lib::WalletTransferFormat::SolanaCliJson,
);
let info = match inspected {
Ok(value) => value,
Err(error) => return Err(error),
};
println!("{}", info.pubkey());
return Ok(());
}
Import fichier vers un nouveau .kspwallet :
let imported = ksp_wallet_lib::import_wallet_transfer_file_v1(
"wallets/imported.kspwallet",
"wallets/legacy-id.json",
ksp_wallet_lib::WalletTransferFormat::SolanaCliJson,
ksp_wallet_lib::OwnerPassword::new(std::string::String::from("OWNER-PASSWORD")),
None,
ksp_wallet_lib::WalletCreateMetadataV1::new(Some(std::string::String::from("imported")), vec![]),
)
.await;
L'import valide la cohérence secret/public, conserve exactement la keypair Solana et génère un nouvel environnement cryptographique KSP. Une destination native existante n'est jamais écrasée.
9. Exporter avec OWNER
En mémoire :
let exported = owner.export_transfer(ksp_wallet_lib::WalletTransferFormat::SolanaKeypairBase58);
Vers un fichier no-clobber :
let exported = owner
.export_transfer_file(
"exports/devnet-main.keypair",
ksp_wallet_lib::WalletTransferFormat::SolanaCliJson,
)
.await;
Les octets retournés par export_transfer contiennent volontairement le secret. Le caller doit limiter leur durée de vie et les zeroize lorsqu'approprié. Sur Unix, l'export fichier tente 0600, qui reste une hygiène filesystem et non une garantie cryptographique.
10. Surfaces in-memory
Les équivalents sans I/O filesystem sont :
create_wallet_v1
open_wallet_view_v1
open_wallet_owner_v1
inspect_locked_wallet_v1
inspect_wallet_transfer
Les handles WalletOwner et WalletView peuvent être sérialisés vers le document natif courant avec to_json_bytes(). Ces bytes restent un .kspwallet chiffré, pas un export de la keypair.
11. Erreurs et diagnostics
Les erreurs Wallet sont des ksp_core_lib::Error avec codes wallet.*. Les erreurs de password restent génériques (owner_unlock_failed, view_unlock_failed) et ne doivent jamais être transformées en oracle détaillant KDF/wrap/ciphertext.
Les Debug des passwords et metadata protégées sont redacted. Les logs Wallet ne doivent contenir ni password, ni keypair, ni payload exporté.
12. Intégration avec Config et Transport
Wallet lui-même ne dépend ni de Config ni de Transport. Une application compose explicitement les couches :
ksp-config-lib
-> fournit chemins/profils applicatifs
ksp-wallet-lib
-> ouvre le wallet et fournit la Pubkey autorisée
ksp-onchain-transport-lib
-> utilise cette Pubkey pour getBalance et autres lectures réseau
Cette composition est le rôle de 0.2.6 — ksp-app-wallet-desk, pas de ksp-wallet-lib.