Skip to content

Commit 2140de6

Browse files
feat(key-wallet): add DIP-13 identity authentication accounts (ECDSA + BLS)
Add two new `AccountType` variants for DIP-13 sub-feature 0' (per-identity signing keys the user employs to sign Dash Platform state transitions): - `IdentityAuthenticationEcdsa { identity_index }` — key_type 0', backed by a regular `Account` (secp256k1). - `IdentityAuthenticationBls { identity_index }` — key_type 1', backed by `BLSAccount`, gated on `#[cfg(feature = "bls")]`. Both account types use the DIP-13 derivation path `m/9'/coin_type'/5'/0'/key_type'/identity_index'` with hardened children for individual keys (`.../identity_index'/key_index'`). Address pools use `AbsentHardened` since DIP-13 mandates hardened leaves. ### Wiring - `AccountCollection` gains `identity_authentication_ecdsa: BTreeMap<u32, Account>` and (under `bls`) `identity_authentication_bls: BTreeMap<u32, BLSAccount>`, keyed by `identity_index`. All collection methods (`new`, `insert`, `insert_bls_account`, `contains_account_type`, `account_of_type[_mut]`, `bls_account_of_type[_mut]`, `all_accounts[_mut]`, `count`, `is_empty`, `clear`) are updated. - `ManagedAccountCollection`, `ManagedAccountType`, `CoreAccountTypeMatch` mirror the new variants and are routed through the usual matchers. - `AccountTypeToCheck::IdentityAuthentication{Ecdsa,Bls}` variants are added so conversions from `ManagedAccountType`/`AccountType` stay total. Identity authentication accounts are **Platform-only**: they are deliberately absent from every `TransactionType` relevance set (`TransactionRouter::get_relevant_account_types`), and the `ManagedAccountCollection::check_account_type` arms return empty results. Address matching in `ManagedCoreAccount::check_transaction_for_match` returns `None` for these variants for the same reason. - `Wallet::add_bls_account` now accepts `IdentityAuthenticationBls` in addition to `ProviderOperatorKeys`. - Two new DIP-9 `IndexConstPath<5>` constants per network (`IDENTITY_AUTHENTICATION_{ECDSA,BLS}_PATH_{MAINNET,TESTNET}`) and the matching `DerivationPathReference::BlockchainIdentityAuthentication{Ecdsa,Bls}` variants. - `asset_lock_builder::resolve_funding_account` is intentionally left untouched — identity authentication accounts do not fund asset locks. - `WalletAccountCreationOptions` is unchanged. Identity authentication accounts are per-identity and come into existence when the user registers a Platform identity, not at wallet creation. Callers insert them post-hoc via `Wallet::add_account` (ECDSA) or `Wallet::add_bls_account` (BLS). ### FFI `FFIAccountType` gains `IdentityAuthenticationEcdsa = 16` and `IdentityAuthenticationBls = 17`; `to_account_type` / `from_account_type` route the `index` parameter as `identity_index`. `FFIAccountMatch` emission for `CoreAccountTypeMatch::IdentityAuthentication*` reports the identity index in `account_index` (these variants are never produced by the L1 transaction router, but the FFI matcher stays exhaustive). ### Tests New `identity_authentication_tests` module in `account_type.rs` covers: ECDSA and BLS mainnet/testnet/regtest path derivation, `index()` / `derivation_path_reference()` / `AccountTypeToCheck` round-trip, and end-to-end insert / `contains_account_type` / `account_of_type` / `bls_account_of_type` round-trips through `AccountCollection`. BLS tests are `#[cfg(feature = "bls")]`-gated. Existing `test_wrong_account_type_for_bls` message was updated for the broadened `insert_bls_account` validation. ### Serialization compatibility Adding enum variants is forward-incompatible for `bincode::Encode`/ `Decode` — wallet blobs serialized by earlier v0.42-dev builds will fail to decode after this change. This is acceptable given the unstable 0.x API per `CLAUDE.md`. Serde uses its default (externally tagged) representation, so new readers still decode old data identically and old readers will error cleanly on new variants they cannot name. Verified: `cargo build -p key-wallet --all-features`, `cargo test -p key-wallet --lib --all-features`, `cargo clippy -p key-wallet --all-features --all-targets -- -D warnings`, `cargo fmt -p key-wallet --check`, and downstream `key-wallet-ffi` / `key-wallet-manager` builds and lib tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ee1ebd9 commit 2140de6

17 files changed

Lines changed: 1053 additions & 19 deletions

File tree

