v0.2.5-pre.010
This commit is contained in:
279
crates/ksp-wallet-lib/USAGE.md
Normal file
279
crates/ksp-wallet-lib/USAGE.md
Normal file
@@ -0,0 +1,279 @@
|
||||
<!-- file: crates/ksp-wallet-lib/USAGE.md -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# 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`](../../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`
|
||||
|
||||
```rust
|
||||
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é
|
||||
|
||||
```rust
|
||||
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
|
||||
|
||||
```rust
|
||||
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
|
||||
|
||||
```rust
|
||||
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
|
||||
|
||||
```rust
|
||||
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 :
|
||||
|
||||
```rust
|
||||
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 :
|
||||
|
||||
```rust
|
||||
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 :
|
||||
|
||||
```rust
|
||||
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.
|
||||
|
||||
```rust
|
||||
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` :
|
||||
|
||||
```rust
|
||||
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 :
|
||||
|
||||
```rust
|
||||
let exported = owner.export_transfer(ksp_wallet_lib::WalletTransferFormat::SolanaKeypairBase58);
|
||||
```
|
||||
|
||||
Vers un fichier no-clobber :
|
||||
|
||||
```rust
|
||||
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 :
|
||||
|
||||
```text
|
||||
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 :
|
||||
|
||||
```text
|
||||
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`.
|
||||
Reference in New Issue
Block a user