Skip to main content

bsv/wallet/
interfaces.rs

1//! WalletInterface trait and all arg/result structs.
2//!
3//! Defines the contract that all wallet implementations must satisfy.
4//! Translated from Go SDK wallet/interfaces.go and TS SDK Wallet.interfaces.ts.
5//! Uses #[async_trait] for object safety -- enables `dyn WalletInterface`.
6
7use std::collections::HashMap;
8
9use async_trait::async_trait;
10
11use crate::primitives::public_key::PublicKey;
12use crate::wallet::error::WalletError;
13use crate::wallet::types::{
14    BasketStringUnder300Bytes, BooleanDefaultFalse, BooleanDefaultTrue, Counterparty,
15    DescriptionString5to50Bytes, LabelStringUnder300Bytes, OutpointString,
16    OutputTagStringUnder300Bytes, PositiveIntegerDefault10Max10000, PositiveIntegerOrZero,
17    Protocol, SatoshiValue, TXIDHexString,
18};
19
20// ---------------------------------------------------------------------------
21// Serde helper modules (only compiled with "serde" feature)
22// ---------------------------------------------------------------------------
23
24/// Serde helpers for custom JSON serialization of wallet types.
25/// Gated behind the "serde" feature since serde is an optional dependency.
26#[cfg(feature = "serde")]
27pub(crate) mod serde_helpers {
28    use crate::primitives::public_key::PublicKey;
29
30    /// Serialize/deserialize PublicKey as DER hex string.
31    pub mod public_key_hex {
32        use super::PublicKey;
33        use serde::{self, Deserialize, Deserializer, Serializer};
34
35        pub fn serialize<S>(pk: &PublicKey, serializer: S) -> Result<S::Ok, S::Error>
36        where
37            S: Serializer,
38        {
39            serializer.serialize_str(&pk.to_der_hex())
40        }
41
42        pub fn deserialize<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
43        where
44            D: Deserializer<'de>,
45        {
46            let s = String::deserialize(deserializer)?;
47            PublicKey::from_string(&s).map_err(serde::de::Error::custom)
48        }
49    }
50
51    /// Serialize/deserialize `Option<PublicKey>` as optional DER hex string.
52    #[allow(dead_code)]
53    pub mod option_public_key_hex {
54        use super::PublicKey;
55        use serde::{self, Deserialize, Deserializer, Serializer};
56
57        pub fn serialize<S>(pk: &Option<PublicKey>, serializer: S) -> Result<S::Ok, S::Error>
58        where
59            S: Serializer,
60        {
61            match pk {
62                Some(pk) => serializer.serialize_str(&pk.to_der_hex()),
63                None => serializer.serialize_none(),
64            }
65        }
66
67        pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<PublicKey>, D::Error>
68        where
69            D: Deserializer<'de>,
70        {
71            let opt: Option<String> = Option::deserialize(deserializer)?;
72            match opt {
73                Some(s) if !s.is_empty() => PublicKey::from_string(&s)
74                    .map(Some)
75                    .map_err(serde::de::Error::custom),
76                _ => Ok(None),
77            }
78        }
79    }
80
81    /// Serialize/deserialize `Vec<PublicKey>` as array of DER hex strings.
82    pub mod vec_public_key_hex {
83        use super::PublicKey;
84        use serde::ser::SerializeSeq;
85        use serde::{self, Deserialize, Deserializer, Serializer};
86
87        pub fn serialize<S>(pks: &[PublicKey], serializer: S) -> Result<S::Ok, S::Error>
88        where
89            S: Serializer,
90        {
91            let mut seq = serializer.serialize_seq(Some(pks.len()))?;
92            for pk in pks {
93                seq.serialize_element(&pk.to_der_hex())?;
94            }
95            seq.end()
96        }
97
98        pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<PublicKey>, D::Error>
99        where
100            D: Deserializer<'de>,
101        {
102            let strs: Vec<String> = Vec::deserialize(deserializer)?;
103            strs.iter()
104                .map(|s| PublicKey::from_string(s).map_err(serde::de::Error::custom))
105                .collect()
106        }
107    }
108
109    /// Serialize/deserialize [u8; 32] as base64 string (matches Go SDK Bytes32Base64).
110    pub mod bytes32_base64 {
111        use serde::{self, Deserialize, Deserializer, Serializer};
112
113        pub fn serialize<S>(bytes: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
114        where
115            S: Serializer,
116        {
117            serializer.serialize_str(&base64_encode(bytes))
118        }
119
120        pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
121        where
122            D: Deserializer<'de>,
123        {
124            let s = String::deserialize(deserializer)?;
125            let decoded = base64_decode(&s).map_err(serde::de::Error::custom)?;
126            if decoded.len() > 32 {
127                return Err(serde::de::Error::custom(
128                    "base64 decoded value exceeds 32 bytes",
129                ));
130            }
131            let mut buf = [0u8; 32];
132            buf[..decoded.len()].copy_from_slice(&decoded);
133            Ok(buf)
134        }
135
136        /// Encode variable-length bytes as base64 (used by bytes_as_base64 module).
137        pub(super) fn base64_encode_vec(data: &[u8]) -> String {
138            base64_encode(data)
139        }
140
141        /// Decode base64 string to variable-length bytes (used by bytes_as_base64 module).
142        pub(super) fn base64_decode_vec(s: &str) -> Result<Vec<u8>, String> {
143            base64_decode(s)
144        }
145
146        fn base64_encode(data: &[u8]) -> String {
147            const CHARS: &[u8] =
148                b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
149            let mut result = String::new();
150            let chunks = data.chunks(3);
151            for chunk in chunks {
152                let b0 = chunk[0] as u32;
153                let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 };
154                let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 };
155                let triple = (b0 << 16) | (b1 << 8) | b2;
156                result.push(CHARS[((triple >> 18) & 0x3F) as usize] as char);
157                result.push(CHARS[((triple >> 12) & 0x3F) as usize] as char);
158                if chunk.len() > 1 {
159                    result.push(CHARS[((triple >> 6) & 0x3F) as usize] as char);
160                } else {
161                    result.push('=');
162                }
163                if chunk.len() > 2 {
164                    result.push(CHARS[(triple & 0x3F) as usize] as char);
165                } else {
166                    result.push('=');
167                }
168            }
169            result
170        }
171
172        fn base64_decode(s: &str) -> Result<Vec<u8>, String> {
173            fn char_to_val(c: u8) -> Result<u8, String> {
174                match c {
175                    b'A'..=b'Z' => Ok(c - b'A'),
176                    b'a'..=b'z' => Ok(c - b'a' + 26),
177                    b'0'..=b'9' => Ok(c - b'0' + 52),
178                    b'+' => Ok(62),
179                    b'/' => Ok(63),
180                    _ => Err(format!("invalid base64 character: {}", c as char)),
181                }
182            }
183            let bytes = s.as_bytes();
184            let mut result = Vec::new();
185            let mut i = 0;
186            while i < bytes.len() {
187                if bytes[i] == b'=' {
188                    break;
189                }
190                let a = char_to_val(bytes[i])?;
191                let b = if i + 1 < bytes.len() && bytes[i + 1] != b'=' {
192                    char_to_val(bytes[i + 1])?
193                } else {
194                    0
195                };
196                let c = if i + 2 < bytes.len() && bytes[i + 2] != b'=' {
197                    char_to_val(bytes[i + 2])?
198                } else {
199                    0
200                };
201                let d = if i + 3 < bytes.len() && bytes[i + 3] != b'=' {
202                    char_to_val(bytes[i + 3])?
203                } else {
204                    0
205                };
206                let triple =
207                    ((a as u32) << 18) | ((b as u32) << 12) | ((c as u32) << 6) | (d as u32);
208                result.push(((triple >> 16) & 0xFF) as u8);
209                if i + 2 < bytes.len() && bytes[i + 2] != b'=' {
210                    result.push(((triple >> 8) & 0xFF) as u8);
211                }
212                if i + 3 < bytes.len() && bytes[i + 3] != b'=' {
213                    result.push((triple & 0xFF) as u8);
214                }
215                i += 4;
216            }
217            Ok(result)
218        }
219    }
220
221    /// Serialize/deserialize `Vec<u8>` as base64 string.
222    ///
223    /// Matches Go's default `json.Marshal` for `[]byte` and TS SDK `Base64String`.
224    /// Used for `Payment.derivation_prefix` and `Payment.derivation_suffix`.
225    pub mod bytes_as_base64 {
226        use serde::{self, Deserialize, Deserializer, Serializer};
227
228        pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
229        where
230            S: Serializer,
231        {
232            serializer.serialize_str(&super::bytes32_base64::base64_encode_vec(bytes))
233        }
234
235        pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
236        where
237            D: Deserializer<'de>,
238        {
239            let s = String::deserialize(deserializer)?;
240            super::bytes32_base64::base64_decode_vec(&s).map_err(serde::de::Error::custom)
241        }
242    }
243
244    /// Serialize/deserialize `Vec<u8>` as JSON array of numbers (matches Go SDK BytesList).
245    pub mod bytes_as_array {
246        use serde::{Deserialize, Deserializer, Serializer};
247
248        pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
249        where
250            S: Serializer,
251        {
252            serializer.collect_seq(bytes.iter())
253        }
254
255        pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
256        where
257            D: Deserializer<'de>,
258        {
259            Vec::<u8>::deserialize(deserializer)
260        }
261    }
262
263    /// Serialize/deserialize `Option<Vec<u8>>` as optional JSON array of numbers.
264    pub mod option_bytes_as_array {
265        use serde::{Deserialize, Deserializer, Serializer};
266
267        pub fn serialize<S>(bytes: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
268        where
269            S: Serializer,
270        {
271            match bytes {
272                Some(b) => serializer.collect_seq(b.iter()),
273                None => serializer.serialize_none(),
274            }
275        }
276
277        pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
278        where
279            D: Deserializer<'de>,
280        {
281            Option::<Vec<u8>>::deserialize(deserializer)
282        }
283    }
284
285    /// Serialize/deserialize `Vec<u8>` as hex string (matches Go SDK BytesHex).
286    pub mod bytes_as_hex {
287        use serde::{self, Deserialize, Deserializer, Serializer};
288
289        pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
290        where
291            S: Serializer,
292        {
293            serializer.serialize_str(&to_hex(bytes))
294        }
295
296        pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
297        where
298            D: Deserializer<'de>,
299        {
300            let s = String::deserialize(deserializer)?;
301            from_hex(&s).map_err(serde::de::Error::custom)
302        }
303
304        fn to_hex(bytes: &[u8]) -> String {
305            const HEX: &[u8; 16] = b"0123456789abcdef";
306            let mut s = String::with_capacity(bytes.len() * 2);
307            for &b in bytes {
308                s.push(HEX[(b >> 4) as usize] as char);
309                s.push(HEX[(b & 0xf) as usize] as char);
310            }
311            s
312        }
313
314        pub(crate) fn from_hex(s: &str) -> Result<Vec<u8>, String> {
315            if !s.len().is_multiple_of(2) {
316                return Err("hex string has odd length".to_string());
317            }
318            let bytes = s.as_bytes();
319            let mut result = Vec::with_capacity(bytes.len() / 2);
320            for chunk in bytes.chunks(2) {
321                let hi = hex_val(chunk[0])?;
322                let lo = hex_val(chunk[1])?;
323                result.push((hi << 4) | lo);
324            }
325            Ok(result)
326        }
327
328        fn hex_val(b: u8) -> Result<u8, String> {
329            match b {
330                b'0'..=b'9' => Ok(b - b'0'),
331                b'a'..=b'f' => Ok(b - b'a' + 10),
332                b'A'..=b'F' => Ok(b - b'A' + 10),
333                _ => Err(format!("invalid hex character: {}", b as char)),
334            }
335        }
336    }
337
338    /// Serialize/deserialize `Option<Vec<u8>>` as optional hex string.
339    pub mod option_bytes_as_hex {
340        use serde::{self, Deserialize, Deserializer, Serializer};
341
342        pub fn serialize<S>(bytes: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
343        where
344            S: Serializer,
345        {
346            match bytes {
347                Some(b) => super::bytes_as_hex::serialize(b, serializer),
348                None => serializer.serialize_none(),
349            }
350        }
351
352        pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
353        where
354            D: Deserializer<'de>,
355        {
356            let opt: Option<String> = Option::deserialize(deserializer)?;
357            match opt {
358                Some(s) if !s.is_empty() => super::bytes_as_hex::from_hex(&s)
359                    .map(Some)
360                    .map_err(serde::de::Error::custom),
361                _ => Ok(None),
362            }
363        }
364    }
365}
366
367/// Serde `skip_serializing_if` predicate that skips `false` values.
368///
369/// Used for plain `bool` fields whose TS wire form is `?: true` (the literal
370/// type `true | undefined`) — those must serialize only when `true`, and be
371/// omitted when `false` to match TS's `JSON.stringify(undefined)` behavior.
372#[cfg(feature = "serde")]
373fn is_false(b: &bool) -> bool {
374    !*b
375}
376
377// ---------------------------------------------------------------------------
378// Enums
379// ---------------------------------------------------------------------------
380
381/// Current state of a transaction action.
382#[derive(Clone, Debug, PartialEq, Eq)]
383#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
384#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
385pub enum ActionStatus {
386    Completed,
387    Unprocessed,
388    Sending,
389    Unproven,
390    Unsigned,
391    #[cfg_attr(feature = "serde", serde(rename = "nosend"))]
392    NoSend,
393    #[cfg_attr(feature = "serde", serde(rename = "nonfinal"))]
394    NonFinal,
395    Failed,
396}
397
398impl ActionStatus {
399    pub fn as_str(&self) -> &'static str {
400        match self {
401            ActionStatus::Completed => "completed",
402            ActionStatus::Unprocessed => "unprocessed",
403            ActionStatus::Sending => "sending",
404            ActionStatus::Unproven => "unproven",
405            ActionStatus::Unsigned => "unsigned",
406            ActionStatus::NoSend => "nosend",
407            ActionStatus::NonFinal => "nonfinal",
408            ActionStatus::Failed => "failed",
409        }
410    }
411}
412
413/// Status of a transaction result (subset of ActionStatus).
414#[derive(Clone, Debug, PartialEq, Eq)]
415#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
416#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
417pub enum ActionResultStatus {
418    Unproven,
419    Sending,
420    Failed,
421}
422
423impl ActionResultStatus {
424    pub fn as_str(&self) -> &'static str {
425        match self {
426            ActionResultStatus::Unproven => "unproven",
427            ActionResultStatus::Sending => "sending",
428            ActionResultStatus::Failed => "failed",
429        }
430    }
431}
432
433/// How multiple criteria are combined in queries.
434#[derive(Clone, Debug, PartialEq, Eq)]
435#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
436#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
437pub enum QueryMode {
438    Any,
439    All,
440}
441
442impl QueryMode {
443    pub fn as_str(&self) -> &'static str {
444        match self {
445            QueryMode::Any => "any",
446            QueryMode::All => "all",
447        }
448    }
449}
450
451/// What additional data to include with output listings.
452#[derive(Clone, Debug, PartialEq, Eq)]
453#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
454pub enum OutputInclude {
455    #[cfg_attr(feature = "serde", serde(rename = "locking scripts"))]
456    LockingScripts,
457    #[cfg_attr(feature = "serde", serde(rename = "entire transactions"))]
458    EntireTransactions,
459}
460
461impl OutputInclude {
462    pub fn as_str(&self) -> &'static str {
463        match self {
464            OutputInclude::LockingScripts => "locking scripts",
465            OutputInclude::EntireTransactions => "entire transactions",
466        }
467    }
468}
469
470/// Protocol for internalizing transaction outputs.
471#[derive(Clone, Debug, PartialEq, Eq)]
472#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
473pub enum InternalizeProtocol {
474    #[cfg_attr(feature = "serde", serde(rename = "wallet payment"))]
475    WalletPayment,
476    #[cfg_attr(feature = "serde", serde(rename = "basket insertion"))]
477    BasketInsertion,
478}
479
480impl InternalizeProtocol {
481    pub fn as_str(&self) -> &'static str {
482        match self {
483            InternalizeProtocol::WalletPayment => "wallet payment",
484            InternalizeProtocol::BasketInsertion => "basket insertion",
485        }
486    }
487}
488
489/// Protocol for certificate acquisition.
490#[derive(Clone, Debug, PartialEq, Eq)]
491#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
492#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
493pub enum AcquisitionProtocol {
494    Direct,
495    Issuance,
496}
497
498impl AcquisitionProtocol {
499    pub fn as_str(&self) -> &'static str {
500        match self {
501            AcquisitionProtocol::Direct => "direct",
502            AcquisitionProtocol::Issuance => "issuance",
503        }
504    }
505}
506
507/// Blockchain network type.
508#[derive(Clone, Debug, PartialEq, Eq)]
509#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
510#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
511pub enum Network {
512    Mainnet,
513    Testnet,
514}
515
516impl Network {
517    pub fn as_str(&self) -> &'static str {
518        match self {
519            Network::Mainnet => "mainnet",
520            Network::Testnet => "testnet",
521        }
522    }
523}
524
525/// Trust level for self-referential operations.
526#[derive(Clone, Debug, PartialEq, Eq)]
527#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
528#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
529pub enum TrustSelf {
530    Known,
531}
532
533impl TrustSelf {
534    pub fn as_str(&self) -> &'static str {
535        match self {
536            TrustSelf::Known => "known",
537        }
538    }
539}
540
541// ---------------------------------------------------------------------------
542// Core types: Certificate, CertificateType, SerialNumber, KeyringRevealer
543// ---------------------------------------------------------------------------
544
545/// Newtype wrapper for certificate type identifier (32 bytes).
546/// Serializes as base64 string matching Go SDK Bytes32Base64.
547#[derive(Clone, Debug, PartialEq, Eq, Hash)]
548pub struct CertificateType(pub [u8; 32]);
549
550#[cfg(feature = "serde")]
551impl serde::Serialize for CertificateType {
552    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
553        serde_helpers::bytes32_base64::serialize(&self.0, serializer)
554    }
555}
556
557#[cfg(feature = "serde")]
558impl<'de> serde::Deserialize<'de> for CertificateType {
559    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
560        serde_helpers::bytes32_base64::deserialize(deserializer).map(CertificateType)
561    }
562}
563
564impl CertificateType {
565    /// Parse a CertificateType from a base64, hex, or raw string.
566    ///
567    /// Accepts base64 (44 chars or ending with '='), hex (64 hex chars),
568    /// or raw byte strings (<=32 bytes).
569    pub fn from_string(s: &str) -> Result<Self, WalletError> {
570        let bytes = if s.len() == 44 || (!s.is_empty() && s.ends_with('=')) {
571            SerialNumber::base64_decode_sn(s)?
572        } else if s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit()) {
573            crate::primitives::utils::from_hex(s)
574                .map_err(|e| WalletError::InvalidParameter(format!("hex: {e}")))?
575        } else if s.len() <= 32 {
576            let mut buf = [0u8; 32];
577            buf[..s.len()].copy_from_slice(s.as_bytes());
578            return Ok(CertificateType(buf));
579        } else {
580            return Err(WalletError::InvalidParameter(format!(
581                "CertificateType: unsupported string length {}",
582                s.len()
583            )));
584        };
585        if bytes.len() != 32 {
586            return Err(WalletError::InvalidParameter(format!(
587                "CertificateType must decode to 32 bytes, got {}",
588                bytes.len()
589            )));
590        }
591        let mut buf = [0u8; 32];
592        buf.copy_from_slice(&bytes);
593        Ok(CertificateType(buf))
594    }
595
596    pub fn bytes(&self) -> &[u8; 32] {
597        &self.0
598    }
599}
600
601/// Newtype wrapper for certificate serial number (32 bytes).
602/// Serializes as base64 string matching Go SDK Bytes32Base64.
603#[derive(Clone, Debug, PartialEq, Eq, Hash)]
604pub struct SerialNumber(pub [u8; 32]);
605
606#[cfg(feature = "serde")]
607impl serde::Serialize for SerialNumber {
608    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
609        serde_helpers::bytes32_base64::serialize(&self.0, serializer)
610    }
611}
612
613#[cfg(feature = "serde")]
614impl<'de> serde::Deserialize<'de> for SerialNumber {
615    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
616        serde_helpers::bytes32_base64::deserialize(deserializer).map(SerialNumber)
617    }
618}
619
620impl SerialNumber {
621    /// Parse a SerialNumber from a base64 or hex string.
622    ///
623    /// Accepts:
624    /// - 44-character base64 string (with optional padding, decodes to 32 bytes)
625    /// - 64-character hex string (decodes to 32 bytes)
626    ///
627    /// Returns an error for other formats or if the decoded length is not 32 bytes.
628    pub fn from_string(s: &str) -> Result<Self, WalletError> {
629        let bytes = if s.len() == 44 || (!s.is_empty() && s.ends_with('=')) {
630            // Base64 format (32 bytes -> 44 base64 chars with padding)
631            Self::base64_decode_sn(s)?
632        } else if s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit()) {
633            // Hex format (32 bytes -> 64 hex chars)
634            crate::primitives::utils::from_hex(s)
635                .map_err(|e| WalletError::InvalidParameter(format!("hex: {e}")))?
636        } else {
637            return Err(WalletError::InvalidParameter(format!(
638                "SerialNumber string must be 44 (base64) or 64 (hex) chars, got {}",
639                s.len()
640            )));
641        };
642        if bytes.len() != 32 {
643            return Err(WalletError::InvalidParameter(
644                "SerialNumber must decode to 32 bytes".into(),
645            ));
646        }
647        let mut buf = [0u8; 32];
648        buf.copy_from_slice(&bytes);
649        Ok(SerialNumber(buf))
650    }
651
652    /// Inline base64 decoder for SerialNumber (self-contained, no cross-module dependency).
653    pub(crate) fn base64_decode_sn(s: &str) -> Result<Vec<u8>, WalletError> {
654        fn b64_val(c: u8) -> Result<u8, WalletError> {
655            match c {
656                b'A'..=b'Z' => Ok(c - b'A'),
657                b'a'..=b'z' => Ok(c - b'a' + 26),
658                b'0'..=b'9' => Ok(c - b'0' + 52),
659                b'+' => Ok(62),
660                b'/' => Ok(63),
661                _ => Err(WalletError::InvalidParameter(format!(
662                    "invalid base64 character: {}",
663                    c as char
664                ))),
665            }
666        }
667        let bytes = s.as_bytes();
668        let mut result = Vec::new();
669        let mut i = 0;
670        while i < bytes.len() {
671            if bytes[i] == b'=' {
672                break;
673            }
674            let a = b64_val(bytes[i])?;
675            let b = if i + 1 < bytes.len() && bytes[i + 1] != b'=' {
676                b64_val(bytes[i + 1])?
677            } else {
678                0
679            };
680            let c = if i + 2 < bytes.len() && bytes[i + 2] != b'=' {
681                b64_val(bytes[i + 2])?
682            } else {
683                0
684            };
685            let d = if i + 3 < bytes.len() && bytes[i + 3] != b'=' {
686                b64_val(bytes[i + 3])?
687            } else {
688                0
689            };
690            let n = (a as u32) << 18 | (b as u32) << 12 | (c as u32) << 6 | (d as u32);
691            result.push((n >> 16) as u8);
692            if i + 2 < bytes.len() && bytes[i + 2] != b'=' {
693                result.push((n >> 8) as u8);
694            }
695            if i + 3 < bytes.len() && bytes[i + 3] != b'=' {
696                result.push(n as u8);
697            }
698            i += 4;
699        }
700        Ok(result)
701    }
702
703    pub fn bytes(&self) -> &[u8; 32] {
704        &self.0
705    }
706}
707
708/// A certificate in the wallet.
709#[derive(Clone, Debug)]
710#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
711#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
712pub struct Certificate {
713    #[cfg_attr(feature = "serde", serde(rename = "type"))]
714    pub cert_type: CertificateType,
715    pub serial_number: SerialNumber,
716    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
717    pub subject: PublicKey,
718    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
719    pub certifier: PublicKey,
720    // `skip_serializing_if` without `default` is not a round-trip: serializing a
721    // `None` OMITS the key, and deserializing then rejects the very JSON we just
722    // produced ("missing field"). `signature` in particular is `None` for every
723    // UNSIGNED certificate — the state a cert is necessarily in while it is being
724    // presented for signing — so without `default` such a cert cannot be sent over
725    // the wire at all.
726    #[cfg_attr(
727        feature = "serde",
728        serde(default, skip_serializing_if = "Option::is_none")
729    )]
730    pub revocation_outpoint: Option<String>,
731    #[cfg_attr(
732        feature = "serde",
733        serde(default, skip_serializing_if = "Option::is_none")
734    )]
735    pub fields: Option<HashMap<String, String>>,
736    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
737    #[cfg_attr(
738        feature = "serde",
739        serde(default, skip_serializing_if = "Option::is_none")
740    )]
741    pub signature: Option<Vec<u8>>,
742}
743
744/// A partial certificate where all fields are optional.
745/// Used for ProveCertificateArgs to match TS SDK's `Partial<WalletCertificate>`.
746#[derive(Clone, Debug)]
747#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
748#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
749pub struct PartialCertificate {
750    #[cfg_attr(feature = "serde", serde(rename = "type"))]
751    #[cfg_attr(
752        feature = "serde",
753        serde(default, skip_serializing_if = "Option::is_none")
754    )]
755    pub cert_type: Option<CertificateType>,
756    #[cfg_attr(
757        feature = "serde",
758        serde(default, skip_serializing_if = "Option::is_none")
759    )]
760    pub serial_number: Option<SerialNumber>,
761    #[cfg_attr(
762        feature = "serde",
763        serde(with = "serde_helpers::option_public_key_hex")
764    )]
765    #[cfg_attr(feature = "serde", serde(default))]
766    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
767    pub subject: Option<PublicKey>,
768    #[cfg_attr(
769        feature = "serde",
770        serde(with = "serde_helpers::option_public_key_hex")
771    )]
772    #[cfg_attr(feature = "serde", serde(default))]
773    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
774    pub certifier: Option<PublicKey>,
775    #[cfg_attr(
776        feature = "serde",
777        serde(default, skip_serializing_if = "Option::is_none")
778    )]
779    pub revocation_outpoint: Option<String>,
780    #[cfg_attr(
781        feature = "serde",
782        serde(default, skip_serializing_if = "Option::is_none")
783    )]
784    pub fields: Option<HashMap<String, String>>,
785    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
786    #[cfg_attr(feature = "serde", serde(default))]
787    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
788    pub signature: Option<Vec<u8>>,
789}
790
791impl From<Certificate> for PartialCertificate {
792    fn from(c: Certificate) -> Self {
793        PartialCertificate {
794            cert_type: Some(c.cert_type),
795            serial_number: Some(c.serial_number),
796            subject: Some(c.subject),
797            certifier: Some(c.certifier),
798            revocation_outpoint: c.revocation_outpoint,
799            fields: c.fields,
800            signature: c.signature,
801        }
802    }
803}
804
805/// Identifies who reveals a keyring.
806#[derive(Clone, Debug)]
807pub enum KeyringRevealer {
808    /// The certifier reveals the keyring.
809    Certifier,
810    /// A specific public key reveals the keyring.
811    PubKey(PublicKey),
812}
813
814#[cfg(feature = "serde")]
815impl serde::Serialize for KeyringRevealer {
816    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
817        match self {
818            KeyringRevealer::Certifier => serializer.serialize_str("certifier"),
819            KeyringRevealer::PubKey(pk) => serializer.serialize_str(&pk.to_der_hex()),
820        }
821    }
822}
823
824#[cfg(feature = "serde")]
825impl<'de> serde::Deserialize<'de> for KeyringRevealer {
826    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
827        let s = String::deserialize(deserializer)?;
828        if s == "certifier" || s.is_empty() {
829            Ok(KeyringRevealer::Certifier)
830        } else {
831            PublicKey::from_string(&s)
832                .map(KeyringRevealer::PubKey)
833                .map_err(serde::de::Error::custom)
834        }
835    }
836}
837
838// ---------------------------------------------------------------------------
839// Action types (CreateAction, SignAction, AbortAction)
840// ---------------------------------------------------------------------------
841
842/// An input to be spent in a new transaction.
843#[derive(Clone, Debug)]
844#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
845#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
846pub struct CreateActionInput {
847    pub outpoint: OutpointString,
848    pub input_description: String,
849    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
850    #[cfg_attr(
851        feature = "serde",
852        serde(skip_serializing_if = "Option::is_none", default)
853    )]
854    pub unlocking_script: Option<Vec<u8>>,
855    #[cfg_attr(
856        feature = "serde",
857        serde(default, skip_serializing_if = "Option::is_none")
858    )]
859    pub unlocking_script_length: Option<u32>,
860    #[cfg_attr(
861        feature = "serde",
862        serde(default, skip_serializing_if = "Option::is_none")
863    )]
864    pub sequence_number: Option<u32>,
865}
866
867/// An output to be created in a new transaction.
868#[derive(Clone, Debug)]
869#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
870#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
871pub struct CreateActionOutput {
872    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
873    #[cfg_attr(
874        feature = "serde",
875        serde(skip_serializing_if = "Option::is_none", default)
876    )]
877    pub locking_script: Option<Vec<u8>>,
878    pub satoshis: SatoshiValue,
879    pub output_description: String,
880    #[cfg_attr(
881        feature = "serde",
882        serde(default, skip_serializing_if = "Option::is_none")
883    )]
884    pub basket: Option<BasketStringUnder300Bytes>,
885    #[cfg_attr(
886        feature = "serde",
887        serde(default, skip_serializing_if = "Option::is_none")
888    )]
889    pub custom_instructions: Option<String>,
890    #[cfg_attr(
891        feature = "serde",
892        serde(skip_serializing_if = "Vec::is_empty", default)
893    )]
894    pub tags: Vec<OutputTagStringUnder300Bytes>,
895}
896
897/// Optional parameters for creating a new transaction.
898#[derive(Clone, Debug, Default)]
899#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
900#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
901pub struct CreateActionOptions {
902    #[cfg_attr(
903        feature = "serde",
904        serde(
905            default = "BooleanDefaultTrue::none",
906            skip_serializing_if = "BooleanDefaultTrue::is_none"
907        )
908    )]
909    pub sign_and_process: BooleanDefaultTrue,
910    #[cfg_attr(
911        feature = "serde",
912        serde(
913            default = "BooleanDefaultTrue::none",
914            skip_serializing_if = "BooleanDefaultTrue::is_none"
915        )
916    )]
917    pub accept_delayed_broadcast: BooleanDefaultTrue,
918    #[cfg_attr(
919        feature = "serde",
920        serde(default, skip_serializing_if = "Option::is_none")
921    )]
922    pub trust_self: Option<TrustSelf>,
923    #[cfg_attr(
924        feature = "serde",
925        serde(skip_serializing_if = "Vec::is_empty", default)
926    )]
927    pub known_txids: Vec<TXIDHexString>,
928    #[cfg_attr(
929        feature = "serde",
930        serde(
931            default = "BooleanDefaultFalse::none",
932            skip_serializing_if = "BooleanDefaultFalse::is_none"
933        )
934    )]
935    pub return_txid_only: BooleanDefaultFalse,
936    #[cfg_attr(
937        feature = "serde",
938        serde(
939            default = "BooleanDefaultFalse::none",
940            skip_serializing_if = "BooleanDefaultFalse::is_none"
941        )
942    )]
943    pub no_send: BooleanDefaultFalse,
944    #[cfg_attr(
945        feature = "serde",
946        serde(skip_serializing_if = "Vec::is_empty", default)
947    )]
948    pub no_send_change: Vec<OutpointString>,
949    #[cfg_attr(
950        feature = "serde",
951        serde(skip_serializing_if = "Vec::is_empty", default)
952    )]
953    pub send_with: Vec<TXIDHexString>,
954    #[cfg_attr(
955        feature = "serde",
956        serde(
957            default = "BooleanDefaultTrue::none",
958            skip_serializing_if = "BooleanDefaultTrue::is_none"
959        )
960    )]
961    pub randomize_outputs: BooleanDefaultTrue,
962}
963
964/// Arguments for creating a new transaction.
965#[derive(Clone, Debug)]
966#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
967#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
968pub struct CreateActionArgs {
969    pub description: DescriptionString5to50Bytes,
970    #[cfg_attr(
971        feature = "serde",
972        serde(with = "serde_helpers::option_bytes_as_array")
973    )]
974    #[cfg_attr(
975        feature = "serde",
976        serde(skip_serializing_if = "Option::is_none", default)
977    )]
978    #[cfg_attr(feature = "serde", serde(rename = "inputBEEF"))]
979    pub input_beef: Option<Vec<u8>>,
980    #[cfg_attr(
981        feature = "serde",
982        serde(skip_serializing_if = "Vec::is_empty", default)
983    )]
984    pub inputs: Vec<CreateActionInput>,
985    #[cfg_attr(
986        feature = "serde",
987        serde(skip_serializing_if = "Vec::is_empty", default)
988    )]
989    pub outputs: Vec<CreateActionOutput>,
990    #[cfg_attr(
991        feature = "serde",
992        serde(default, skip_serializing_if = "Option::is_none")
993    )]
994    pub lock_time: Option<u32>,
995    #[cfg_attr(
996        feature = "serde",
997        serde(default, skip_serializing_if = "Option::is_none")
998    )]
999    pub version: Option<u32>,
1000    #[cfg_attr(
1001        feature = "serde",
1002        serde(skip_serializing_if = "Vec::is_empty", default)
1003    )]
1004    pub labels: Vec<LabelStringUnder300Bytes>,
1005    #[cfg_attr(
1006        feature = "serde",
1007        serde(default, skip_serializing_if = "Option::is_none")
1008    )]
1009    pub options: Option<CreateActionOptions>,
1010    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1011    pub reference: Option<String>,
1012}
1013
1014/// Data needed to complete signing of a partial transaction.
1015#[derive(Clone, Debug)]
1016#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1017#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1018pub struct SignableTransaction {
1019    // bytes_as_array: TS types `tx` as `AtomicBEEF = Byte[]` (number array).
1020    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1021    pub tx: Vec<u8>,
1022    // bytes_as_base64: TS types `reference` as `Base64String`, so the wire
1023    // format is a base64 string (not a number array). See BRC-100.
1024    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_base64"))]
1025    pub reference: Vec<u8>,
1026}
1027
1028/// Status of a transaction sent as part of a batch.
1029#[derive(Clone, Debug)]
1030#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1031#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1032pub struct SendWithResult {
1033    pub txid: TXIDHexString,
1034    pub status: ActionResultStatus,
1035}
1036
1037/// Status of a review action result from undelayed broadcast.
1038#[derive(Clone, Debug, PartialEq, Eq)]
1039#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1040#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1041pub enum ReviewActionResultStatus {
1042    Success,
1043    DoubleSpend,
1044    ServiceError,
1045    InvalidTx,
1046}
1047
1048impl ReviewActionResultStatus {
1049    pub fn as_str(&self) -> &'static str {
1050        match self {
1051            ReviewActionResultStatus::Success => "success",
1052            ReviewActionResultStatus::DoubleSpend => "doubleSpend",
1053            ReviewActionResultStatus::ServiceError => "serviceError",
1054            ReviewActionResultStatus::InvalidTx => "invalidTx",
1055        }
1056    }
1057}
1058
1059/// Result of reviewing a non-delayed broadcast action.
1060#[derive(Clone, Debug)]
1061#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1062#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1063pub struct ReviewActionResult {
1064    pub txid: TXIDHexString,
1065    pub status: ReviewActionResultStatus,
1066    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1067    pub competing_txs: Option<Vec<String>>,
1068    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1069    pub competing_beef: Option<Vec<u8>>,
1070}
1071
1072/// Result of creating a transaction.
1073#[derive(Clone, Debug)]
1074#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1075#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1076pub struct CreateActionResult {
1077    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1078    pub txid: Option<TXIDHexString>,
1079    #[cfg_attr(
1080        feature = "serde",
1081        serde(with = "serde_helpers::option_bytes_as_array")
1082    )]
1083    #[cfg_attr(
1084        feature = "serde",
1085        serde(skip_serializing_if = "Option::is_none", default)
1086    )]
1087    pub tx: Option<Vec<u8>>,
1088    #[cfg_attr(
1089        feature = "serde",
1090        serde(skip_serializing_if = "Vec::is_empty", default)
1091    )]
1092    pub no_send_change: Vec<OutpointString>,
1093    #[cfg_attr(
1094        feature = "serde",
1095        serde(skip_serializing_if = "Vec::is_empty", default)
1096    )]
1097    pub send_with_results: Vec<SendWithResult>,
1098    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1099    pub signable_transaction: Option<SignableTransaction>,
1100}
1101
1102/// Unlocking script and sequence number for a specific input.
1103#[derive(Clone, Debug)]
1104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1105#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1106pub struct SignActionSpend {
1107    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_hex"))]
1108    pub unlocking_script: Vec<u8>,
1109    #[cfg_attr(
1110        feature = "serde",
1111        serde(default, skip_serializing_if = "Option::is_none")
1112    )]
1113    pub sequence_number: Option<u32>,
1114}
1115
1116/// Controls signing and broadcasting behavior.
1117#[derive(Clone, Debug, Default)]
1118#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1119#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1120pub struct SignActionOptions {
1121    #[cfg_attr(
1122        feature = "serde",
1123        serde(
1124            default = "BooleanDefaultTrue::none",
1125            skip_serializing_if = "BooleanDefaultTrue::is_none"
1126        )
1127    )]
1128    pub accept_delayed_broadcast: BooleanDefaultTrue,
1129    #[cfg_attr(
1130        feature = "serde",
1131        serde(
1132            default = "BooleanDefaultFalse::none",
1133            skip_serializing_if = "BooleanDefaultFalse::is_none"
1134        )
1135    )]
1136    pub return_txid_only: BooleanDefaultFalse,
1137    #[cfg_attr(
1138        feature = "serde",
1139        serde(
1140            default = "BooleanDefaultFalse::none",
1141            skip_serializing_if = "BooleanDefaultFalse::is_none"
1142        )
1143    )]
1144    pub no_send: BooleanDefaultFalse,
1145    #[cfg_attr(
1146        feature = "serde",
1147        serde(skip_serializing_if = "Vec::is_empty", default)
1148    )]
1149    pub send_with: Vec<TXIDHexString>,
1150}
1151
1152/// Arguments for signing a previously created transaction.
1153#[derive(Clone, Debug)]
1154#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1155#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1156pub struct SignActionArgs {
1157    // bytes_as_base64: TS types `reference` as `Base64String` on the wire.
1158    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_base64"))]
1159    pub reference: Vec<u8>,
1160    pub spends: HashMap<u32, SignActionSpend>,
1161    #[cfg_attr(
1162        feature = "serde",
1163        serde(default, skip_serializing_if = "Option::is_none")
1164    )]
1165    pub options: Option<SignActionOptions>,
1166}
1167
1168/// Result of a successful signing operation.
1169#[derive(Clone, Debug)]
1170#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1171#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1172pub struct SignActionResult {
1173    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1174    pub txid: Option<TXIDHexString>,
1175    #[cfg_attr(
1176        feature = "serde",
1177        serde(with = "serde_helpers::option_bytes_as_array")
1178    )]
1179    #[cfg_attr(
1180        feature = "serde",
1181        serde(skip_serializing_if = "Option::is_none", default)
1182    )]
1183    pub tx: Option<Vec<u8>>,
1184    #[cfg_attr(
1185        feature = "serde",
1186        serde(skip_serializing_if = "Vec::is_empty", default)
1187    )]
1188    pub send_with_results: Vec<SendWithResult>,
1189}
1190
1191/// Arguments for aborting a transaction.
1192#[derive(Clone, Debug)]
1193#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1194#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1195pub struct AbortActionArgs {
1196    // bytes_as_base64: TS types `reference` as `Base64String` on the wire.
1197    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_base64"))]
1198    pub reference: Vec<u8>,
1199}
1200
1201/// Result of aborting a transaction.
1202#[derive(Clone, Debug)]
1203#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1204#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1205pub struct AbortActionResult {
1206    pub aborted: bool,
1207}
1208
1209// ---------------------------------------------------------------------------
1210// Action detail types (for listing)
1211// ---------------------------------------------------------------------------
1212
1213/// A transaction input with full details.
1214#[derive(Clone, Debug)]
1215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1216#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1217pub struct ActionInput {
1218    pub source_outpoint: OutpointString,
1219    pub source_satoshis: SatoshiValue,
1220    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
1221    #[cfg_attr(
1222        feature = "serde",
1223        serde(skip_serializing_if = "Option::is_none", default)
1224    )]
1225    pub source_locking_script: Option<Vec<u8>>,
1226    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
1227    #[cfg_attr(
1228        feature = "serde",
1229        serde(skip_serializing_if = "Option::is_none", default)
1230    )]
1231    pub unlocking_script: Option<Vec<u8>>,
1232    pub input_description: String,
1233    pub sequence_number: u32,
1234}
1235
1236/// A transaction output with full details.
1237#[derive(Clone, Debug)]
1238#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1239#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1240pub struct ActionOutput {
1241    pub satoshis: SatoshiValue,
1242    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
1243    #[cfg_attr(
1244        feature = "serde",
1245        serde(skip_serializing_if = "Option::is_none", default)
1246    )]
1247    pub locking_script: Option<Vec<u8>>,
1248    pub spendable: bool,
1249    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1250    pub custom_instructions: Option<String>,
1251    pub tags: Vec<String>,
1252    pub output_index: u32,
1253    pub output_description: String,
1254    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1255    pub basket: Option<String>,
1256}
1257
1258/// Full details about a wallet transaction.
1259#[derive(Clone, Debug)]
1260#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1261#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1262pub struct Action {
1263    pub txid: TXIDHexString,
1264    pub satoshis: i64,
1265    pub status: ActionStatus,
1266    pub is_outgoing: bool,
1267    pub description: String,
1268    #[cfg_attr(
1269        feature = "serde",
1270        serde(skip_serializing_if = "Vec::is_empty", default)
1271    )]
1272    pub labels: Vec<String>,
1273    pub version: u32,
1274    pub lock_time: u32,
1275    #[cfg_attr(
1276        feature = "serde",
1277        serde(skip_serializing_if = "Vec::is_empty", default)
1278    )]
1279    pub inputs: Vec<ActionInput>,
1280    #[cfg_attr(
1281        feature = "serde",
1282        serde(skip_serializing_if = "Vec::is_empty", default)
1283    )]
1284    pub outputs: Vec<ActionOutput>,
1285}
1286
1287/// Maximum number of actions or outputs that can be returned.
1288pub const MAX_ACTIONS_LIMIT: u32 = 10000;
1289
1290// ---------------------------------------------------------------------------
1291// ListActions
1292// ---------------------------------------------------------------------------
1293
1294/// Filtering and pagination options for listing wallet transactions.
1295#[derive(Clone, Debug)]
1296#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1297#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1298pub struct ListActionsArgs {
1299    #[cfg_attr(
1300        feature = "serde",
1301        serde(skip_serializing_if = "Vec::is_empty", default)
1302    )]
1303    pub labels: Vec<LabelStringUnder300Bytes>,
1304    #[cfg_attr(
1305        feature = "serde",
1306        serde(default, skip_serializing_if = "Option::is_none")
1307    )]
1308    pub label_query_mode: Option<QueryMode>,
1309    #[cfg_attr(
1310        feature = "serde",
1311        serde(
1312            default = "BooleanDefaultFalse::none",
1313            skip_serializing_if = "BooleanDefaultFalse::is_none"
1314        )
1315    )]
1316    pub include_labels: BooleanDefaultFalse,
1317    #[cfg_attr(
1318        feature = "serde",
1319        serde(
1320            default = "BooleanDefaultFalse::none",
1321            skip_serializing_if = "BooleanDefaultFalse::is_none"
1322        )
1323    )]
1324    pub include_inputs: BooleanDefaultFalse,
1325    #[cfg_attr(
1326        feature = "serde",
1327        serde(
1328            default = "BooleanDefaultFalse::none",
1329            skip_serializing_if = "BooleanDefaultFalse::is_none"
1330        )
1331    )]
1332    pub include_input_source_locking_scripts: BooleanDefaultFalse,
1333    #[cfg_attr(
1334        feature = "serde",
1335        serde(
1336            default = "BooleanDefaultFalse::none",
1337            skip_serializing_if = "BooleanDefaultFalse::is_none"
1338        )
1339    )]
1340    pub include_input_unlocking_scripts: BooleanDefaultFalse,
1341    #[cfg_attr(
1342        feature = "serde",
1343        serde(
1344            default = "BooleanDefaultFalse::none",
1345            skip_serializing_if = "BooleanDefaultFalse::is_none"
1346        )
1347    )]
1348    pub include_outputs: BooleanDefaultFalse,
1349    #[cfg_attr(
1350        feature = "serde",
1351        serde(
1352            default = "BooleanDefaultFalse::none",
1353            skip_serializing_if = "BooleanDefaultFalse::is_none"
1354        )
1355    )]
1356    pub include_output_locking_scripts: BooleanDefaultFalse,
1357    #[cfg_attr(feature = "serde", serde(default))]
1358    pub limit: PositiveIntegerDefault10Max10000,
1359    #[cfg_attr(
1360        feature = "serde",
1361        serde(default, skip_serializing_if = "Option::is_none")
1362    )]
1363    pub offset: Option<PositiveIntegerOrZero>,
1364    #[cfg_attr(
1365        feature = "serde",
1366        serde(
1367            default = "BooleanDefaultTrue::none",
1368            skip_serializing_if = "BooleanDefaultTrue::is_none"
1369        )
1370    )]
1371    pub seek_permission: BooleanDefaultTrue,
1372}
1373
1374/// Paginated list of wallet transactions.
1375#[derive(Clone, Debug)]
1376#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1377#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1378pub struct ListActionsResult {
1379    pub total_actions: u32,
1380    pub actions: Vec<Action>,
1381}
1382
1383// ---------------------------------------------------------------------------
1384// InternalizeAction
1385// ---------------------------------------------------------------------------
1386
1387/// Derivation and identity data for wallet payment outputs.
1388#[derive(Clone, Debug)]
1389#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1390#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1391pub struct Payment {
1392    // bytes_as_base64: TS SDK types these as Base64String, Go uses default
1393    // json.Marshal for []byte which produces base64. BSV Desktop expects strings.
1394    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_base64"))]
1395    pub derivation_prefix: Vec<u8>,
1396    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_base64"))]
1397    pub derivation_suffix: Vec<u8>,
1398    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
1399    pub sender_identity_key: PublicKey,
1400}
1401
1402/// Metadata for outputs being inserted into baskets.
1403#[derive(Clone, Debug)]
1404#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1405#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1406pub struct BasketInsertion {
1407    pub basket: BasketStringUnder300Bytes,
1408    #[cfg_attr(
1409        feature = "serde",
1410        serde(default, skip_serializing_if = "Option::is_none")
1411    )]
1412    pub custom_instructions: Option<String>,
1413    #[cfg_attr(
1414        feature = "serde",
1415        serde(skip_serializing_if = "Vec::is_empty", default)
1416    )]
1417    pub tags: Vec<OutputTagStringUnder300Bytes>,
1418}
1419
1420/// How to process a transaction output -- as payment or basket insertion.
1421///
1422/// An enum with two variants, encoding the protocol in the variant itself.
1423/// This makes impossible states unrepresentable: a WalletPayment always has
1424/// a Payment, and a BasketInsertion always has a BasketInsertion.
1425#[derive(Clone, Debug)]
1426#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1427#[cfg_attr(feature = "serde", serde(tag = "protocol", rename_all = "camelCase"))]
1428pub enum InternalizeOutput {
1429    #[cfg_attr(feature = "serde", serde(rename = "wallet payment"))]
1430    WalletPayment {
1431        #[cfg_attr(feature = "serde", serde(rename = "outputIndex"))]
1432        output_index: u32,
1433        #[cfg_attr(feature = "serde", serde(rename = "paymentRemittance"))]
1434        payment: Payment,
1435    },
1436    #[cfg_attr(feature = "serde", serde(rename = "basket insertion"))]
1437    BasketInsertion {
1438        #[cfg_attr(feature = "serde", serde(rename = "outputIndex"))]
1439        output_index: u32,
1440        #[cfg_attr(feature = "serde", serde(rename = "insertionRemittance"))]
1441        insertion: BasketInsertion,
1442    },
1443}
1444
1445/// Arguments for importing an external transaction into the wallet.
1446#[derive(Clone, Debug)]
1447#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1448#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1449pub struct InternalizeActionArgs {
1450    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1451    pub tx: Vec<u8>,
1452    pub description: String,
1453    #[cfg_attr(
1454        feature = "serde",
1455        serde(skip_serializing_if = "Vec::is_empty", default)
1456    )]
1457    pub labels: Vec<LabelStringUnder300Bytes>,
1458    #[cfg_attr(
1459        feature = "serde",
1460        serde(
1461            default = "BooleanDefaultTrue::none",
1462            skip_serializing_if = "BooleanDefaultTrue::is_none"
1463        )
1464    )]
1465    pub seek_permission: BooleanDefaultTrue,
1466    pub outputs: Vec<InternalizeOutput>,
1467}
1468
1469/// Result of internalizing a transaction.
1470#[derive(Clone, Debug)]
1471#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1472#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1473pub struct InternalizeActionResult {
1474    pub accepted: bool,
1475}
1476
1477// ---------------------------------------------------------------------------
1478// ListOutputs
1479// ---------------------------------------------------------------------------
1480
1481/// Filtering and options for listing wallet outputs.
1482#[derive(Clone, Debug)]
1483#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1484#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1485pub struct ListOutputsArgs {
1486    pub basket: BasketStringUnder300Bytes,
1487    #[cfg_attr(
1488        feature = "serde",
1489        serde(skip_serializing_if = "Vec::is_empty", default)
1490    )]
1491    pub tags: Vec<OutputTagStringUnder300Bytes>,
1492    #[cfg_attr(
1493        feature = "serde",
1494        serde(default, skip_serializing_if = "Option::is_none")
1495    )]
1496    pub tag_query_mode: Option<QueryMode>,
1497    #[cfg_attr(
1498        feature = "serde",
1499        serde(default, skip_serializing_if = "Option::is_none")
1500    )]
1501    pub include: Option<OutputInclude>,
1502    #[cfg_attr(
1503        feature = "serde",
1504        serde(
1505            default = "BooleanDefaultFalse::none",
1506            skip_serializing_if = "BooleanDefaultFalse::is_none"
1507        )
1508    )]
1509    pub include_custom_instructions: BooleanDefaultFalse,
1510    #[cfg_attr(
1511        feature = "serde",
1512        serde(
1513            default = "BooleanDefaultFalse::none",
1514            skip_serializing_if = "BooleanDefaultFalse::is_none"
1515        )
1516    )]
1517    pub include_tags: BooleanDefaultFalse,
1518    #[cfg_attr(
1519        feature = "serde",
1520        serde(
1521            default = "BooleanDefaultFalse::none",
1522            skip_serializing_if = "BooleanDefaultFalse::is_none"
1523        )
1524    )]
1525    pub include_labels: BooleanDefaultFalse,
1526    #[cfg_attr(
1527        feature = "serde",
1528        serde(skip_serializing_if = "Option::is_none", default)
1529    )]
1530    pub limit: PositiveIntegerDefault10Max10000,
1531    #[cfg_attr(
1532        feature = "serde",
1533        serde(default, skip_serializing_if = "Option::is_none")
1534    )]
1535    pub offset: Option<PositiveIntegerOrZero>,
1536    #[cfg_attr(
1537        feature = "serde",
1538        serde(
1539            default = "BooleanDefaultTrue::none",
1540            skip_serializing_if = "BooleanDefaultTrue::is_none"
1541        )
1542    )]
1543    pub seek_permission: BooleanDefaultTrue,
1544}
1545
1546/// A wallet UTXO with its metadata.
1547#[derive(Clone, Debug)]
1548#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1549#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1550pub struct Output {
1551    pub satoshis: SatoshiValue,
1552    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
1553    #[cfg_attr(
1554        feature = "serde",
1555        serde(skip_serializing_if = "Option::is_none", default)
1556    )]
1557    pub locking_script: Option<Vec<u8>>,
1558    pub spendable: bool,
1559    #[cfg_attr(
1560        feature = "serde",
1561        serde(default, skip_serializing_if = "Option::is_none")
1562    )]
1563    pub custom_instructions: Option<String>,
1564    #[cfg_attr(
1565        feature = "serde",
1566        serde(skip_serializing_if = "Vec::is_empty", default)
1567    )]
1568    pub tags: Vec<String>,
1569    pub outpoint: OutpointString,
1570    #[cfg_attr(
1571        feature = "serde",
1572        serde(skip_serializing_if = "Vec::is_empty", default)
1573    )]
1574    pub labels: Vec<String>,
1575}
1576
1577/// Paginated list of wallet outputs.
1578#[derive(Clone, Debug)]
1579#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1580#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1581pub struct ListOutputsResult {
1582    pub total_outputs: u32,
1583    #[cfg_attr(
1584        feature = "serde",
1585        serde(with = "serde_helpers::option_bytes_as_array")
1586    )]
1587    #[cfg_attr(
1588        feature = "serde",
1589        serde(skip_serializing_if = "Option::is_none", default)
1590    )]
1591    #[cfg_attr(feature = "serde", serde(rename = "BEEF"))]
1592    pub beef: Option<Vec<u8>>,
1593    pub outputs: Vec<Output>,
1594}
1595
1596// ---------------------------------------------------------------------------
1597// RelinquishOutput
1598// ---------------------------------------------------------------------------
1599
1600/// Arguments for relinquishing ownership of an output.
1601#[derive(Clone, Debug)]
1602#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1603#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1604pub struct RelinquishOutputArgs {
1605    pub basket: BasketStringUnder300Bytes,
1606    pub output: OutpointString,
1607}
1608
1609/// Result of relinquishing an output.
1610#[derive(Clone, Debug)]
1611#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1612#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1613pub struct RelinquishOutputResult {
1614    pub relinquished: bool,
1615}
1616
1617// ---------------------------------------------------------------------------
1618// Key/Crypto types
1619// ---------------------------------------------------------------------------
1620
1621/// Arguments for getting a public key.
1622#[derive(Clone, Debug)]
1623#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1624#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1625pub struct GetPublicKeyArgs {
1626    // TS types this as `identityKey?: true` (literal `true | undefined`),
1627    // so the wire form is either present-and-true or absent. Skip when false
1628    // to match TS's `JSON.stringify(undefined)` omission.
1629    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
1630    pub identity_key: bool,
1631    #[cfg_attr(feature = "serde", serde(rename = "protocolID"))]
1632    #[cfg_attr(
1633        feature = "serde",
1634        serde(default, skip_serializing_if = "Option::is_none")
1635    )]
1636    pub protocol_id: Option<Protocol>,
1637    #[cfg_attr(feature = "serde", serde(rename = "keyID"))]
1638    #[cfg_attr(
1639        feature = "serde",
1640        serde(default, skip_serializing_if = "Option::is_none")
1641    )]
1642    pub key_id: Option<String>,
1643    #[cfg_attr(
1644        feature = "serde",
1645        serde(skip_serializing_if = "Option::is_none", default)
1646    )]
1647    pub counterparty: Option<Counterparty>,
1648    #[cfg_attr(feature = "serde", serde(default))]
1649    pub privileged: bool,
1650    #[cfg_attr(
1651        feature = "serde",
1652        serde(default, skip_serializing_if = "Option::is_none")
1653    )]
1654    pub privileged_reason: Option<String>,
1655    #[cfg_attr(
1656        feature = "serde",
1657        serde(skip_serializing_if = "Option::is_none", default)
1658    )]
1659    pub for_self: Option<bool>,
1660    #[cfg_attr(
1661        feature = "serde",
1662        serde(skip_serializing_if = "Option::is_none", default)
1663    )]
1664    pub seek_permission: Option<bool>,
1665}
1666
1667/// Result of getting a public key.
1668#[derive(Clone, Debug)]
1669#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1670#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1671pub struct GetPublicKeyResult {
1672    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
1673    pub public_key: PublicKey,
1674}
1675
1676/// Arguments for encryption.
1677#[derive(Clone, Debug)]
1678#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1679#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1680pub struct EncryptArgs {
1681    #[cfg_attr(feature = "serde", serde(rename = "protocolID"))]
1682    pub protocol_id: Protocol,
1683    #[cfg_attr(feature = "serde", serde(rename = "keyID"))]
1684    pub key_id: String,
1685    #[cfg_attr(feature = "serde", serde(default))]
1686    pub counterparty: Counterparty,
1687    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1688    pub plaintext: Vec<u8>,
1689    #[cfg_attr(feature = "serde", serde(default))]
1690    pub privileged: bool,
1691    #[cfg_attr(
1692        feature = "serde",
1693        serde(default, skip_serializing_if = "Option::is_none")
1694    )]
1695    pub privileged_reason: Option<String>,
1696    #[cfg_attr(
1697        feature = "serde",
1698        serde(default, skip_serializing_if = "Option::is_none")
1699    )]
1700    pub seek_permission: Option<bool>,
1701}
1702
1703/// Result of encryption.
1704#[derive(Clone, Debug)]
1705#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1706#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1707pub struct EncryptResult {
1708    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1709    pub ciphertext: Vec<u8>,
1710}
1711
1712/// Arguments for decryption.
1713#[derive(Clone, Debug)]
1714#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1715#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1716pub struct DecryptArgs {
1717    #[cfg_attr(feature = "serde", serde(rename = "protocolID"))]
1718    pub protocol_id: Protocol,
1719    #[cfg_attr(feature = "serde", serde(rename = "keyID"))]
1720    pub key_id: String,
1721    #[cfg_attr(feature = "serde", serde(default))]
1722    pub counterparty: Counterparty,
1723    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1724    pub ciphertext: Vec<u8>,
1725    #[cfg_attr(feature = "serde", serde(default))]
1726    pub privileged: bool,
1727    #[cfg_attr(
1728        feature = "serde",
1729        serde(default, skip_serializing_if = "Option::is_none")
1730    )]
1731    pub privileged_reason: Option<String>,
1732    #[cfg_attr(
1733        feature = "serde",
1734        serde(default, skip_serializing_if = "Option::is_none")
1735    )]
1736    pub seek_permission: Option<bool>,
1737}
1738
1739/// Result of decryption.
1740#[derive(Clone, Debug)]
1741#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1742#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1743pub struct DecryptResult {
1744    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1745    pub plaintext: Vec<u8>,
1746}
1747
1748/// Arguments for creating an HMAC.
1749#[derive(Clone, Debug)]
1750#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1751#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1752pub struct CreateHmacArgs {
1753    #[cfg_attr(feature = "serde", serde(rename = "protocolID"))]
1754    pub protocol_id: Protocol,
1755    #[cfg_attr(feature = "serde", serde(rename = "keyID"))]
1756    pub key_id: String,
1757    #[cfg_attr(feature = "serde", serde(default))]
1758    pub counterparty: Counterparty,
1759    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1760    pub data: Vec<u8>,
1761    #[cfg_attr(feature = "serde", serde(default))]
1762    pub privileged: bool,
1763    #[cfg_attr(
1764        feature = "serde",
1765        serde(default, skip_serializing_if = "Option::is_none")
1766    )]
1767    pub privileged_reason: Option<String>,
1768    #[cfg_attr(
1769        feature = "serde",
1770        serde(default, skip_serializing_if = "Option::is_none")
1771    )]
1772    pub seek_permission: Option<bool>,
1773}
1774
1775/// Result of creating an HMAC.
1776#[derive(Clone, Debug)]
1777#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1778#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1779pub struct CreateHmacResult {
1780    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1781    pub hmac: Vec<u8>,
1782}
1783
1784/// Arguments for verifying an HMAC.
1785#[derive(Clone, Debug)]
1786#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1787#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1788pub struct VerifyHmacArgs {
1789    #[cfg_attr(feature = "serde", serde(rename = "protocolID"))]
1790    pub protocol_id: Protocol,
1791    #[cfg_attr(feature = "serde", serde(rename = "keyID"))]
1792    pub key_id: String,
1793    #[cfg_attr(feature = "serde", serde(default))]
1794    pub counterparty: Counterparty,
1795    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1796    pub data: Vec<u8>,
1797    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1798    pub hmac: Vec<u8>,
1799    #[cfg_attr(feature = "serde", serde(default))]
1800    pub privileged: bool,
1801    #[cfg_attr(
1802        feature = "serde",
1803        serde(default, skip_serializing_if = "Option::is_none")
1804    )]
1805    pub privileged_reason: Option<String>,
1806    #[cfg_attr(
1807        feature = "serde",
1808        serde(default, skip_serializing_if = "Option::is_none")
1809    )]
1810    pub seek_permission: Option<bool>,
1811}
1812
1813/// Result of verifying an HMAC.
1814#[derive(Clone, Debug)]
1815#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1816#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1817pub struct VerifyHmacResult {
1818    pub valid: bool,
1819}
1820
1821/// Arguments for creating a digital signature.
1822#[derive(Clone, Debug)]
1823#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1824#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1825pub struct CreateSignatureArgs {
1826    #[cfg_attr(feature = "serde", serde(rename = "protocolID"))]
1827    pub protocol_id: Protocol,
1828    #[cfg_attr(feature = "serde", serde(rename = "keyID"))]
1829    pub key_id: String,
1830    #[cfg_attr(feature = "serde", serde(default))]
1831    pub counterparty: Counterparty,
1832    #[cfg_attr(
1833        feature = "serde",
1834        serde(with = "serde_helpers::option_bytes_as_array")
1835    )]
1836    #[cfg_attr(
1837        feature = "serde",
1838        serde(skip_serializing_if = "Option::is_none", default)
1839    )]
1840    pub data: Option<Vec<u8>>,
1841    #[cfg_attr(
1842        feature = "serde",
1843        serde(with = "serde_helpers::option_bytes_as_array")
1844    )]
1845    #[cfg_attr(
1846        feature = "serde",
1847        serde(skip_serializing_if = "Option::is_none", default)
1848    )]
1849    pub hash_to_directly_sign: Option<Vec<u8>>,
1850    #[cfg_attr(feature = "serde", serde(default))]
1851    pub privileged: bool,
1852    #[cfg_attr(
1853        feature = "serde",
1854        serde(default, skip_serializing_if = "Option::is_none")
1855    )]
1856    pub privileged_reason: Option<String>,
1857    #[cfg_attr(
1858        feature = "serde",
1859        serde(default, skip_serializing_if = "Option::is_none")
1860    )]
1861    pub seek_permission: Option<bool>,
1862}
1863
1864/// Result of creating a digital signature.
1865#[derive(Clone, Debug)]
1866#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1867#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1868pub struct CreateSignatureResult {
1869    // bytes_as_array: TS SDK returns Byte[] (number array), Go SDK uses BytesList.
1870    // NOT bytes_as_hex — BSV Desktop JSON API returns [48, 69, ...] not "3045...".
1871    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1872    pub signature: Vec<u8>,
1873}
1874
1875/// Arguments for verifying a digital signature.
1876#[derive(Clone, Debug)]
1877#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1878#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1879pub struct VerifySignatureArgs {
1880    #[cfg_attr(feature = "serde", serde(rename = "protocolID"))]
1881    pub protocol_id: Protocol,
1882    #[cfg_attr(feature = "serde", serde(rename = "keyID"))]
1883    pub key_id: String,
1884    #[cfg_attr(feature = "serde", serde(default))]
1885    pub counterparty: Counterparty,
1886    #[cfg_attr(
1887        feature = "serde",
1888        serde(with = "serde_helpers::option_bytes_as_array")
1889    )]
1890    #[cfg_attr(
1891        feature = "serde",
1892        serde(skip_serializing_if = "Option::is_none", default)
1893    )]
1894    pub data: Option<Vec<u8>>,
1895    #[cfg_attr(
1896        feature = "serde",
1897        serde(with = "serde_helpers::option_bytes_as_array")
1898    )]
1899    #[cfg_attr(
1900        feature = "serde",
1901        serde(skip_serializing_if = "Option::is_none", default)
1902    )]
1903    pub hash_to_directly_verify: Option<Vec<u8>>,
1904    // bytes_as_array: matches TS Byte[] and Go BytesList format.
1905    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
1906    pub signature: Vec<u8>,
1907    #[cfg_attr(
1908        feature = "serde",
1909        serde(default, skip_serializing_if = "Option::is_none")
1910    )]
1911    pub for_self: Option<bool>,
1912    #[cfg_attr(feature = "serde", serde(default))]
1913    pub privileged: bool,
1914    #[cfg_attr(
1915        feature = "serde",
1916        serde(default, skip_serializing_if = "Option::is_none")
1917    )]
1918    pub privileged_reason: Option<String>,
1919    #[cfg_attr(
1920        feature = "serde",
1921        serde(default, skip_serializing_if = "Option::is_none")
1922    )]
1923    pub seek_permission: Option<bool>,
1924}
1925
1926/// Result of verifying a digital signature.
1927#[derive(Clone, Debug)]
1928#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1929#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1930pub struct VerifySignatureResult {
1931    pub valid: bool,
1932}
1933
1934// ---------------------------------------------------------------------------
1935// Certificate operations
1936// ---------------------------------------------------------------------------
1937
1938/// Arguments for acquiring a new certificate.
1939#[derive(Clone, Debug)]
1940#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1941#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1942pub struct AcquireCertificateArgs {
1943    #[cfg_attr(feature = "serde", serde(rename = "type"))]
1944    pub cert_type: CertificateType,
1945    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
1946    pub certifier: PublicKey,
1947    pub acquisition_protocol: AcquisitionProtocol,
1948    #[cfg_attr(
1949        feature = "serde",
1950        serde(skip_serializing_if = "HashMap::is_empty", default)
1951    )]
1952    pub fields: HashMap<String, String>,
1953    #[cfg_attr(
1954        feature = "serde",
1955        serde(default, skip_serializing_if = "Option::is_none")
1956    )]
1957    pub serial_number: Option<SerialNumber>,
1958    #[cfg_attr(
1959        feature = "serde",
1960        serde(default, skip_serializing_if = "Option::is_none")
1961    )]
1962    pub revocation_outpoint: Option<String>,
1963    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
1964    #[cfg_attr(
1965        feature = "serde",
1966        serde(skip_serializing_if = "Option::is_none", default)
1967    )]
1968    pub signature: Option<Vec<u8>>,
1969    #[cfg_attr(
1970        feature = "serde",
1971        serde(default, skip_serializing_if = "Option::is_none")
1972    )]
1973    pub certifier_url: Option<String>,
1974    #[cfg_attr(
1975        feature = "serde",
1976        serde(default, skip_serializing_if = "Option::is_none")
1977    )]
1978    pub keyring_revealer: Option<KeyringRevealer>,
1979    #[cfg_attr(
1980        feature = "serde",
1981        serde(default, skip_serializing_if = "Option::is_none")
1982    )]
1983    pub keyring_for_subject: Option<HashMap<String, String>>,
1984    #[cfg_attr(feature = "serde", serde(default))]
1985    pub privileged: bool,
1986    #[cfg_attr(
1987        feature = "serde",
1988        serde(default, skip_serializing_if = "Option::is_none")
1989    )]
1990    pub privileged_reason: Option<String>,
1991}
1992
1993/// Arguments for listing certificates.
1994#[derive(Clone, Debug)]
1995#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1996#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1997pub struct ListCertificatesArgs {
1998    #[cfg_attr(
1999        feature = "serde",
2000        serde(with = "serde_helpers::vec_public_key_hex", default)
2001    )]
2002    pub certifiers: Vec<PublicKey>,
2003    #[cfg_attr(
2004        feature = "serde",
2005        serde(skip_serializing_if = "Vec::is_empty", default)
2006    )]
2007    pub types: Vec<CertificateType>,
2008    #[cfg_attr(feature = "serde", serde(default))]
2009    pub limit: PositiveIntegerDefault10Max10000,
2010    #[cfg_attr(
2011        feature = "serde",
2012        serde(default, skip_serializing_if = "Option::is_none")
2013    )]
2014    pub offset: Option<PositiveIntegerOrZero>,
2015    #[cfg_attr(
2016        feature = "serde",
2017        serde(
2018            default = "BooleanDefaultFalse::none",
2019            skip_serializing_if = "BooleanDefaultFalse::is_none"
2020        )
2021    )]
2022    pub privileged: BooleanDefaultFalse,
2023    #[cfg_attr(
2024        feature = "serde",
2025        serde(default, skip_serializing_if = "Option::is_none")
2026    )]
2027    pub privileged_reason: Option<String>,
2028    /// Optional partial certificate filter for exact matching.
2029    /// When provided, only certificates matching these fields are returned.
2030    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
2031    pub partial: Option<PartialCertificate>,
2032}
2033
2034/// A certificate with its keyring and verifier.
2035#[derive(Clone, Debug)]
2036#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2037#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2038pub struct CertificateResult {
2039    #[cfg_attr(feature = "serde", serde(flatten))]
2040    pub certificate: Certificate,
2041    #[cfg_attr(
2042        feature = "serde",
2043        serde(default, skip_serializing_if = "Option::is_none")
2044    )]
2045    pub keyring: Option<HashMap<String, String>>,
2046    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::option_bytes_as_hex"))]
2047    #[cfg_attr(
2048        feature = "serde",
2049        serde(skip_serializing_if = "Option::is_none", default)
2050    )]
2051    pub verifier: Option<Vec<u8>>,
2052}
2053
2054/// Paginated list of certificates.
2055#[derive(Clone, Debug)]
2056#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2057#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2058pub struct ListCertificatesResult {
2059    pub total_certificates: u32,
2060    pub certificates: Vec<CertificateResult>,
2061}
2062
2063/// Arguments for creating a verifiable certificate.
2064#[derive(Clone, Debug)]
2065#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2066#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2067pub struct ProveCertificateArgs {
2068    pub certificate: PartialCertificate,
2069    pub fields_to_reveal: Vec<String>,
2070    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2071    pub verifier: PublicKey,
2072    #[cfg_attr(
2073        feature = "serde",
2074        serde(
2075            default = "BooleanDefaultFalse::none",
2076            skip_serializing_if = "BooleanDefaultFalse::is_none"
2077        )
2078    )]
2079    pub privileged: BooleanDefaultFalse,
2080    #[cfg_attr(
2081        feature = "serde",
2082        serde(default, skip_serializing_if = "Option::is_none")
2083    )]
2084    pub privileged_reason: Option<String>,
2085}
2086
2087/// Result of creating a verifiable certificate.
2088#[derive(Clone, Debug)]
2089#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2090#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2091pub struct ProveCertificateResult {
2092    pub keyring_for_verifier: HashMap<String, String>,
2093    #[cfg_attr(
2094        feature = "serde",
2095        serde(default, skip_serializing_if = "Option::is_none")
2096    )]
2097    pub certificate: Option<Certificate>,
2098    #[cfg_attr(
2099        feature = "serde",
2100        serde(with = "serde_helpers::option_public_key_hex")
2101    )]
2102    #[cfg_attr(feature = "serde", serde(default))]
2103    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
2104    pub verifier: Option<PublicKey>,
2105}
2106
2107/// Arguments for relinquishing ownership of a certificate.
2108#[derive(Clone, Debug)]
2109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2110#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2111pub struct RelinquishCertificateArgs {
2112    #[cfg_attr(feature = "serde", serde(rename = "type"))]
2113    pub cert_type: CertificateType,
2114    pub serial_number: SerialNumber,
2115    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2116    pub certifier: PublicKey,
2117}
2118
2119/// Result of relinquishing a certificate.
2120#[derive(Clone, Debug)]
2121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2122#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2123pub struct RelinquishCertificateResult {
2124    pub relinquished: bool,
2125}
2126
2127// ---------------------------------------------------------------------------
2128// Discovery types
2129// ---------------------------------------------------------------------------
2130
2131/// Information about an entity that issues identity certificates.
2132#[derive(Clone, Debug)]
2133#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2134#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2135pub struct IdentityCertifier {
2136    pub name: String,
2137    pub icon_url: String,
2138    pub description: String,
2139    pub trust: u8,
2140}
2141
2142/// An identity certificate with decoded fields and certifier info.
2143#[derive(Clone, Debug)]
2144#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2145#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2146pub struct IdentityCertificate {
2147    #[cfg_attr(feature = "serde", serde(flatten))]
2148    pub certificate: Certificate,
2149    pub certifier_info: IdentityCertifier,
2150    pub publicly_revealed_keyring: HashMap<String, String>,
2151    pub decrypted_fields: HashMap<String, String>,
2152}
2153
2154/// Arguments for discovering certificates by identity key.
2155#[derive(Clone, Debug)]
2156#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2157#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2158pub struct DiscoverByIdentityKeyArgs {
2159    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2160    pub identity_key: PublicKey,
2161    #[cfg_attr(
2162        feature = "serde",
2163        serde(default, skip_serializing_if = "Option::is_none")
2164    )]
2165    pub limit: Option<u32>,
2166    #[cfg_attr(
2167        feature = "serde",
2168        serde(default, skip_serializing_if = "Option::is_none")
2169    )]
2170    pub offset: Option<u32>,
2171    #[cfg_attr(
2172        feature = "serde",
2173        serde(default, skip_serializing_if = "Option::is_none")
2174    )]
2175    pub seek_permission: Option<bool>,
2176}
2177
2178/// Arguments for discovering certificates by attributes.
2179#[derive(Clone, Debug)]
2180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2181#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2182pub struct DiscoverByAttributesArgs {
2183    pub attributes: HashMap<String, String>,
2184    #[cfg_attr(
2185        feature = "serde",
2186        serde(default, skip_serializing_if = "Option::is_none")
2187    )]
2188    pub limit: Option<u32>,
2189    #[cfg_attr(
2190        feature = "serde",
2191        serde(default, skip_serializing_if = "Option::is_none")
2192    )]
2193    pub offset: Option<u32>,
2194    #[cfg_attr(
2195        feature = "serde",
2196        serde(default, skip_serializing_if = "Option::is_none")
2197    )]
2198    pub seek_permission: Option<bool>,
2199}
2200
2201/// Paginated list of identity certificates found during discovery.
2202#[derive(Clone, Debug)]
2203#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2204#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2205pub struct DiscoverCertificatesResult {
2206    pub total_certificates: u32,
2207    pub certificates: Vec<IdentityCertificate>,
2208}
2209
2210// ---------------------------------------------------------------------------
2211// Key linkage types
2212// ---------------------------------------------------------------------------
2213
2214/// Arguments for revealing key linkage between counterparties.
2215#[derive(Clone, Debug)]
2216#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2217#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2218pub struct RevealCounterpartyKeyLinkageArgs {
2219    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2220    pub counterparty: PublicKey,
2221    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2222    pub verifier: PublicKey,
2223    #[cfg_attr(
2224        feature = "serde",
2225        serde(default, skip_serializing_if = "Option::is_none")
2226    )]
2227    pub privileged: Option<bool>,
2228    #[cfg_attr(
2229        feature = "serde",
2230        serde(default, skip_serializing_if = "Option::is_none")
2231    )]
2232    pub privileged_reason: Option<String>,
2233}
2234
2235/// Result of revealing counterparty key linkage.
2236#[derive(Clone, Debug)]
2237#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2238#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2239pub struct RevealCounterpartyKeyLinkageResult {
2240    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2241    pub prover: PublicKey,
2242    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2243    pub counterparty: PublicKey,
2244    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2245    pub verifier: PublicKey,
2246    pub revelation_time: String,
2247    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
2248    pub encrypted_linkage: Vec<u8>,
2249    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
2250    pub encrypted_linkage_proof: Vec<u8>,
2251}
2252
2253/// Arguments for revealing specific key linkage.
2254#[derive(Clone, Debug)]
2255#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2256#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2257pub struct RevealSpecificKeyLinkageArgs {
2258    pub counterparty: Counterparty,
2259    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2260    pub verifier: PublicKey,
2261    #[cfg_attr(feature = "serde", serde(rename = "protocolID"))]
2262    pub protocol_id: Protocol,
2263    #[cfg_attr(feature = "serde", serde(rename = "keyID"))]
2264    pub key_id: String,
2265    #[cfg_attr(
2266        feature = "serde",
2267        serde(default, skip_serializing_if = "Option::is_none")
2268    )]
2269    pub privileged: Option<bool>,
2270    #[cfg_attr(
2271        feature = "serde",
2272        serde(default, skip_serializing_if = "Option::is_none")
2273    )]
2274    pub privileged_reason: Option<String>,
2275}
2276
2277/// Result of revealing specific key linkage.
2278#[derive(Clone, Debug)]
2279#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2280#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2281pub struct RevealSpecificKeyLinkageResult {
2282    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
2283    pub encrypted_linkage: Vec<u8>,
2284    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_array"))]
2285    pub encrypted_linkage_proof: Vec<u8>,
2286    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2287    pub prover: PublicKey,
2288    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2289    pub verifier: PublicKey,
2290    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::public_key_hex"))]
2291    pub counterparty: PublicKey,
2292    #[cfg_attr(feature = "serde", serde(rename = "protocolID"))]
2293    pub protocol_id: Protocol,
2294    #[cfg_attr(feature = "serde", serde(rename = "keyID"))]
2295    pub key_id: String,
2296    pub proof_type: u8,
2297}
2298
2299// ---------------------------------------------------------------------------
2300// Auth/Info types
2301// ---------------------------------------------------------------------------
2302
2303/// Whether the current session is authenticated.
2304#[derive(Clone, Debug)]
2305#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2306#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2307pub struct AuthenticatedResult {
2308    pub authenticated: bool,
2309}
2310
2311/// Current blockchain height.
2312#[derive(Clone, Debug)]
2313#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2314#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2315pub struct GetHeightResult {
2316    pub height: u32,
2317}
2318
2319/// Arguments for retrieving a blockchain header at a specific height.
2320#[derive(Clone, Debug)]
2321#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2322#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2323pub struct GetHeaderArgs {
2324    pub height: u32,
2325}
2326
2327/// Blockchain header data for the requested height.
2328#[derive(Clone, Debug)]
2329#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2330#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2331pub struct GetHeaderResult {
2332    #[cfg_attr(feature = "serde", serde(with = "serde_helpers::bytes_as_hex"))]
2333    pub header: Vec<u8>,
2334}
2335
2336/// Current blockchain network.
2337#[derive(Clone, Debug)]
2338#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2339#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2340pub struct GetNetworkResult {
2341    pub network: Network,
2342}
2343
2344/// Version information about the wallet implementation.
2345#[derive(Clone, Debug)]
2346#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2347#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2348pub struct GetVersionResult {
2349    pub version: String,
2350}
2351
2352// ---------------------------------------------------------------------------
2353// WalletInterface trait
2354// ---------------------------------------------------------------------------
2355
2356/// The core wallet interface with all 28 async methods.
2357///
2358/// Uses `#[async_trait]` for object safety -- supports both static dispatch
2359/// (`W: WalletInterface`) and dynamic dispatch (`dyn WalletInterface`).
2360///
2361/// Every method takes `originator: Option<&str>` as the last parameter,
2362/// identifying the calling application domain.
2363#[async_trait]
2364pub trait WalletInterface: Send + Sync {
2365    // -- Action methods --
2366
2367    async fn create_action(
2368        &self,
2369        args: CreateActionArgs,
2370        originator: Option<&str>,
2371    ) -> Result<CreateActionResult, WalletError>;
2372
2373    async fn sign_action(
2374        &self,
2375        args: SignActionArgs,
2376        originator: Option<&str>,
2377    ) -> Result<SignActionResult, WalletError>;
2378
2379    async fn abort_action(
2380        &self,
2381        args: AbortActionArgs,
2382        originator: Option<&str>,
2383    ) -> Result<AbortActionResult, WalletError>;
2384
2385    async fn list_actions(
2386        &self,
2387        args: ListActionsArgs,
2388        originator: Option<&str>,
2389    ) -> Result<ListActionsResult, WalletError>;
2390
2391    async fn internalize_action(
2392        &self,
2393        args: InternalizeActionArgs,
2394        originator: Option<&str>,
2395    ) -> Result<InternalizeActionResult, WalletError>;
2396
2397    // -- Output methods --
2398
2399    async fn list_outputs(
2400        &self,
2401        args: ListOutputsArgs,
2402        originator: Option<&str>,
2403    ) -> Result<ListOutputsResult, WalletError>;
2404
2405    async fn relinquish_output(
2406        &self,
2407        args: RelinquishOutputArgs,
2408        originator: Option<&str>,
2409    ) -> Result<RelinquishOutputResult, WalletError>;
2410
2411    // -- Key/Crypto methods --
2412
2413    async fn get_public_key(
2414        &self,
2415        args: GetPublicKeyArgs,
2416        originator: Option<&str>,
2417    ) -> Result<GetPublicKeyResult, WalletError>;
2418
2419    async fn reveal_counterparty_key_linkage(
2420        &self,
2421        args: RevealCounterpartyKeyLinkageArgs,
2422        originator: Option<&str>,
2423    ) -> Result<RevealCounterpartyKeyLinkageResult, WalletError>;
2424
2425    async fn reveal_specific_key_linkage(
2426        &self,
2427        args: RevealSpecificKeyLinkageArgs,
2428        originator: Option<&str>,
2429    ) -> Result<RevealSpecificKeyLinkageResult, WalletError>;
2430
2431    async fn encrypt(
2432        &self,
2433        args: EncryptArgs,
2434        originator: Option<&str>,
2435    ) -> Result<EncryptResult, WalletError>;
2436
2437    async fn decrypt(
2438        &self,
2439        args: DecryptArgs,
2440        originator: Option<&str>,
2441    ) -> Result<DecryptResult, WalletError>;
2442
2443    async fn create_hmac(
2444        &self,
2445        args: CreateHmacArgs,
2446        originator: Option<&str>,
2447    ) -> Result<CreateHmacResult, WalletError>;
2448
2449    async fn verify_hmac(
2450        &self,
2451        args: VerifyHmacArgs,
2452        originator: Option<&str>,
2453    ) -> Result<VerifyHmacResult, WalletError>;
2454
2455    async fn create_signature(
2456        &self,
2457        args: CreateSignatureArgs,
2458        originator: Option<&str>,
2459    ) -> Result<CreateSignatureResult, WalletError>;
2460
2461    async fn verify_signature(
2462        &self,
2463        args: VerifySignatureArgs,
2464        originator: Option<&str>,
2465    ) -> Result<VerifySignatureResult, WalletError>;
2466
2467    // -- Certificate methods --
2468
2469    async fn acquire_certificate(
2470        &self,
2471        args: AcquireCertificateArgs,
2472        originator: Option<&str>,
2473    ) -> Result<Certificate, WalletError>;
2474
2475    async fn list_certificates(
2476        &self,
2477        args: ListCertificatesArgs,
2478        originator: Option<&str>,
2479    ) -> Result<ListCertificatesResult, WalletError>;
2480
2481    async fn prove_certificate(
2482        &self,
2483        args: ProveCertificateArgs,
2484        originator: Option<&str>,
2485    ) -> Result<ProveCertificateResult, WalletError>;
2486
2487    async fn relinquish_certificate(
2488        &self,
2489        args: RelinquishCertificateArgs,
2490        originator: Option<&str>,
2491    ) -> Result<RelinquishCertificateResult, WalletError>;
2492
2493    // -- Discovery methods --
2494
2495    async fn discover_by_identity_key(
2496        &self,
2497        args: DiscoverByIdentityKeyArgs,
2498        originator: Option<&str>,
2499    ) -> Result<DiscoverCertificatesResult, WalletError>;
2500
2501    async fn discover_by_attributes(
2502        &self,
2503        args: DiscoverByAttributesArgs,
2504        originator: Option<&str>,
2505    ) -> Result<DiscoverCertificatesResult, WalletError>;
2506
2507    // -- Auth/Info methods --
2508
2509    async fn is_authenticated(
2510        &self,
2511        originator: Option<&str>,
2512    ) -> Result<AuthenticatedResult, WalletError>;
2513
2514    async fn wait_for_authentication(
2515        &self,
2516        originator: Option<&str>,
2517    ) -> Result<AuthenticatedResult, WalletError>;
2518
2519    async fn get_height(&self, originator: Option<&str>) -> Result<GetHeightResult, WalletError>;
2520
2521    async fn get_header_for_height(
2522        &self,
2523        args: GetHeaderArgs,
2524        originator: Option<&str>,
2525    ) -> Result<GetHeaderResult, WalletError>;
2526
2527    async fn get_network(&self, originator: Option<&str>) -> Result<GetNetworkResult, WalletError>;
2528
2529    async fn get_version(&self, originator: Option<&str>) -> Result<GetVersionResult, WalletError>;
2530}
2531
2532/// Blanket impl so `Arc<W>` is itself a `WalletInterface`.
2533///
2534/// # Why this exists
2535///
2536/// In the TS SDK a wallet is passed by reference — `new AuthFetch(wallet)`
2537/// imposes nothing on the caller. The Rust translation of that call site is
2538/// `AuthFetch<W: WalletInterface + Clone + 'static>`, and the natural wallet to
2539/// hand it, [`ProtoWallet`](crate::wallet::proto_wallet::ProtoWallet), is not
2540/// `Clone`: its `KeyDeriver` owns an `RwLock` shared-secret cache, and that
2541/// type's own documentation says it is *"`Send + Sync`, safe to share via
2542/// `Arc`"*. Arc-sharing is the intended mechanism; the `Clone` bound was the
2543/// only thing standing between callers and it.
2544///
2545/// Without this impl every consumer writes the same newtype-over-`Arc` plus a
2546/// 28-method delegation block just to satisfy the bound. That happened three
2547/// separate times in one downstream repo alone — ~750 lines of pure ceremony,
2548/// none of it copy-paste drift, all of it patching this one gap.
2549///
2550/// With it, `Arc<ProtoWallet>` satisfies `WalletInterface + Clone` directly and
2551/// those wrappers delete. No existing impl changes meaning: a type that was
2552/// already `WalletInterface + Clone` keeps working exactly as before.
2553#[async_trait]
2554impl<W: WalletInterface + ?Sized> WalletInterface for std::sync::Arc<W> {
2555    async fn create_action(
2556        &self,
2557        args: CreateActionArgs,
2558        originator: Option<&str>,
2559    ) -> Result<CreateActionResult, WalletError> {
2560        (**self).create_action(args, originator).await
2561    }
2562
2563    async fn sign_action(
2564        &self,
2565        args: SignActionArgs,
2566        originator: Option<&str>,
2567    ) -> Result<SignActionResult, WalletError> {
2568        (**self).sign_action(args, originator).await
2569    }
2570
2571    async fn abort_action(
2572        &self,
2573        args: AbortActionArgs,
2574        originator: Option<&str>,
2575    ) -> Result<AbortActionResult, WalletError> {
2576        (**self).abort_action(args, originator).await
2577    }
2578
2579    async fn list_actions(
2580        &self,
2581        args: ListActionsArgs,
2582        originator: Option<&str>,
2583    ) -> Result<ListActionsResult, WalletError> {
2584        (**self).list_actions(args, originator).await
2585    }
2586
2587    async fn internalize_action(
2588        &self,
2589        args: InternalizeActionArgs,
2590        originator: Option<&str>,
2591    ) -> Result<InternalizeActionResult, WalletError> {
2592        (**self).internalize_action(args, originator).await
2593    }
2594
2595    async fn list_outputs(
2596        &self,
2597        args: ListOutputsArgs,
2598        originator: Option<&str>,
2599    ) -> Result<ListOutputsResult, WalletError> {
2600        (**self).list_outputs(args, originator).await
2601    }
2602
2603    async fn relinquish_output(
2604        &self,
2605        args: RelinquishOutputArgs,
2606        originator: Option<&str>,
2607    ) -> Result<RelinquishOutputResult, WalletError> {
2608        (**self).relinquish_output(args, originator).await
2609    }
2610
2611    async fn get_public_key(
2612        &self,
2613        args: GetPublicKeyArgs,
2614        originator: Option<&str>,
2615    ) -> Result<GetPublicKeyResult, WalletError> {
2616        (**self).get_public_key(args, originator).await
2617    }
2618
2619    async fn reveal_counterparty_key_linkage(
2620        &self,
2621        args: RevealCounterpartyKeyLinkageArgs,
2622        originator: Option<&str>,
2623    ) -> Result<RevealCounterpartyKeyLinkageResult, WalletError> {
2624        (**self)
2625            .reveal_counterparty_key_linkage(args, originator)
2626            .await
2627    }
2628
2629    async fn reveal_specific_key_linkage(
2630        &self,
2631        args: RevealSpecificKeyLinkageArgs,
2632        originator: Option<&str>,
2633    ) -> Result<RevealSpecificKeyLinkageResult, WalletError> {
2634        (**self).reveal_specific_key_linkage(args, originator).await
2635    }
2636
2637    async fn encrypt(
2638        &self,
2639        args: EncryptArgs,
2640        originator: Option<&str>,
2641    ) -> Result<EncryptResult, WalletError> {
2642        (**self).encrypt(args, originator).await
2643    }
2644
2645    async fn decrypt(
2646        &self,
2647        args: DecryptArgs,
2648        originator: Option<&str>,
2649    ) -> Result<DecryptResult, WalletError> {
2650        (**self).decrypt(args, originator).await
2651    }
2652
2653    async fn create_hmac(
2654        &self,
2655        args: CreateHmacArgs,
2656        originator: Option<&str>,
2657    ) -> Result<CreateHmacResult, WalletError> {
2658        (**self).create_hmac(args, originator).await
2659    }
2660
2661    async fn verify_hmac(
2662        &self,
2663        args: VerifyHmacArgs,
2664        originator: Option<&str>,
2665    ) -> Result<VerifyHmacResult, WalletError> {
2666        (**self).verify_hmac(args, originator).await
2667    }
2668
2669    async fn create_signature(
2670        &self,
2671        args: CreateSignatureArgs,
2672        originator: Option<&str>,
2673    ) -> Result<CreateSignatureResult, WalletError> {
2674        (**self).create_signature(args, originator).await
2675    }
2676
2677    async fn verify_signature(
2678        &self,
2679        args: VerifySignatureArgs,
2680        originator: Option<&str>,
2681    ) -> Result<VerifySignatureResult, WalletError> {
2682        (**self).verify_signature(args, originator).await
2683    }
2684
2685    async fn acquire_certificate(
2686        &self,
2687        args: AcquireCertificateArgs,
2688        originator: Option<&str>,
2689    ) -> Result<Certificate, WalletError> {
2690        (**self).acquire_certificate(args, originator).await
2691    }
2692
2693    async fn list_certificates(
2694        &self,
2695        args: ListCertificatesArgs,
2696        originator: Option<&str>,
2697    ) -> Result<ListCertificatesResult, WalletError> {
2698        (**self).list_certificates(args, originator).await
2699    }
2700
2701    async fn prove_certificate(
2702        &self,
2703        args: ProveCertificateArgs,
2704        originator: Option<&str>,
2705    ) -> Result<ProveCertificateResult, WalletError> {
2706        (**self).prove_certificate(args, originator).await
2707    }
2708
2709    async fn relinquish_certificate(
2710        &self,
2711        args: RelinquishCertificateArgs,
2712        originator: Option<&str>,
2713    ) -> Result<RelinquishCertificateResult, WalletError> {
2714        (**self).relinquish_certificate(args, originator).await
2715    }
2716
2717    async fn discover_by_identity_key(
2718        &self,
2719        args: DiscoverByIdentityKeyArgs,
2720        originator: Option<&str>,
2721    ) -> Result<DiscoverCertificatesResult, WalletError> {
2722        (**self).discover_by_identity_key(args, originator).await
2723    }
2724
2725    async fn discover_by_attributes(
2726        &self,
2727        args: DiscoverByAttributesArgs,
2728        originator: Option<&str>,
2729    ) -> Result<DiscoverCertificatesResult, WalletError> {
2730        (**self).discover_by_attributes(args, originator).await
2731    }
2732
2733    async fn is_authenticated(
2734        &self,
2735        originator: Option<&str>,
2736    ) -> Result<AuthenticatedResult, WalletError> {
2737        (**self).is_authenticated(originator).await
2738    }
2739
2740    async fn wait_for_authentication(
2741        &self,
2742        originator: Option<&str>,
2743    ) -> Result<AuthenticatedResult, WalletError> {
2744        (**self).wait_for_authentication(originator).await
2745    }
2746
2747    async fn get_height(&self, originator: Option<&str>) -> Result<GetHeightResult, WalletError> {
2748        (**self).get_height(originator).await
2749    }
2750
2751    async fn get_header_for_height(
2752        &self,
2753        args: GetHeaderArgs,
2754        originator: Option<&str>,
2755    ) -> Result<GetHeaderResult, WalletError> {
2756        (**self).get_header_for_height(args, originator).await
2757    }
2758
2759    async fn get_network(&self, originator: Option<&str>) -> Result<GetNetworkResult, WalletError> {
2760        (**self).get_network(originator).await
2761    }
2762
2763    async fn get_version(&self, originator: Option<&str>) -> Result<GetVersionResult, WalletError> {
2764        (**self).get_version(originator).await
2765    }
2766}
2767
2768#[cfg(all(test, feature = "serde"))]
2769mod certificate_serde_tests {
2770    //! An UNSIGNED certificate must survive a JSON round-trip.
2771    //!
2772    //! Every optional field is `skip_serializing_if = "Option::is_none"`, so a `None`
2773    //! is OMITTED from the JSON. Without a matching `default`, deserialization then
2774    //! rejects the exact document serialization just produced. That is not a corner
2775    //! case: `signature: None` is the state of every certificate that is being
2776    //! presented to a signer, which is the one time a cert MUST cross a wire.
2777
2778    use super::*;
2779    use crate::primitives::private_key::PrivateKey;
2780
2781    fn unsigned_cert() -> Certificate {
2782        Certificate {
2783            cert_type: CertificateType([0x01; 32]),
2784            serial_number: SerialNumber([0x02; 32]),
2785            subject: PrivateKey::from_random().unwrap().to_public_key(),
2786            certifier: PrivateKey::from_random().unwrap().to_public_key(),
2787            revocation_outpoint: None,
2788            fields: None,
2789            signature: None,
2790        }
2791    }
2792
2793    #[test]
2794    fn unsigned_certificate_round_trips() {
2795        let cert = unsigned_cert();
2796        let json = serde_json::to_string(&cert).expect("serialize");
2797        let back: Certificate = serde_json::from_str(&json).expect(
2798            "an unsigned certificate must deserialize from its own serialization — a signer \
2799             cannot be handed a cert body it is unable to parse",
2800        );
2801        assert_eq!(back.cert_type.0, cert.cert_type.0);
2802        assert_eq!(back.serial_number.0, cert.serial_number.0);
2803        assert_eq!(back.subject, cert.subject);
2804        assert_eq!(back.certifier, cert.certifier);
2805        assert!(back.revocation_outpoint.is_none());
2806        assert!(back.fields.is_none());
2807        assert!(back.signature.is_none());
2808    }
2809
2810    #[test]
2811    fn absent_optional_keys_deserialize_as_none() {
2812        // The omitted keys really are absent — this is what the signer receives.
2813        let json = serde_json::to_string(&unsigned_cert()).unwrap();
2814        assert!(!json.contains("signature"), "None must be omitted: {json}");
2815        assert!(!json.contains("revocationOutpoint"), "None must be omitted");
2816        assert!(!json.contains("fields"), "None must be omitted");
2817
2818        let back: Certificate = serde_json::from_str(&json).unwrap();
2819        assert!(back.signature.is_none());
2820    }
2821
2822    #[test]
2823    fn a_populated_certificate_still_round_trips() {
2824        // The default must not swallow real values.
2825        let mut fields = HashMap::new();
2826        fields.insert("role".to_string(), "admin".to_string());
2827        let cert = Certificate {
2828            revocation_outpoint: Some("aa".repeat(32) + ".1"),
2829            fields: Some(fields),
2830            signature: Some(vec![0xde, 0xad, 0xbe, 0xef]),
2831            ..unsigned_cert()
2832        };
2833        let json = serde_json::to_string(&cert).unwrap();
2834        let back: Certificate = serde_json::from_str(&json).unwrap();
2835        assert_eq!(back.revocation_outpoint, cert.revocation_outpoint);
2836        assert_eq!(back.fields, cert.fields);
2837        assert_eq!(back.signature, Some(vec![0xde, 0xad, 0xbe, 0xef]));
2838    }
2839}
2840
2841#[cfg(test)]
2842mod tests {
2843    use super::*;
2844
2845    #[test]
2846    fn test_serial_number_from_string_hex_valid() {
2847        let hex = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2";
2848        let sn = SerialNumber::from_string(hex).unwrap();
2849        assert_eq!(sn.0[0], 0xa1);
2850        assert_eq!(sn.0[31], 0xb2);
2851    }
2852
2853    #[test]
2854    fn test_serial_number_from_string_base64_valid() {
2855        // 32 bytes of zeros -> base64 is "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
2856        let b64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
2857        let sn = SerialNumber::from_string(b64).unwrap();
2858        assert_eq!(sn.0, [0u8; 32]);
2859    }
2860
2861    #[test]
2862    fn test_serial_number_from_string_base64_nonzero() {
2863        // All 0xFF bytes: base64 = "//////////////////////////////////////////8="
2864        let b64 = "//////////////////////////////////////////8=";
2865        let sn = SerialNumber::from_string(b64).unwrap();
2866        assert_eq!(sn.0, [0xffu8; 32]);
2867    }
2868
2869    #[test]
2870    fn test_serial_number_from_string_invalid_length() {
2871        assert!(SerialNumber::from_string("abc").is_err());
2872        assert!(SerialNumber::from_string("").is_err());
2873        assert!(SerialNumber::from_string("a1b2c3").is_err());
2874    }
2875
2876    #[test]
2877    fn test_serial_number_from_string_invalid_chars() {
2878        // 64 chars but not valid hex
2879        let bad_hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
2880        assert!(SerialNumber::from_string(bad_hex).is_err());
2881    }
2882}
2883
2884#[cfg(test)]
2885mod review_action_result_tests {
2886    use super::*;
2887
2888    #[test]
2889    fn test_review_action_result_status_values() {
2890        assert_eq!(ReviewActionResultStatus::Success.as_str(), "success");
2891        assert_eq!(
2892            ReviewActionResultStatus::DoubleSpend.as_str(),
2893            "doubleSpend"
2894        );
2895        assert_eq!(
2896            ReviewActionResultStatus::ServiceError.as_str(),
2897            "serviceError"
2898        );
2899        assert_eq!(ReviewActionResultStatus::InvalidTx.as_str(), "invalidTx");
2900    }
2901
2902    #[cfg(feature = "serde")]
2903    #[test]
2904    fn test_review_action_result_serde_roundtrip() {
2905        let r = ReviewActionResult {
2906            txid: "aabb".to_string(),
2907            status: ReviewActionResultStatus::DoubleSpend,
2908            competing_txs: Some(vec!["ccdd".to_string()]),
2909            competing_beef: None,
2910        };
2911        let json = serde_json::to_string(&r).unwrap();
2912        assert!(json.contains("\"doubleSpend\""));
2913        assert!(json.contains("\"competingTxs\""));
2914        // competing_beef should be omitted since None
2915        assert!(!json.contains("\"competingBeef\""));
2916        let r2: ReviewActionResult = serde_json::from_str(&json).unwrap();
2917        assert_eq!(r2.status, ReviewActionResultStatus::DoubleSpend);
2918        assert_eq!(r2.competing_txs.unwrap()[0], "ccdd");
2919    }
2920}
2921
2922#[cfg(test)]
2923mod arc_blanket_impl_tests {
2924    use super::*;
2925    use std::sync::Arc;
2926
2927    /// The property the blanket impl exists for: a bound of
2928    /// `WalletInterface + Clone` — which `AuthFetch` and `MessageBoxClient`
2929    /// both impose — is satisfiable by `Arc<W>` for a NON-`Clone` `W`.
2930    ///
2931    /// Before the blanket impl this did not compile, and every consumer wrote
2932    /// a newtype-over-`Arc` plus a 28-method delegation block to get here.
2933    fn requires_wallet_interface_and_clone<W: WalletInterface + Clone + 'static>(_w: W) {}
2934
2935    /// A deliberately NON-`Clone` wallet, standing in for `ProtoWallet` (whose
2936    /// `KeyDeriver` owns an `RwLock` cache and so cannot derive `Clone`).
2937    struct NotClone;
2938
2939    #[async_trait]
2940    impl WalletInterface for NotClone {
2941        async fn create_action(
2942            &self,
2943            _args: CreateActionArgs,
2944            _originator: Option<&str>,
2945        ) -> Result<CreateActionResult, WalletError> {
2946            Err(WalletError::NotImplemented("test stub".into()))
2947        }
2948        async fn sign_action(
2949            &self,
2950            _args: SignActionArgs,
2951            _originator: Option<&str>,
2952        ) -> Result<SignActionResult, WalletError> {
2953            Err(WalletError::NotImplemented("test stub".into()))
2954        }
2955        async fn abort_action(
2956            &self,
2957            _args: AbortActionArgs,
2958            _originator: Option<&str>,
2959        ) -> Result<AbortActionResult, WalletError> {
2960            Err(WalletError::NotImplemented("test stub".into()))
2961        }
2962        async fn list_actions(
2963            &self,
2964            _args: ListActionsArgs,
2965            _originator: Option<&str>,
2966        ) -> Result<ListActionsResult, WalletError> {
2967            Err(WalletError::NotImplemented("test stub".into()))
2968        }
2969        async fn internalize_action(
2970            &self,
2971            _args: InternalizeActionArgs,
2972            _originator: Option<&str>,
2973        ) -> Result<InternalizeActionResult, WalletError> {
2974            Err(WalletError::NotImplemented("test stub".into()))
2975        }
2976        async fn list_outputs(
2977            &self,
2978            _args: ListOutputsArgs,
2979            _originator: Option<&str>,
2980        ) -> Result<ListOutputsResult, WalletError> {
2981            Err(WalletError::NotImplemented("test stub".into()))
2982        }
2983        async fn relinquish_output(
2984            &self,
2985            _args: RelinquishOutputArgs,
2986            _originator: Option<&str>,
2987        ) -> Result<RelinquishOutputResult, WalletError> {
2988            Err(WalletError::NotImplemented("test stub".into()))
2989        }
2990        async fn get_public_key(
2991            &self,
2992            _args: GetPublicKeyArgs,
2993            _originator: Option<&str>,
2994        ) -> Result<GetPublicKeyResult, WalletError> {
2995            Err(WalletError::NotImplemented("test stub".into()))
2996        }
2997        async fn reveal_counterparty_key_linkage(
2998            &self,
2999            _args: RevealCounterpartyKeyLinkageArgs,
3000            _originator: Option<&str>,
3001        ) -> Result<RevealCounterpartyKeyLinkageResult, WalletError> {
3002            Err(WalletError::NotImplemented("test stub".into()))
3003        }
3004        async fn reveal_specific_key_linkage(
3005            &self,
3006            _args: RevealSpecificKeyLinkageArgs,
3007            _originator: Option<&str>,
3008        ) -> Result<RevealSpecificKeyLinkageResult, WalletError> {
3009            Err(WalletError::NotImplemented("test stub".into()))
3010        }
3011        async fn encrypt(
3012            &self,
3013            _args: EncryptArgs,
3014            _originator: Option<&str>,
3015        ) -> Result<EncryptResult, WalletError> {
3016            Err(WalletError::NotImplemented("test stub".into()))
3017        }
3018        async fn decrypt(
3019            &self,
3020            _args: DecryptArgs,
3021            _originator: Option<&str>,
3022        ) -> Result<DecryptResult, WalletError> {
3023            Err(WalletError::NotImplemented("test stub".into()))
3024        }
3025        async fn create_hmac(
3026            &self,
3027            _args: CreateHmacArgs,
3028            _originator: Option<&str>,
3029        ) -> Result<CreateHmacResult, WalletError> {
3030            Err(WalletError::NotImplemented("test stub".into()))
3031        }
3032        async fn verify_hmac(
3033            &self,
3034            _args: VerifyHmacArgs,
3035            _originator: Option<&str>,
3036        ) -> Result<VerifyHmacResult, WalletError> {
3037            Err(WalletError::NotImplemented("test stub".into()))
3038        }
3039        async fn create_signature(
3040            &self,
3041            _args: CreateSignatureArgs,
3042            _originator: Option<&str>,
3043        ) -> Result<CreateSignatureResult, WalletError> {
3044            Err(WalletError::NotImplemented("test stub".into()))
3045        }
3046        async fn verify_signature(
3047            &self,
3048            _args: VerifySignatureArgs,
3049            _originator: Option<&str>,
3050        ) -> Result<VerifySignatureResult, WalletError> {
3051            Err(WalletError::NotImplemented("test stub".into()))
3052        }
3053        async fn acquire_certificate(
3054            &self,
3055            _args: AcquireCertificateArgs,
3056            _originator: Option<&str>,
3057        ) -> Result<Certificate, WalletError> {
3058            Err(WalletError::NotImplemented("test stub".into()))
3059        }
3060        async fn list_certificates(
3061            &self,
3062            _args: ListCertificatesArgs,
3063            _originator: Option<&str>,
3064        ) -> Result<ListCertificatesResult, WalletError> {
3065            Err(WalletError::NotImplemented("test stub".into()))
3066        }
3067        async fn prove_certificate(
3068            &self,
3069            _args: ProveCertificateArgs,
3070            _originator: Option<&str>,
3071        ) -> Result<ProveCertificateResult, WalletError> {
3072            Err(WalletError::NotImplemented("test stub".into()))
3073        }
3074        async fn relinquish_certificate(
3075            &self,
3076            _args: RelinquishCertificateArgs,
3077            _originator: Option<&str>,
3078        ) -> Result<RelinquishCertificateResult, WalletError> {
3079            Err(WalletError::NotImplemented("test stub".into()))
3080        }
3081        async fn discover_by_identity_key(
3082            &self,
3083            _args: DiscoverByIdentityKeyArgs,
3084            _originator: Option<&str>,
3085        ) -> Result<DiscoverCertificatesResult, WalletError> {
3086            Err(WalletError::NotImplemented("test stub".into()))
3087        }
3088        async fn discover_by_attributes(
3089            &self,
3090            _args: DiscoverByAttributesArgs,
3091            _originator: Option<&str>,
3092        ) -> Result<DiscoverCertificatesResult, WalletError> {
3093            Err(WalletError::NotImplemented("test stub".into()))
3094        }
3095        async fn is_authenticated(
3096            &self,
3097            _originator: Option<&str>,
3098        ) -> Result<AuthenticatedResult, WalletError> {
3099            Err(WalletError::NotImplemented("test stub".into()))
3100        }
3101        async fn wait_for_authentication(
3102            &self,
3103            _originator: Option<&str>,
3104        ) -> Result<AuthenticatedResult, WalletError> {
3105            Err(WalletError::NotImplemented("test stub".into()))
3106        }
3107        async fn get_height(
3108            &self,
3109            _originator: Option<&str>,
3110        ) -> Result<GetHeightResult, WalletError> {
3111            Err(WalletError::NotImplemented("test stub".into()))
3112        }
3113        async fn get_header_for_height(
3114            &self,
3115            _args: GetHeaderArgs,
3116            _originator: Option<&str>,
3117        ) -> Result<GetHeaderResult, WalletError> {
3118            Err(WalletError::NotImplemented("test stub".into()))
3119        }
3120        async fn get_network(
3121            &self,
3122            _originator: Option<&str>,
3123        ) -> Result<GetNetworkResult, WalletError> {
3124            Err(WalletError::NotImplemented("test stub".into()))
3125        }
3126        async fn get_version(
3127            &self,
3128            _originator: Option<&str>,
3129        ) -> Result<GetVersionResult, WalletError> {
3130            Ok(GetVersionResult {
3131                version: "arc-blanket".to_string(),
3132            })
3133        }
3134    }
3135
3136    #[test]
3137    fn arc_of_a_non_clone_wallet_satisfies_wallet_interface_plus_clone() {
3138        requires_wallet_interface_and_clone(Arc::new(NotClone));
3139    }
3140
3141    #[tokio::test]
3142    async fn arc_delegates_to_the_inner_wallet() {
3143        let w: Arc<NotClone> = Arc::new(NotClone);
3144        // Called through the blanket impl, not through `NotClone` directly.
3145        let v = WalletInterface::get_version(&w, None).await.unwrap();
3146        assert_eq!(v.version, "arc-blanket");
3147    }
3148
3149    /// `Arc<dyn WalletInterface>` is the shape callers actually want when the
3150    /// concrete wallet is chosen at runtime; `?Sized` on the blanket impl is
3151    /// what makes it work.
3152    #[test]
3153    fn arc_dyn_wallet_interface_also_satisfies_the_bound() {
3154        let w: Arc<dyn WalletInterface> = Arc::new(NotClone);
3155        requires_wallet_interface_and_clone(w);
3156    }
3157}