key-wallet-ffi/src/address_pool.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ fn get_managed_account_by_type<'a>(
4545
collection.identity_topup_not_bound.as_ref()
4646
}
4747
AccountType::IdentityInvitation => collection.identity_invitation.as_ref(),
48+
AccountType::IdentityAuthenticationEcdsa {
49+
identity_index,
50+
} => collection.identity_authentication_ecdsa.get(identity_index),
51+
#[cfg(feature = "bls")]
52+
AccountType::IdentityAuthenticationBls {
53+
identity_index,
54+
} => collection.identity_authentication_bls.get(identity_index),
4855
AccountType::AssetLockAddressTopUp => collection.asset_lock_address_topup.as_ref(),
4956
AccountType::AssetLockShieldedAddressTopUp => {
5057
collection.asset_lock_shielded_address_topup.as_ref()
@@ -98,6 +105,13 @@ fn get_managed_account_by_type_mut<'a>(
98105
collection.identity_topup_not_bound.as_mut()
99106
}
100107
AccountType::IdentityInvitation => collection.identity_invitation.as_mut(),
108+
AccountType::IdentityAuthenticationEcdsa {
109+
identity_index,
110+
} => collection.identity_authentication_ecdsa.get_mut(identity_index),
111+
#[cfg(feature = "bls")]
112+
AccountType::IdentityAuthenticationBls {
113+
identity_index,
114+
} => collection.identity_authentication_bls.get_mut(identity_index),
101115
AccountType::AssetLockAddressTopUp => collection.asset_lock_address_topup.as_mut(),
102116
AccountType::AssetLockShieldedAddressTopUp => {
103117
collection.asset_lock_shielded_address_topup.as_mut()

key-wallet-ffi/src/managed_account.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,13 @@ pub unsafe extern "C" fn managed_wallet_get_account(
247247
managed_collection.identity_topup_not_bound.as_ref()
248248
}
249249
AccountType::IdentityInvitation => managed_collection.identity_invitation.as_ref(),
250+
AccountType::IdentityAuthenticationEcdsa {
251+
identity_index,
252+
} => managed_collection.identity_authentication_ecdsa.get(&identity_index),
253+
#[cfg(feature = "bls")]
254+
AccountType::IdentityAuthenticationBls {
255+
identity_index,
256+
} => managed_collection.identity_authentication_bls.get(&identity_index),
250257
AccountType::AssetLockAddressTopUp => {
251258
managed_collection.asset_lock_address_topup.as_ref()
252259
}
@@ -564,6 +571,13 @@ pub unsafe extern "C" fn managed_core_account_get_account_type(
564571
FFIAccountType::IdentityTopUpNotBoundToIdentity
565572
}
566573
AccountType::IdentityInvitation => FFIAccountType::IdentityInvitation,
574+
AccountType::IdentityAuthenticationEcdsa {
575+
..
576+
} => FFIAccountType::IdentityAuthenticationEcdsa,
577+
#[cfg(feature = "bls")]
578+
AccountType::IdentityAuthenticationBls {
579+
..
580+
} => FFIAccountType::IdentityAuthenticationBls,
567581
AccountType::AssetLockAddressTopUp => FFIAccountType::AssetLockAddressTopUp,
568582
AccountType::AssetLockShieldedAddressTopUp => FFIAccountType::AssetLockShieldedAddressTopUp,
569583
AccountType::ProviderVotingKeys => FFIAccountType::ProviderVotingKeys,
@@ -1167,6 +1181,15 @@ pub unsafe extern "C" fn managed_core_account_get_address_pool(
11671181
addresses,
11681182
..
11691183
} => addresses,
1184+
ManagedAccountType::IdentityAuthenticationEcdsa {
1185+
addresses,
1186+
..
1187+
} => addresses,
1188+
#[cfg(feature = "bls")]
1189+
ManagedAccountType::IdentityAuthenticationBls {
1190+
addresses,
1191+
..
1192+
} => addresses,
11701193
};
11711194

