commit 36ac0acccc2ad5c840b52146209479e6a49a761f
parent 4aaee761afcf03c633ba7a6a071c1126bf4b1b62
Author: triesap <tyson@radroots.org>
Date: Mon, 3 Aug 2026 22:35:47 +0000
domain: adopt validated account aggregates
- replace legacy signer flags with canonical identity and local binding aggregates
- verify persisted public identity and reject unsupported signer records
- update application snapshots, repositories, transitions, and DTO projection
- migrate domain and integration coverage to typed binding availability
Diffstat:
12 files changed, 149 insertions(+), 214 deletions(-)
diff --git a/core/crates/application/src/accounts.rs b/core/crates/application/src/accounts.rs
@@ -1,8 +1,8 @@
use std::sync::{Mutex, MutexGuard};
use radroots_studio_domain::{
- AccountCreatedAt, AccountSummary, KeyAvailability, Nsec, PublicKey, SafeError, SafeErrorCode,
- SafeMessage, SecretKeyInput, SignerKind,
+ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding,
+ Nsec, PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput,
};
use radroots_studio_nostr::{generate_local_keypair, import_secret};
@@ -96,7 +96,8 @@ impl AppCore {
Ok(()) => {}
Err(error)
if error.code() == SafeErrorCode::CredentialMissing
- && account.key_availability() == KeyAvailability::CredentialMissing => {}
+ && account.signer().availability()
+ == BindingAvailability::CredentialMissing => {}
Err(error) => return Err(error),
}
journal.update_operation(
@@ -154,14 +155,12 @@ impl AppCore {
let generated = generate_local_keypair()?;
let (public_key, npub, secret, nsec) = generated.into_parts();
let account = AccountSummary::new(
- public_key,
- npub,
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::verify(public_key, npub.as_str().to_owned())?,
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
None,
AccountCreatedAt::new(clock.now()),
None,
- );
+ )?;
Self::persist_account_transaction(
AccountOperationKind::Add,
&account,
@@ -201,12 +200,12 @@ impl AppCore {
let imported = import_secret(input)?;
let (public_key, npub, secret) = imported.into_parts();
if let Some(existing) = accounts.find_account(public_key)? {
- if existing.key_availability() != KeyAvailability::CredentialMissing
+ if existing.signer().availability() != BindingAvailability::CredentialMissing
|| secrets.contains(public_key)?
{
return Err(account_exists());
}
- let repaired = existing.with_key_availability(KeyAvailability::Available);
+ let repaired = existing.with_binding_availability(BindingAvailability::Available);
Self::persist_account_transaction(
AccountOperationKind::Import,
&repaired,
@@ -228,14 +227,12 @@ impl AppCore {
return Err(account_exists());
}
let account = AccountSummary::new(
- public_key,
- npub,
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::verify(public_key, npub.as_str().to_owned())?,
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
None,
AccountCreatedAt::new(clock.now()),
None,
- );
+ )?;
Self::persist_account_transaction(
AccountOperationKind::Import,
&account,
@@ -566,8 +563,8 @@ mod tests {
use std::sync::atomic::{AtomicBool, Ordering};
use radroots_studio_domain::{
- AccountCreatedAt, AccountSummary, KeyAvailability, PublicKey, SafeError, SafeErrorCode,
- SafeMessage, SecretKeyInput, SignerKind, UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding,
+ PublicKey, SafeError, SafeErrorCode, SafeMessage, SecretKeyInput, UnixTimestamp,
};
use super::InMemoryAccountRepository;
@@ -795,14 +792,13 @@ mod tests {
let imported = radroots_studio_nostr::import_secret(input()).expect("derive");
let (public_key, npub, _) = imported.into_parts();
let missing = AccountSummary::new(
- public_key,
- npub,
- SignerKind::LocalSecret,
- KeyAvailability::CredentialMissing,
+ AccountIdentity::verify(public_key, npub.as_str().to_owned()).expect("identity"),
+ LocalSignerBinding::new(public_key, BindingAvailability::CredentialMissing),
None,
AccountCreatedAt::new(FixedClock.now()),
None,
- );
+ )
+ .expect("missing account");
accounts.insert_account(&missing).expect("missing metadata");
accounts
.save_selected_account(Some(public_key))
@@ -824,8 +820,8 @@ mod tests {
)
.expect("repair");
assert_eq!(
- receipt.account().key_availability(),
- KeyAvailability::Available
+ receipt.account().signer().availability(),
+ BindingAvailability::Available
);
assert!(secrets.contains(public_key).expect("credential"));
assert_eq!(core.snapshot().accounts().len(), 1);
diff --git a/core/crates/application/src/snapshot.rs b/core/crates/application/src/snapshot.rs
@@ -278,8 +278,8 @@ const fn invalid_snapshot() -> SafeError {
#[cfg(test)]
mod tests {
use radroots_studio_domain::{
- AccountCreatedAt, AccountSummary, KeyAvailability, Npub, PublicKey, SignerKind,
- UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding,
+ PublicKey, UnixTimestamp,
};
use super::{
@@ -287,18 +287,16 @@ mod tests {
RelayConnectionState, SessionState, SnapshotRevision,
};
- const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
-
fn account(key_byte: u8) -> AccountSummary {
+ let public_key = PublicKey::from_bytes([key_byte; 32]);
AccountSummary::new(
- PublicKey::from_bytes([key_byte; 32]),
- Npub::from_encoded(NPUB.to_owned()).expect("valid npub"),
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::derive(public_key).expect("identity"),
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
None,
AccountCreatedAt::new(UnixTimestamp::from_seconds(1).expect("valid time")),
None,
)
+ .expect("account")
}
#[test]
diff --git a/core/crates/application/src/state_machine.rs b/core/crates/application/src/state_machine.rs
@@ -357,8 +357,8 @@ const fn account_not_found() -> SafeError {
#[cfg(test)]
mod tests {
use radroots_studio_domain::{
- AccountCreatedAt, AccountSummary, KeyAvailability, Npub, PublicKey, SafeError,
- SafeErrorCode, SafeMessage, SignerKind, UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding,
+ PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp,
};
use crate::{
@@ -366,18 +366,16 @@ mod tests {
SessionState, StateMachine, StateTransition,
};
- const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
-
fn account(key_byte: u8) -> AccountSummary {
+ let public_key = PublicKey::from_bytes([key_byte; 32]);
AccountSummary::new(
- PublicKey::from_bytes([key_byte; 32]),
- Npub::from_encoded(NPUB.to_owned()).expect("valid npub"),
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::derive(public_key).expect("identity"),
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
None,
AccountCreatedAt::new(UnixTimestamp::from_seconds(1).expect("valid time")),
None,
)
+ .expect("account")
}
fn active(account: AccountSummary) -> ActiveAccountSnapshot {
diff --git a/core/crates/application/tests/redaction.rs b/core/crates/application/tests/redaction.rs
@@ -2,14 +2,12 @@ use radroots_studio_application::{
AppSnapshot, RelayConfiguration, SessionState, SnapshotRevision,
};
use radroots_studio_domain::{
- AccountCreatedAt, AccountSummary, KeyAvailability, Npub, PublicKey, SafeError, SafeErrorCode,
- SafeMessage, SignerKind, UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding,
+ PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp,
};
const SECRET_HEX: &str = "1111111111111111111111111111111111111111111111111111111111111111";
const SECRET_NSEC: &str = "nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5";
-const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
-
fn assert_redacted(text: &str) {
assert!(!text.contains(SECRET_HEX));
assert!(!text.contains(SECRET_NSEC));
@@ -19,14 +17,16 @@ fn assert_redacted(text: &str) {
#[test]
fn redaction_guards_public_snapshot_and_safe_error_debug() {
let account = AccountSummary::new(
- PublicKey::from_bytes([2; 32]),
- Npub::from_encoded(NPUB.to_owned()).expect("npub"),
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::derive(PublicKey::from_bytes([2; 32])).expect("identity"),
+ LocalSignerBinding::new(
+ PublicKey::from_bytes([2; 32]),
+ BindingAvailability::Available,
+ ),
None,
AccountCreatedAt::new(UnixTimestamp::from_seconds(1).expect("time")),
None,
- );
+ )
+ .expect("account");
let snapshot = AppSnapshot::ready(
SnapshotRevision::from_value(1),
RelayConfiguration::default(),
diff --git a/core/crates/domain/src/account.rs b/core/crates/domain/src/account.rs
@@ -154,21 +154,6 @@ pub enum BindingRepairAction {
RetryCredentialStore,
}
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum SignerKind {
- LocalSecret,
- WatchOnly,
- RemoteNip46,
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum KeyAvailability {
- Available,
- CredentialMissing,
- StoreUnavailable,
- NotRequired,
-}
-
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AccountLabel(String);
@@ -213,55 +198,52 @@ impl AccountCreatedAt {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AccountSummary {
- public_key: PublicKey,
- npub: Npub,
- signer_kind: SignerKind,
- key_availability: KeyAvailability,
+ identity: AccountIdentity,
+ signer: LocalSignerBinding,
label: Option<AccountLabel>,
created_at: AccountCreatedAt,
last_used_at: Option<UnixTimestamp>,
}
impl AccountSummary {
- #[must_use]
- pub const fn new(
- public_key: PublicKey,
- npub: Npub,
- signer_kind: SignerKind,
- key_availability: KeyAvailability,
+ /// Creates an account summary whose identity and signer binding refer to the same account.
+ ///
+ /// # Errors
+ ///
+ /// Returns an invalid-account-metadata error when the signer binding belongs to a different
+ /// public key.
+ pub fn new(
+ identity: AccountIdentity,
+ signer: LocalSignerBinding,
label: Option<AccountLabel>,
created_at: AccountCreatedAt,
last_used_at: Option<UnixTimestamp>,
- ) -> Self {
- Self {
- public_key,
- npub,
- signer_kind,
- key_availability,
+ ) -> Result<Self, SafeError> {
+ if identity.public_key() != signer.account() {
+ return Err(invalid_account_metadata());
+ }
+ Ok(Self {
+ identity,
+ signer,
label,
created_at,
last_used_at,
- }
+ })
}
#[must_use]
pub const fn public_key(&self) -> PublicKey {
- self.public_key
+ self.identity.public_key()
}
#[must_use]
pub fn npub(&self) -> &Npub {
- &self.npub
+ self.identity.npub()
}
#[must_use]
- pub const fn signer_kind(&self) -> SignerKind {
- self.signer_kind
- }
-
- #[must_use]
- pub const fn key_availability(&self) -> KeyAvailability {
- self.key_availability
+ pub const fn signer(&self) -> LocalSignerBinding {
+ self.signer
}
#[must_use]
@@ -280,12 +262,10 @@ impl AccountSummary {
}
#[must_use]
- pub fn with_key_availability(&self, key_availability: KeyAvailability) -> Self {
+ pub fn with_binding_availability(&self, availability: BindingAvailability) -> Self {
Self {
- public_key: self.public_key,
- npub: self.npub.clone(),
- signer_kind: self.signer_kind,
- key_availability,
+ identity: self.identity.clone(),
+ signer: LocalSignerBinding::new(self.public_key(), availability),
label: self.label.clone(),
created_at: self.created_at,
last_used_at: self.last_used_at,
@@ -295,10 +275,8 @@ impl AccountSummary {
#[must_use]
pub fn with_last_used_at(&self, last_used_at: UnixTimestamp) -> Self {
Self {
- public_key: self.public_key,
- npub: self.npub.clone(),
- signer_kind: self.signer_kind,
- key_availability: self.key_availability,
+ identity: self.identity.clone(),
+ signer: self.signer,
label: self.label.clone(),
created_at: self.created_at,
last_used_at: Some(last_used_at),
@@ -309,7 +287,7 @@ impl AccountSummary {
pub fn display_label(&self) -> String {
self.label
.as_ref()
- .map_or_else(|| self.npub.short(), |label| label.as_str().to_owned())
+ .map_or_else(|| self.npub().short(), |label| label.as_str().to_owned())
}
}
@@ -322,27 +300,27 @@ const fn invalid_account_metadata() -> SafeError {
#[cfg(test)]
mod tests {
+ use crate::PublicKey;
use crate::time::UnixTimestamp;
- use crate::{Npub, PublicKey};
use super::{
AccountCreatedAt, AccountIdentity, AccountLabel, AccountSummary, BindingAvailability,
- BindingRepairAction, KeyAvailability, LocalSignerBinding, SignerKind,
+ BindingRepairAction, LocalSignerBinding,
};
- const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
const DERIVED_NPUB: &str = "npub1qurswpc8qurswpc8qurswpc8qurswpc8qurswpc8qurswpc8qursnvjvl7";
+ const MISMATCHED_NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
fn account(label: Option<AccountLabel>) -> AccountSummary {
+ let public_key = PublicKey::from_bytes([7_u8; 32]);
AccountSummary::new(
- PublicKey::from_bytes([7_u8; 32]),
- Npub::from_encoded(NPUB.to_owned()).expect("valid npub"),
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::derive(public_key).expect("identity"),
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
label,
AccountCreatedAt::new(UnixTimestamp::from_seconds(10).expect("valid time")),
None,
)
+ .expect("account")
}
#[test]
@@ -361,7 +339,7 @@ mod tests {
let unlabelled = account(None);
assert_eq!(labelled.display_label(), "Farm");
- assert_eq!(unlabelled.display_label(), "npub10elfcs4…8qzvjptg");
+ assert_eq!(unlabelled.display_label(), "npub1qurswpc…rsnvjvl7");
}
#[test]
@@ -369,13 +347,15 @@ mod tests {
let account = account(None);
let debug = format!("{account:?}");
- assert_eq!(account.signer_kind(), SignerKind::LocalSecret);
- assert_eq!(account.key_availability(), KeyAvailability::Available);
+ assert_eq!(
+ account.signer().availability(),
+ BindingAvailability::Available
+ );
assert!(account.label().is_none());
assert!(account.last_used_at().is_none());
assert_eq!(account.created_at().timestamp().as_seconds(), 10);
assert_eq!(account.public_key(), PublicKey::from_bytes([7_u8; 32]));
- assert_eq!(account.npub().as_str(), NPUB);
+ assert_eq!(account.npub().as_str(), DERIVED_NPUB);
assert!(!debug.contains("nsec1"));
assert!(!debug.contains(&"11".repeat(32)));
}
@@ -390,9 +370,13 @@ mod tests {
AccountIdentity::verify(public_key, DERIVED_NPUB.to_owned()).expect("verified"),
identity
);
- assert!(AccountIdentity::verify(public_key, NPUB.to_owned()).is_err());
+ assert!(AccountIdentity::verify(public_key, MISMATCHED_NPUB.to_owned()).is_err());
assert!(
- AccountIdentity::verify(PublicKey::from_bytes([8_u8; 32]), NPUB.to_owned()).is_err()
+ AccountIdentity::verify(
+ PublicKey::from_bytes([8_u8; 32]),
+ MISMATCHED_NPUB.to_owned()
+ )
+ .is_err()
);
}
diff --git a/core/crates/domain/src/lib.rs b/core/crates/domain/src/lib.rs
@@ -9,7 +9,7 @@ pub mod time;
pub use account::{
AccountCreatedAt, AccountIdentity, AccountLabel, AccountSummary, BindingAvailability,
- BindingRepairAction, KeyAvailability, LocalSignerBinding, SignerKind,
+ BindingRepairAction, LocalSignerBinding,
};
pub use error::{SafeError, SafeErrorCode, SafeMessage};
pub use key::{Npub, Nsec, PublicKey, SecretKeyInput, SecretKeyInputKind};
diff --git a/core/crates/ffi/src/dto.rs b/core/crates/ffi/src/dto.rs
@@ -2,9 +2,7 @@ use radroots_studio_application::{
ActiveAccountSnapshot, AppLifecycle, AppSnapshot, ProfileLoadState, RelayConnectionState,
SessionState,
};
-use radroots_studio_domain::{
- AccountSummary, KeyAvailability, ProfileMetadata, SafeError, SignerKind,
-};
+use radroots_studio_domain::{AccountSummary, BindingAvailability, ProfileMetadata, SafeError};
#[derive(Clone, Debug, Eq, PartialEq, uniffi::Record)]
pub struct SafeErrorDto {
@@ -149,8 +147,8 @@ impl From<&AccountSummary> for AccountDto {
public_key_hex: account.public_key().to_hex(),
npub: account.npub().as_str().to_owned(),
display_label: account.display_label(),
- signer_kind: account.signer_kind().into(),
- key_availability: account.key_availability().into(),
+ signer_kind: SignerKindDto::LocalSecret,
+ key_availability: account.signer().availability().into(),
created_at_seconds: account.created_at().timestamp().as_seconds(),
last_used_at_seconds: account
.last_used_at()
@@ -191,23 +189,12 @@ impl From<SafeError> for SafeErrorDto {
}
}
-impl From<SignerKind> for SignerKindDto {
- fn from(value: SignerKind) -> Self {
- match value {
- SignerKind::LocalSecret => Self::LocalSecret,
- SignerKind::WatchOnly => Self::WatchOnly,
- SignerKind::RemoteNip46 => Self::RemoteNip46,
- }
- }
-}
-
-impl From<KeyAvailability> for KeyAvailabilityDto {
- fn from(value: KeyAvailability) -> Self {
+impl From<BindingAvailability> for KeyAvailabilityDto {
+ fn from(value: BindingAvailability) -> Self {
match value {
- KeyAvailability::Available => Self::Available,
- KeyAvailability::CredentialMissing => Self::CredentialMissing,
- KeyAvailability::StoreUnavailable => Self::StoreUnavailable,
- KeyAvailability::NotRequired => Self::NotRequired,
+ BindingAvailability::Available => Self::Available,
+ BindingAvailability::CredentialMissing => Self::CredentialMissing,
+ BindingAvailability::StoreUnavailable => Self::StoreUnavailable,
}
}
}
diff --git a/core/crates/storage/src/account_namespace.rs b/core/crates/storage/src/account_namespace.rs
@@ -80,24 +80,22 @@ mod tests {
AccountNamespaceRepository, AccountPreferenceKey, AccountRepository, AppStateRepository,
};
use radroots_studio_domain::{
- AccountCreatedAt, AccountSummary, KeyAvailability, Npub, PublicKey, SignerKind,
- UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding,
+ PublicKey, UnixTimestamp,
};
use crate::Database;
- const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
-
fn account(byte: u8) -> AccountSummary {
+ let public_key = PublicKey::from_bytes([byte; 32]);
AccountSummary::new(
- PublicKey::from_bytes([byte; 32]),
- Npub::from_encoded(NPUB.to_owned()).expect("npub"),
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::derive(public_key).expect("identity"),
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
None,
AccountCreatedAt::new(UnixTimestamp::from_seconds(i64::from(byte)).expect("time")),
None,
)
+ .expect("account")
}
#[test]
diff --git a/core/crates/storage/src/accounts.rs b/core/crates/storage/src/accounts.rs
@@ -1,7 +1,7 @@
use radroots_studio_application::{AccountRepository, AppStateRepository};
use radroots_studio_domain::{
- AccountCreatedAt, AccountLabel, AccountSummary, KeyAvailability, Npub, PublicKey, SafeError,
- SafeErrorCode, SafeMessage, SignerKind, UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountLabel, AccountSummary, BindingAvailability,
+ LocalSignerBinding, PublicKey, SafeError, SafeErrorCode, SafeMessage, UnixTimestamp,
};
use rusqlite::{OptionalExtension, Row, params};
@@ -135,8 +135,8 @@ impl From<&AccountSummary> for EncodedAccount {
Self {
public_key: account.public_key().to_hex(),
npub: account.npub().as_str().to_owned(),
- signer_kind: encode_signer_kind(account.signer_kind()),
- key_availability: encode_key_availability(account.key_availability()),
+ signer_kind: "local_secret",
+ key_availability: encode_key_availability(account.signer().availability()),
label: account.label().map(|label| label.as_str().to_owned()),
created_at: account.created_at().timestamp().as_seconds(),
last_used_at: account.last_used_at().map(UnixTimestamp::as_seconds),
@@ -147,8 +147,10 @@ impl From<&AccountSummary> for EncodedAccount {
fn decode_account(row: &Row<'_>) -> rusqlite::Result<AccountSummary> {
let public_key =
PublicKey::from_hex(row.get::<_, String>(0)?.as_str()).map_err(|_| invalid_column(0))?;
- let npub = Npub::from_encoded(row.get(1)?).map_err(|_| invalid_column(1))?;
- let signer_kind = decode_signer_kind(row.get::<_, String>(2)?.as_str())?;
+ let npub: String = row.get(1)?;
+ if row.get::<_, String>(2)?.as_str() != "local_secret" {
+ return Err(invalid_column(2));
+ }
let key_availability = decode_key_availability(row.get::<_, String>(3)?.as_str())?;
let label = row
.get::<_, Option<String>>(4)?
@@ -160,49 +162,29 @@ fn decode_account(row: &Row<'_>) -> rusqlite::Result<AccountSummary> {
.map(|value| UnixTimestamp::from_seconds(value).ok_or_else(|| invalid_column(6)))
.transpose()?;
- Ok(AccountSummary::new(
- public_key,
- npub,
- signer_kind,
- key_availability,
+ AccountSummary::new(
+ AccountIdentity::verify(public_key, npub).map_err(|_| invalid_column(1))?,
+ LocalSignerBinding::new(public_key, key_availability),
label,
AccountCreatedAt::new(created_at),
last_used_at,
- ))
-}
-
-const fn encode_signer_kind(value: SignerKind) -> &'static str {
- match value {
- SignerKind::LocalSecret => "local_secret",
- SignerKind::WatchOnly => "watch_only",
- SignerKind::RemoteNip46 => "remote_nip46",
- }
-}
-
-fn decode_signer_kind(value: &str) -> rusqlite::Result<SignerKind> {
- match value {
- "local_secret" => Ok(SignerKind::LocalSecret),
- "watch_only" => Ok(SignerKind::WatchOnly),
- "remote_nip46" => Ok(SignerKind::RemoteNip46),
- _ => Err(invalid_column(2)),
- }
+ )
+ .map_err(|_| invalid_column(0))
}
-const fn encode_key_availability(value: KeyAvailability) -> &'static str {
+const fn encode_key_availability(value: BindingAvailability) -> &'static str {
match value {
- KeyAvailability::Available => "available",
- KeyAvailability::CredentialMissing => "credential_missing",
- KeyAvailability::StoreUnavailable => "store_unavailable",
- KeyAvailability::NotRequired => "not_required",
+ BindingAvailability::Available => "available",
+ BindingAvailability::CredentialMissing => "credential_missing",
+ BindingAvailability::StoreUnavailable => "store_unavailable",
}
}
-fn decode_key_availability(value: &str) -> rusqlite::Result<KeyAvailability> {
+fn decode_key_availability(value: &str) -> rusqlite::Result<BindingAvailability> {
match value {
- "available" => Ok(KeyAvailability::Available),
- "credential_missing" => Ok(KeyAvailability::CredentialMissing),
- "store_unavailable" => Ok(KeyAvailability::StoreUnavailable),
- "not_required" => Ok(KeyAvailability::NotRequired),
+ "available" => Ok(BindingAvailability::Available),
+ "credential_missing" => Ok(BindingAvailability::CredentialMissing),
+ "store_unavailable" => Ok(BindingAvailability::StoreUnavailable),
_ => Err(invalid_column(3)),
}
}
@@ -262,27 +244,25 @@ mod tests {
use radroots_studio_application::{AccountRepository, AppStateRepository};
use radroots_studio_domain::{
- AccountCreatedAt, AccountLabel, AccountSummary, KeyAvailability, Npub, PublicKey,
- SafeErrorCode, SignerKind, UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountLabel, AccountSummary, BindingAvailability,
+ LocalSignerBinding, PublicKey, SafeErrorCode, UnixTimestamp,
};
use tempfile::tempdir;
use crate::Database;
- const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
-
fn account(key_byte: u8, created_at: i64) -> AccountSummary {
+ let public_key = PublicKey::from_bytes([key_byte; 32]);
AccountSummary::new(
- PublicKey::from_bytes([key_byte; 32]),
- Npub::from_encoded(NPUB.to_owned()).expect("valid npub"),
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::derive(public_key).expect("identity"),
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
Some(AccountLabel::parse("Farm account").expect("valid label")),
AccountCreatedAt::new(
UnixTimestamp::from_seconds(created_at).expect("valid timestamp"),
),
None,
)
+ .expect("account")
}
#[test]
diff --git a/core/crates/storage/src/application_adapter.rs b/core/crates/storage/src/application_adapter.rs
@@ -193,25 +193,23 @@ mod tests {
RelayConfiguration, SecretStore, SecretStoreOperation, SessionState,
};
use radroots_studio_domain::{
- AccountCreatedAt, AccountSummary, KeyAvailability, Npub, PublicKey, SafeErrorCode,
- SecretKeyInput, SignerKind, UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding,
+ PublicKey, SafeErrorCode, SecretKeyInput, UnixTimestamp,
};
use tempfile::tempdir;
use super::PersistentAppCore;
- const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
-
fn account() -> AccountSummary {
+ let public_key = PublicKey::from_bytes([4; 32]);
AccountSummary::new(
- PublicKey::from_bytes([4; 32]),
- Npub::from_encoded(NPUB.to_owned()).expect("npub"),
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::derive(public_key).expect("identity"),
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
None,
AccountCreatedAt::new(UnixTimestamp::from_seconds(1).expect("time")),
None,
)
+ .expect("account")
}
struct FixedClock;
diff --git a/core/crates/storage/src/profiles.rs b/core/crates/storage/src/profiles.rs
@@ -150,24 +150,21 @@ mod tests {
AccountRepository, CachedProfile, ProfileRefreshStatus, ProfileRepository,
};
use radroots_studio_domain::{
- AccountCreatedAt, AccountSummary, EventId, KeyAvailability, Kind0ProfileCandidate, Npub,
- ProfileMetadata, PublicKey, SignerKind, UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, EventId,
+ Kind0ProfileCandidate, LocalSignerBinding, ProfileMetadata, PublicKey, UnixTimestamp,
};
use crate::Database;
- const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
-
fn account(public_key: PublicKey) -> AccountSummary {
AccountSummary::new(
- public_key,
- Npub::from_encoded(NPUB.to_owned()).expect("npub"),
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::derive(public_key).expect("identity"),
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
None,
AccountCreatedAt::new(UnixTimestamp::from_seconds(1).expect("time")),
None,
)
+ .expect("account")
}
fn profile(public_key: PublicKey, id: u8, created_at: i64, name: &str) -> CachedProfile {
diff --git a/core/crates/storage/tests/redaction.rs b/core/crates/storage/tests/redaction.rs
@@ -2,15 +2,14 @@ use std::fs;
use radroots_studio_application::{AccountOperationKind, AccountRepository, OperationJournal};
use radroots_studio_domain::{
- AccountCreatedAt, AccountSummary, KeyAvailability, Npub, PublicKey, SignerKind, UnixTimestamp,
+ AccountCreatedAt, AccountIdentity, AccountSummary, BindingAvailability, LocalSignerBinding,
+ PublicKey, UnixTimestamp,
};
use radroots_studio_storage::Database;
use tempfile::tempdir;
const SECRET_HEX: &str = "1111111111111111111111111111111111111111111111111111111111111111";
const SECRET_NSEC: &str = "nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5";
-const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
-
fn assert_redacted(bytes: &[u8]) {
assert!(
!bytes
@@ -31,15 +30,15 @@ fn redaction_guards_sqlite_schema_and_non_secret_records() {
let path = directory.path().join("studio.sqlite3");
{
let database = Database::open(&path).expect("database");
+ let public_key = PublicKey::from_bytes([2; 32]);
let account = AccountSummary::new(
- PublicKey::from_bytes([2; 32]),
- Npub::from_encoded(NPUB.to_owned()).expect("npub"),
- SignerKind::LocalSecret,
- KeyAvailability::Available,
+ AccountIdentity::derive(public_key).expect("identity"),
+ LocalSignerBinding::new(public_key, BindingAvailability::Available),
None,
AccountCreatedAt::new(UnixTimestamp::from_seconds(1).expect("time")),
None,
- );
+ )
+ .expect("account");
database.insert_account(&account).expect("account");
database
.begin_operation(