11721195
let ffi_pool = FFIAddressPool {

key-wallet-ffi/src/transaction_checking.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use std::slice;
1111
use crate::error::{FFIError, FFIErrorCode};
1212
use crate::managed_wallet::{managed_wallet_info_free, FFIManagedWalletInfo};
1313
use crate::types::{
14-
transaction_context_from_ffi, FFIBlockInfo, FFITransactionContextType, FFIWallet,
14+
transaction_context_from_ffi, FFIAccountType, FFIBlockInfo, FFITransactionContextType,
15+
FFIWallet,
1516
};
1617
use dashcore::consensus::Decodable;
1718
use dashcore::Transaction;
@@ -473,6 +474,49 @@ pub unsafe extern "C" fn managed_wallet_check_transaction(
473474
ffi_accounts.push(ffi_match);
474475
continue;
475476
}
477+
// DIP-13 identity-authentication matches are Platform-only and
478+
// therefore never produced by the L1 transaction router. We
479+
// still need exhaustive handling here in case a caller
480+
// constructs the variant directly; in that case we emit an
481+
// FFI record using the `IdentityAuthenticationEcdsa` /
482+
// `IdentityAuthenticationBls` FFI enum values (16 / 17) with
483+
// the identity index reported in `account_index`.
484+
CoreAccountTypeMatch::IdentityAuthenticationEcdsa {
485+
identity_index,
486+
involved_addresses,
487+
} => {
488+
let ffi_match = FFIAccountMatch {
489+
account_type: FFIAccountType::IdentityAuthenticationEcdsa as u32,
490+
account_index: *identity_index,
491+
registration_index: 0,
492+
received: account_match.received,
493+
sent: account_match.sent,
494+
external_addresses_count: involved_addresses.len() as c_uint,
495+
internal_addresses_count: 0,
496+
has_external_addresses: !involved_addresses.is_empty(),
497+
has_internal_addresses: false,
498+
};
499+
ffi_accounts.push(ffi_match);
500+
continue;
501+
}
502+
CoreAccountTypeMatch::IdentityAuthenticationBls {
503+
identity_index,
504+
involved_addresses,
505+
} => {
506+
let ffi_match = FFIAccountMatch {
507+
account_type: FFIAccountType::IdentityAuthenticationBls as u32,
508+
account_index: *identity_index,
509+
registration_index: 0,
510+
received: account_match.received,
511+
sent: account_match.sent,
512+
external_addresses_count: involved_addresses.len() as c_uint,
513+
internal_addresses_count: 0,
514+
has_external_addresses: !involved_addresses.is_empty(),
515+
has_internal_addresses: false,
516+
};
517+
ffi_accounts.push(ffi_match);
518+
continue;
519+
}
476520
}
477521
}
478522

key-wallet-ffi/src/types.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,12 @@ pub enum FFIAccountType {
235235
AssetLockAddressTopUp = 14,
236236
/// Asset lock shielded address top-up funding (subfeature 5)
237237
AssetLockShieldedAddressTopUp = 15,
238+
/// Per-identity ECDSA authentication keys (DIP-13, sub-feature 0', key type 0').
239+
/// Path prefix: `m/9'/coin_type'/5'/0'/0'/identity_index'`.
240+
IdentityAuthenticationEcdsa = 16,
241+
/// Per-identity BLS authentication keys (DIP-13, sub-feature 0', key type 1').
242+
/// Path prefix: `m/9'/coin_type'/5'/0'/1'/identity_index'`.
243+
IdentityAuthenticationBls = 17,
238244
}
239245

240246
impl FFIAccountType {
@@ -273,6 +279,29 @@ impl FFIAccountType {
273279
FFIAccountType::ProviderOwnerKeys => key_wallet::AccountType::ProviderOwnerKeys,
274280
FFIAccountType::ProviderOperatorKeys => key_wallet::AccountType::ProviderOperatorKeys,
275281
FFIAccountType::ProviderPlatformKeys => key_wallet::AccountType::ProviderPlatformKeys,
282+
// DIP-13 authentication accounts use the provided `index` as
283+
// `identity_index` (the hardened child at path level 6).
284+
FFIAccountType::IdentityAuthenticationEcdsa => {
285+
key_wallet::AccountType::IdentityAuthenticationEcdsa {
286+
identity_index: index,
287+
}
288+
}
289+
#[cfg(feature = "bls")]
290+
FFIAccountType::IdentityAuthenticationBls => {
291+
key_wallet::AccountType::IdentityAuthenticationBls {
292+
identity_index: index,
293+
}
294+
}
295+
#[cfg(not(feature = "bls"))]
296+
FFIAccountType::IdentityAuthenticationBls => {
297+
// This should never happen at runtime in a default-featured
298+
// build (key-wallet-ffi enables `bls` by default), but handle
299+
// gracefully if the host opts out.
300+
panic!(
301+
"FFIAccountType::IdentityAuthenticationBls requires the `bls` feature \
302+
in key-wallet-ffi to be enabled"
303+
);
304+
}
276305
// DashPay variants require additional identity IDs (user_identity_id and friend_identity_id)
277306
// that are not part of the current FFI API. These types cannot be constructed via this
278307
// conversion path. Attempting to use them is a programming error.
@@ -366,6 +395,13 @@ impl FFIAccountType {
366395
key_wallet::AccountType::ProviderPlatformKeys => {
367396
(FFIAccountType::ProviderPlatformKeys, 0, None)
368397
}
398+
key_wallet::AccountType::IdentityAuthenticationEcdsa {
399+
identity_index,
400+
} => (FFIAccountType::IdentityAuthenticationEcdsa, *identity_index, None),
401+
#[cfg(feature = "bls")]
402+
key_wallet::AccountType::IdentityAuthenticationBls {
403+
identity_index,
404+
} => (FFIAccountType::IdentityAuthenticationBls, *identity_index, None),
369405
key_wallet::AccountType::DashpayReceivingFunds {
370406
index,
371407
user_identity_id,

0 commit comments

Comments
 (0)