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 "network" feature)
22// ---------------------------------------------------------------------------
23
24/// Serde helpers for custom JSON serialization of wallet types.
25/// Gated behind the "network" feature since serde is an optional dependency.
26#[cfg(feature = "network")]
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 = "network")]
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 = "network", derive(serde::Serialize, serde::Deserialize))]
384#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
385pub enum ActionStatus {
386    Completed,
387    Unprocessed,
388    Sending,
389    Unproven,
390    Unsigned,
391    #[cfg_attr(feature = "network", serde(rename = "nosend"))]
392    NoSend,
393    #[cfg_attr(feature = "network", 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 = "network", derive(serde::Serialize, serde::Deserialize))]
416#[cfg_attr(feature = "network", 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 = "network", derive(serde::Serialize, serde::Deserialize))]
436#[cfg_attr(feature = "network", 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 = "network", derive(serde::Serialize, serde::Deserialize))]
454pub enum OutputInclude {
455    #[cfg_attr(feature = "network", serde(rename = "locking scripts"))]
456    LockingScripts,
457    #[cfg_attr(feature = "network", 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 = "network", derive(serde::Serialize, serde::Deserialize))]
473pub enum InternalizeProtocol {
474    #[cfg_attr(feature = "network", serde(rename = "wallet payment"))]
475    WalletPayment,
476    #[cfg_attr(feature = "network", 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 = "network", derive(serde::Serialize, serde::Deserialize))]
492#[cfg_attr(feature = "network", 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 = "network", derive(serde::Serialize, serde::Deserialize))]
510#[cfg_attr(feature = "network", 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 = "network", derive(serde::Serialize, serde::Deserialize))]
528#[cfg_attr(feature = "network", 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 = "network")]
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 = "network")]
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 = "network")]
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 = "network")]
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 = "network", derive(serde::Serialize, serde::Deserialize))]
711#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
712pub struct Certificate {
713    #[cfg_attr(feature = "network", serde(rename = "type"))]
714    pub cert_type: CertificateType,
715    pub serial_number: SerialNumber,
716    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
717    pub subject: PublicKey,
718    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
719    pub certifier: PublicKey,
720    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
721    pub revocation_outpoint: Option<String>,
722    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
723    pub fields: Option<HashMap<String, String>>,
724    #[cfg_attr(
725        feature = "network",
726        serde(with = "serde_helpers::option_bytes_as_hex")
727    )]
728    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
729    pub signature: Option<Vec<u8>>,
730}
731
732/// A partial certificate where all fields are optional.
733/// Used for ProveCertificateArgs to match TS SDK's `Partial<WalletCertificate>`.
734#[derive(Clone, Debug)]
735#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
736#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
737pub struct PartialCertificate {
738    #[cfg_attr(feature = "network", serde(rename = "type"))]
739    #[cfg_attr(
740        feature = "network",
741        serde(default, skip_serializing_if = "Option::is_none")
742    )]
743    pub cert_type: Option<CertificateType>,
744    #[cfg_attr(
745        feature = "network",
746        serde(default, skip_serializing_if = "Option::is_none")
747    )]
748    pub serial_number: Option<SerialNumber>,
749    #[cfg_attr(
750        feature = "network",
751        serde(with = "serde_helpers::option_public_key_hex")
752    )]
753    #[cfg_attr(feature = "network", serde(default))]
754    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
755    pub subject: Option<PublicKey>,
756    #[cfg_attr(
757        feature = "network",
758        serde(with = "serde_helpers::option_public_key_hex")
759    )]
760    #[cfg_attr(feature = "network", serde(default))]
761    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
762    pub certifier: Option<PublicKey>,
763    #[cfg_attr(
764        feature = "network",
765        serde(default, skip_serializing_if = "Option::is_none")
766    )]
767    pub revocation_outpoint: Option<String>,
768    #[cfg_attr(
769        feature = "network",
770        serde(default, skip_serializing_if = "Option::is_none")
771    )]
772    pub fields: Option<HashMap<String, String>>,
773    #[cfg_attr(
774        feature = "network",
775        serde(with = "serde_helpers::option_bytes_as_hex")
776    )]
777    #[cfg_attr(feature = "network", serde(default))]
778    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
779    pub signature: Option<Vec<u8>>,
780}
781
782impl From<Certificate> for PartialCertificate {
783    fn from(c: Certificate) -> Self {
784        PartialCertificate {
785            cert_type: Some(c.cert_type),
786            serial_number: Some(c.serial_number),
787            subject: Some(c.subject),
788            certifier: Some(c.certifier),
789            revocation_outpoint: c.revocation_outpoint,
790            fields: c.fields,
791            signature: c.signature,
792        }
793    }
794}
795
796/// Identifies who reveals a keyring.
797#[derive(Clone, Debug)]
798pub enum KeyringRevealer {
799    /// The certifier reveals the keyring.
800    Certifier,
801    /// A specific public key reveals the keyring.
802    PubKey(PublicKey),
803}
804
805#[cfg(feature = "network")]
806impl serde::Serialize for KeyringRevealer {
807    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
808        match self {
809            KeyringRevealer::Certifier => serializer.serialize_str("certifier"),
810            KeyringRevealer::PubKey(pk) => serializer.serialize_str(&pk.to_der_hex()),
811        }
812    }
813}
814
815#[cfg(feature = "network")]
816impl<'de> serde::Deserialize<'de> for KeyringRevealer {
817    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
818        let s = String::deserialize(deserializer)?;
819        if s == "certifier" || s.is_empty() {
820            Ok(KeyringRevealer::Certifier)
821        } else {
822            PublicKey::from_string(&s)
823                .map(KeyringRevealer::PubKey)
824                .map_err(serde::de::Error::custom)
825        }
826    }
827}
828
829// ---------------------------------------------------------------------------
830// Action types (CreateAction, SignAction, AbortAction)
831// ---------------------------------------------------------------------------
832
833/// An input to be spent in a new transaction.
834#[derive(Clone, Debug)]
835#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
836#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
837pub struct CreateActionInput {
838    pub outpoint: OutpointString,
839    pub input_description: String,
840    #[cfg_attr(
841        feature = "network",
842        serde(with = "serde_helpers::option_bytes_as_hex")
843    )]
844    #[cfg_attr(
845        feature = "network",
846        serde(skip_serializing_if = "Option::is_none", default)
847    )]
848    pub unlocking_script: Option<Vec<u8>>,
849    #[cfg_attr(
850        feature = "network",
851        serde(default, skip_serializing_if = "Option::is_none")
852    )]
853    pub unlocking_script_length: Option<u32>,
854    #[cfg_attr(
855        feature = "network",
856        serde(default, skip_serializing_if = "Option::is_none")
857    )]
858    pub sequence_number: Option<u32>,
859}
860
861/// An output to be created in a new transaction.
862#[derive(Clone, Debug)]
863#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
864#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
865pub struct CreateActionOutput {
866    #[cfg_attr(
867        feature = "network",
868        serde(with = "serde_helpers::option_bytes_as_hex")
869    )]
870    #[cfg_attr(
871        feature = "network",
872        serde(skip_serializing_if = "Option::is_none", default)
873    )]
874    pub locking_script: Option<Vec<u8>>,
875    pub satoshis: SatoshiValue,
876    pub output_description: String,
877    #[cfg_attr(
878        feature = "network",
879        serde(default, skip_serializing_if = "Option::is_none")
880    )]
881    pub basket: Option<BasketStringUnder300Bytes>,
882    #[cfg_attr(
883        feature = "network",
884        serde(default, skip_serializing_if = "Option::is_none")
885    )]
886    pub custom_instructions: Option<String>,
887    #[cfg_attr(
888        feature = "network",
889        serde(skip_serializing_if = "Vec::is_empty", default)
890    )]
891    pub tags: Vec<OutputTagStringUnder300Bytes>,
892}
893
894/// Optional parameters for creating a new transaction.
895#[derive(Clone, Debug, Default)]
896#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
897#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
898pub struct CreateActionOptions {
899    #[cfg_attr(
900        feature = "network",
901        serde(
902            default = "BooleanDefaultTrue::none",
903            skip_serializing_if = "BooleanDefaultTrue::is_none"
904        )
905    )]
906    pub sign_and_process: BooleanDefaultTrue,
907    #[cfg_attr(
908        feature = "network",
909        serde(
910            default = "BooleanDefaultTrue::none",
911            skip_serializing_if = "BooleanDefaultTrue::is_none"
912        )
913    )]
914    pub accept_delayed_broadcast: BooleanDefaultTrue,
915    #[cfg_attr(
916        feature = "network",
917        serde(default, skip_serializing_if = "Option::is_none")
918    )]
919    pub trust_self: Option<TrustSelf>,
920    #[cfg_attr(
921        feature = "network",
922        serde(skip_serializing_if = "Vec::is_empty", default)
923    )]
924    pub known_txids: Vec<TXIDHexString>,
925    #[cfg_attr(
926        feature = "network",
927        serde(
928            default = "BooleanDefaultFalse::none",
929            skip_serializing_if = "BooleanDefaultFalse::is_none"
930        )
931    )]
932    pub return_txid_only: BooleanDefaultFalse,
933    #[cfg_attr(
934        feature = "network",
935        serde(
936            default = "BooleanDefaultFalse::none",
937            skip_serializing_if = "BooleanDefaultFalse::is_none"
938        )
939    )]
940    pub no_send: BooleanDefaultFalse,
941    #[cfg_attr(
942        feature = "network",
943        serde(skip_serializing_if = "Vec::is_empty", default)
944    )]
945    pub no_send_change: Vec<OutpointString>,
946    #[cfg_attr(
947        feature = "network",
948        serde(skip_serializing_if = "Vec::is_empty", default)
949    )]
950    pub send_with: Vec<TXIDHexString>,
951    #[cfg_attr(
952        feature = "network",
953        serde(
954            default = "BooleanDefaultTrue::none",
955            skip_serializing_if = "BooleanDefaultTrue::is_none"
956        )
957    )]
958    pub randomize_outputs: BooleanDefaultTrue,
959}
960
961/// Arguments for creating a new transaction.
962#[derive(Clone, Debug)]
963#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
964#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
965pub struct CreateActionArgs {
966    pub description: DescriptionString5to50Bytes,
967    #[cfg_attr(
968        feature = "network",
969        serde(with = "serde_helpers::option_bytes_as_array")
970    )]
971    #[cfg_attr(
972        feature = "network",
973        serde(skip_serializing_if = "Option::is_none", default)
974    )]
975    #[cfg_attr(feature = "network", serde(rename = "inputBEEF"))]
976    pub input_beef: Option<Vec<u8>>,
977    #[cfg_attr(
978        feature = "network",
979        serde(skip_serializing_if = "Vec::is_empty", default)
980    )]
981    pub inputs: Vec<CreateActionInput>,
982    #[cfg_attr(
983        feature = "network",
984        serde(skip_serializing_if = "Vec::is_empty", default)
985    )]
986    pub outputs: Vec<CreateActionOutput>,
987    #[cfg_attr(
988        feature = "network",
989        serde(default, skip_serializing_if = "Option::is_none")
990    )]
991    pub lock_time: Option<u32>,
992    #[cfg_attr(
993        feature = "network",
994        serde(default, skip_serializing_if = "Option::is_none")
995    )]
996    pub version: Option<u32>,
997    #[cfg_attr(
998        feature = "network",
999        serde(skip_serializing_if = "Vec::is_empty", default)
1000    )]
1001    pub labels: Vec<LabelStringUnder300Bytes>,
1002    #[cfg_attr(
1003        feature = "network",
1004        serde(default, skip_serializing_if = "Option::is_none")
1005    )]
1006    pub options: Option<CreateActionOptions>,
1007    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1008    pub reference: Option<String>,
1009}
1010
1011/// Data needed to complete signing of a partial transaction.
1012#[derive(Clone, Debug)]
1013#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1014#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1015pub struct SignableTransaction {
1016    // bytes_as_array: TS types `tx` as `AtomicBEEF = Byte[]` (number array).
1017    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1018    pub tx: Vec<u8>,
1019    // bytes_as_base64: TS types `reference` as `Base64String`, so the wire
1020    // format is a base64 string (not a number array). See BRC-100.
1021    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_base64"))]
1022    pub reference: Vec<u8>,
1023}
1024
1025/// Status of a transaction sent as part of a batch.
1026#[derive(Clone, Debug)]
1027#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1028#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1029pub struct SendWithResult {
1030    pub txid: TXIDHexString,
1031    pub status: ActionResultStatus,
1032}
1033
1034/// Status of a review action result from undelayed broadcast.
1035#[derive(Clone, Debug, PartialEq, Eq)]
1036#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1037#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1038pub enum ReviewActionResultStatus {
1039    Success,
1040    DoubleSpend,
1041    ServiceError,
1042    InvalidTx,
1043}
1044
1045impl ReviewActionResultStatus {
1046    pub fn as_str(&self) -> &'static str {
1047        match self {
1048            ReviewActionResultStatus::Success => "success",
1049            ReviewActionResultStatus::DoubleSpend => "doubleSpend",
1050            ReviewActionResultStatus::ServiceError => "serviceError",
1051            ReviewActionResultStatus::InvalidTx => "invalidTx",
1052        }
1053    }
1054}
1055
1056/// Result of reviewing a non-delayed broadcast action.
1057#[derive(Clone, Debug)]
1058#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1059#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1060pub struct ReviewActionResult {
1061    pub txid: TXIDHexString,
1062    pub status: ReviewActionResultStatus,
1063    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1064    pub competing_txs: Option<Vec<String>>,
1065    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1066    pub competing_beef: Option<Vec<u8>>,
1067}
1068
1069/// Result of creating a transaction.
1070#[derive(Clone, Debug)]
1071#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1072#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1073pub struct CreateActionResult {
1074    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1075    pub txid: Option<TXIDHexString>,
1076    #[cfg_attr(
1077        feature = "network",
1078        serde(with = "serde_helpers::option_bytes_as_array")
1079    )]
1080    #[cfg_attr(
1081        feature = "network",
1082        serde(skip_serializing_if = "Option::is_none", default)
1083    )]
1084    pub tx: Option<Vec<u8>>,
1085    #[cfg_attr(
1086        feature = "network",
1087        serde(skip_serializing_if = "Vec::is_empty", default)
1088    )]
1089    pub no_send_change: Vec<OutpointString>,
1090    #[cfg_attr(
1091        feature = "network",
1092        serde(skip_serializing_if = "Vec::is_empty", default)
1093    )]
1094    pub send_with_results: Vec<SendWithResult>,
1095    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1096    pub signable_transaction: Option<SignableTransaction>,
1097}
1098
1099/// Unlocking script and sequence number for a specific input.
1100#[derive(Clone, Debug)]
1101#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1102#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1103pub struct SignActionSpend {
1104    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_hex"))]
1105    pub unlocking_script: Vec<u8>,
1106    #[cfg_attr(
1107        feature = "network",
1108        serde(default, skip_serializing_if = "Option::is_none")
1109    )]
1110    pub sequence_number: Option<u32>,
1111}
1112
1113/// Controls signing and broadcasting behavior.
1114#[derive(Clone, Debug, Default)]
1115#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1116#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1117pub struct SignActionOptions {
1118    #[cfg_attr(
1119        feature = "network",
1120        serde(
1121            default = "BooleanDefaultTrue::none",
1122            skip_serializing_if = "BooleanDefaultTrue::is_none"
1123        )
1124    )]
1125    pub accept_delayed_broadcast: BooleanDefaultTrue,
1126    #[cfg_attr(
1127        feature = "network",
1128        serde(
1129            default = "BooleanDefaultFalse::none",
1130            skip_serializing_if = "BooleanDefaultFalse::is_none"
1131        )
1132    )]
1133    pub return_txid_only: BooleanDefaultFalse,
1134    #[cfg_attr(
1135        feature = "network",
1136        serde(
1137            default = "BooleanDefaultFalse::none",
1138            skip_serializing_if = "BooleanDefaultFalse::is_none"
1139        )
1140    )]
1141    pub no_send: BooleanDefaultFalse,
1142    #[cfg_attr(
1143        feature = "network",
1144        serde(skip_serializing_if = "Vec::is_empty", default)
1145    )]
1146    pub send_with: Vec<TXIDHexString>,
1147}
1148
1149/// Arguments for signing a previously created transaction.
1150#[derive(Clone, Debug)]
1151#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1152#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1153pub struct SignActionArgs {
1154    // bytes_as_base64: TS types `reference` as `Base64String` on the wire.
1155    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_base64"))]
1156    pub reference: Vec<u8>,
1157    pub spends: HashMap<u32, SignActionSpend>,
1158    #[cfg_attr(
1159        feature = "network",
1160        serde(default, skip_serializing_if = "Option::is_none")
1161    )]
1162    pub options: Option<SignActionOptions>,
1163}
1164
1165/// Result of a successful signing operation.
1166#[derive(Clone, Debug)]
1167#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1168#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1169pub struct SignActionResult {
1170    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1171    pub txid: Option<TXIDHexString>,
1172    #[cfg_attr(
1173        feature = "network",
1174        serde(with = "serde_helpers::option_bytes_as_array")
1175    )]
1176    #[cfg_attr(
1177        feature = "network",
1178        serde(skip_serializing_if = "Option::is_none", default)
1179    )]
1180    pub tx: Option<Vec<u8>>,
1181    #[cfg_attr(
1182        feature = "network",
1183        serde(skip_serializing_if = "Vec::is_empty", default)
1184    )]
1185    pub send_with_results: Vec<SendWithResult>,
1186}
1187
1188/// Arguments for aborting a transaction.
1189#[derive(Clone, Debug)]
1190#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1191#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1192pub struct AbortActionArgs {
1193    // bytes_as_base64: TS types `reference` as `Base64String` on the wire.
1194    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_base64"))]
1195    pub reference: Vec<u8>,
1196}
1197
1198/// Result of aborting a transaction.
1199#[derive(Clone, Debug)]
1200#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1201#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1202pub struct AbortActionResult {
1203    pub aborted: bool,
1204}
1205
1206// ---------------------------------------------------------------------------
1207// Action detail types (for listing)
1208// ---------------------------------------------------------------------------
1209
1210/// A transaction input with full details.
1211#[derive(Clone, Debug)]
1212#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1213#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1214pub struct ActionInput {
1215    pub source_outpoint: OutpointString,
1216    pub source_satoshis: SatoshiValue,
1217    #[cfg_attr(
1218        feature = "network",
1219        serde(with = "serde_helpers::option_bytes_as_hex")
1220    )]
1221    #[cfg_attr(
1222        feature = "network",
1223        serde(skip_serializing_if = "Option::is_none", default)
1224    )]
1225    pub source_locking_script: Option<Vec<u8>>,
1226    #[cfg_attr(
1227        feature = "network",
1228        serde(with = "serde_helpers::option_bytes_as_hex")
1229    )]
1230    #[cfg_attr(
1231        feature = "network",
1232        serde(skip_serializing_if = "Option::is_none", default)
1233    )]
1234    pub unlocking_script: Option<Vec<u8>>,
1235    pub input_description: String,
1236    pub sequence_number: u32,
1237}
1238
1239/// A transaction output with full details.
1240#[derive(Clone, Debug)]
1241#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1242#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1243pub struct ActionOutput {
1244    pub satoshis: SatoshiValue,
1245    #[cfg_attr(
1246        feature = "network",
1247        serde(with = "serde_helpers::option_bytes_as_hex")
1248    )]
1249    #[cfg_attr(
1250        feature = "network",
1251        serde(skip_serializing_if = "Option::is_none", default)
1252    )]
1253    pub locking_script: Option<Vec<u8>>,
1254    pub spendable: bool,
1255    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1256    pub custom_instructions: Option<String>,
1257    pub tags: Vec<String>,
1258    pub output_index: u32,
1259    pub output_description: String,
1260    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1261    pub basket: Option<String>,
1262}
1263
1264/// Full details about a wallet transaction.
1265#[derive(Clone, Debug)]
1266#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1267#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1268pub struct Action {
1269    pub txid: TXIDHexString,
1270    pub satoshis: i64,
1271    pub status: ActionStatus,
1272    pub is_outgoing: bool,
1273    pub description: String,
1274    #[cfg_attr(
1275        feature = "network",
1276        serde(skip_serializing_if = "Vec::is_empty", default)
1277    )]
1278    pub labels: Vec<String>,
1279    pub version: u32,
1280    pub lock_time: u32,
1281    #[cfg_attr(
1282        feature = "network",
1283        serde(skip_serializing_if = "Vec::is_empty", default)
1284    )]
1285    pub inputs: Vec<ActionInput>,
1286    #[cfg_attr(
1287        feature = "network",
1288        serde(skip_serializing_if = "Vec::is_empty", default)
1289    )]
1290    pub outputs: Vec<ActionOutput>,
1291}
1292
1293/// Maximum number of actions or outputs that can be returned.
1294pub const MAX_ACTIONS_LIMIT: u32 = 10000;
1295
1296// ---------------------------------------------------------------------------
1297// ListActions
1298// ---------------------------------------------------------------------------
1299
1300/// Filtering and pagination options for listing wallet transactions.
1301#[derive(Clone, Debug)]
1302#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1303#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1304pub struct ListActionsArgs {
1305    #[cfg_attr(
1306        feature = "network",
1307        serde(skip_serializing_if = "Vec::is_empty", default)
1308    )]
1309    pub labels: Vec<LabelStringUnder300Bytes>,
1310    #[cfg_attr(
1311        feature = "network",
1312        serde(default, skip_serializing_if = "Option::is_none")
1313    )]
1314    pub label_query_mode: Option<QueryMode>,
1315    #[cfg_attr(
1316        feature = "network",
1317        serde(
1318            default = "BooleanDefaultFalse::none",
1319            skip_serializing_if = "BooleanDefaultFalse::is_none"
1320        )
1321    )]
1322    pub include_labels: BooleanDefaultFalse,
1323    #[cfg_attr(
1324        feature = "network",
1325        serde(
1326            default = "BooleanDefaultFalse::none",
1327            skip_serializing_if = "BooleanDefaultFalse::is_none"
1328        )
1329    )]
1330    pub include_inputs: BooleanDefaultFalse,
1331    #[cfg_attr(
1332        feature = "network",
1333        serde(
1334            default = "BooleanDefaultFalse::none",
1335            skip_serializing_if = "BooleanDefaultFalse::is_none"
1336        )
1337    )]
1338    pub include_input_source_locking_scripts: BooleanDefaultFalse,
1339    #[cfg_attr(
1340        feature = "network",
1341        serde(
1342            default = "BooleanDefaultFalse::none",
1343            skip_serializing_if = "BooleanDefaultFalse::is_none"
1344        )
1345    )]
1346    pub include_input_unlocking_scripts: BooleanDefaultFalse,
1347    #[cfg_attr(
1348        feature = "network",
1349        serde(
1350            default = "BooleanDefaultFalse::none",
1351            skip_serializing_if = "BooleanDefaultFalse::is_none"
1352        )
1353    )]
1354    pub include_outputs: BooleanDefaultFalse,
1355    #[cfg_attr(
1356        feature = "network",
1357        serde(
1358            default = "BooleanDefaultFalse::none",
1359            skip_serializing_if = "BooleanDefaultFalse::is_none"
1360        )
1361    )]
1362    pub include_output_locking_scripts: BooleanDefaultFalse,
1363    #[cfg_attr(feature = "network", serde(default))]
1364    pub limit: PositiveIntegerDefault10Max10000,
1365    #[cfg_attr(
1366        feature = "network",
1367        serde(default, skip_serializing_if = "Option::is_none")
1368    )]
1369    pub offset: Option<PositiveIntegerOrZero>,
1370    #[cfg_attr(
1371        feature = "network",
1372        serde(
1373            default = "BooleanDefaultTrue::none",
1374            skip_serializing_if = "BooleanDefaultTrue::is_none"
1375        )
1376    )]
1377    pub seek_permission: BooleanDefaultTrue,
1378}
1379
1380/// Paginated list of wallet transactions.
1381#[derive(Clone, Debug)]
1382#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1383#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1384pub struct ListActionsResult {
1385    pub total_actions: u32,
1386    pub actions: Vec<Action>,
1387}
1388
1389// ---------------------------------------------------------------------------
1390// InternalizeAction
1391// ---------------------------------------------------------------------------
1392
1393/// Derivation and identity data for wallet payment outputs.
1394#[derive(Clone, Debug)]
1395#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1396#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1397pub struct Payment {
1398    // bytes_as_base64: TS SDK types these as Base64String, Go uses default
1399    // json.Marshal for []byte which produces base64. BSV Desktop expects strings.
1400    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_base64"))]
1401    pub derivation_prefix: Vec<u8>,
1402    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_base64"))]
1403    pub derivation_suffix: Vec<u8>,
1404    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1405    pub sender_identity_key: PublicKey,
1406}
1407
1408/// Metadata for outputs being inserted into baskets.
1409#[derive(Clone, Debug)]
1410#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1411#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1412pub struct BasketInsertion {
1413    pub basket: BasketStringUnder300Bytes,
1414    #[cfg_attr(
1415        feature = "network",
1416        serde(default, skip_serializing_if = "Option::is_none")
1417    )]
1418    pub custom_instructions: Option<String>,
1419    #[cfg_attr(
1420        feature = "network",
1421        serde(skip_serializing_if = "Vec::is_empty", default)
1422    )]
1423    pub tags: Vec<OutputTagStringUnder300Bytes>,
1424}
1425
1426/// How to process a transaction output -- as payment or basket insertion.
1427///
1428/// An enum with two variants, encoding the protocol in the variant itself.
1429/// This makes impossible states unrepresentable: a WalletPayment always has
1430/// a Payment, and a BasketInsertion always has a BasketInsertion.
1431#[derive(Clone, Debug)]
1432#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1433#[cfg_attr(feature = "network", serde(tag = "protocol", rename_all = "camelCase"))]
1434pub enum InternalizeOutput {
1435    #[cfg_attr(feature = "network", serde(rename = "wallet payment"))]
1436    WalletPayment {
1437        #[cfg_attr(feature = "network", serde(rename = "outputIndex"))]
1438        output_index: u32,
1439        #[cfg_attr(feature = "network", serde(rename = "paymentRemittance"))]
1440        payment: Payment,
1441    },
1442    #[cfg_attr(feature = "network", serde(rename = "basket insertion"))]
1443    BasketInsertion {
1444        #[cfg_attr(feature = "network", serde(rename = "outputIndex"))]
1445        output_index: u32,
1446        #[cfg_attr(feature = "network", serde(rename = "insertionRemittance"))]
1447        insertion: BasketInsertion,
1448    },
1449}
1450
1451/// Arguments for importing an external transaction into the wallet.
1452#[derive(Clone, Debug)]
1453#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1454#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1455pub struct InternalizeActionArgs {
1456    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1457    pub tx: Vec<u8>,
1458    pub description: String,
1459    #[cfg_attr(
1460        feature = "network",
1461        serde(skip_serializing_if = "Vec::is_empty", default)
1462    )]
1463    pub labels: Vec<LabelStringUnder300Bytes>,
1464    #[cfg_attr(
1465        feature = "network",
1466        serde(
1467            default = "BooleanDefaultTrue::none",
1468            skip_serializing_if = "BooleanDefaultTrue::is_none"
1469        )
1470    )]
1471    pub seek_permission: BooleanDefaultTrue,
1472    pub outputs: Vec<InternalizeOutput>,
1473}
1474
1475/// Result of internalizing a transaction.
1476#[derive(Clone, Debug)]
1477#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1478#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1479pub struct InternalizeActionResult {
1480    pub accepted: bool,
1481}
1482
1483// ---------------------------------------------------------------------------
1484// ListOutputs
1485// ---------------------------------------------------------------------------
1486
1487/// Filtering and options for listing wallet outputs.
1488#[derive(Clone, Debug)]
1489#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1490#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1491pub struct ListOutputsArgs {
1492    pub basket: BasketStringUnder300Bytes,
1493    #[cfg_attr(
1494        feature = "network",
1495        serde(skip_serializing_if = "Vec::is_empty", default)
1496    )]
1497    pub tags: Vec<OutputTagStringUnder300Bytes>,
1498    #[cfg_attr(
1499        feature = "network",
1500        serde(default, skip_serializing_if = "Option::is_none")
1501    )]
1502    pub tag_query_mode: Option<QueryMode>,
1503    #[cfg_attr(
1504        feature = "network",
1505        serde(default, skip_serializing_if = "Option::is_none")
1506    )]
1507    pub include: Option<OutputInclude>,
1508    #[cfg_attr(
1509        feature = "network",
1510        serde(
1511            default = "BooleanDefaultFalse::none",
1512            skip_serializing_if = "BooleanDefaultFalse::is_none"
1513        )
1514    )]
1515    pub include_custom_instructions: BooleanDefaultFalse,
1516    #[cfg_attr(
1517        feature = "network",
1518        serde(
1519            default = "BooleanDefaultFalse::none",
1520            skip_serializing_if = "BooleanDefaultFalse::is_none"
1521        )
1522    )]
1523    pub include_tags: BooleanDefaultFalse,
1524    #[cfg_attr(
1525        feature = "network",
1526        serde(
1527            default = "BooleanDefaultFalse::none",
1528            skip_serializing_if = "BooleanDefaultFalse::is_none"
1529        )
1530    )]
1531    pub include_labels: BooleanDefaultFalse,
1532    #[cfg_attr(
1533        feature = "network",
1534        serde(skip_serializing_if = "Option::is_none", default)
1535    )]
1536    pub limit: PositiveIntegerDefault10Max10000,
1537    #[cfg_attr(
1538        feature = "network",
1539        serde(default, skip_serializing_if = "Option::is_none")
1540    )]
1541    pub offset: Option<PositiveIntegerOrZero>,
1542    #[cfg_attr(
1543        feature = "network",
1544        serde(
1545            default = "BooleanDefaultTrue::none",
1546            skip_serializing_if = "BooleanDefaultTrue::is_none"
1547        )
1548    )]
1549    pub seek_permission: BooleanDefaultTrue,
1550}
1551
1552/// A wallet UTXO with its metadata.
1553#[derive(Clone, Debug)]
1554#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1555#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1556pub struct Output {
1557    pub satoshis: SatoshiValue,
1558    #[cfg_attr(
1559        feature = "network",
1560        serde(with = "serde_helpers::option_bytes_as_hex")
1561    )]
1562    #[cfg_attr(
1563        feature = "network",
1564        serde(skip_serializing_if = "Option::is_none", default)
1565    )]
1566    pub locking_script: Option<Vec<u8>>,
1567    pub spendable: bool,
1568    #[cfg_attr(
1569        feature = "network",
1570        serde(default, skip_serializing_if = "Option::is_none")
1571    )]
1572    pub custom_instructions: Option<String>,
1573    #[cfg_attr(
1574        feature = "network",
1575        serde(skip_serializing_if = "Vec::is_empty", default)
1576    )]
1577    pub tags: Vec<String>,
1578    pub outpoint: OutpointString,
1579    #[cfg_attr(
1580        feature = "network",
1581        serde(skip_serializing_if = "Vec::is_empty", default)
1582    )]
1583    pub labels: Vec<String>,
1584}
1585
1586/// Paginated list of wallet outputs.
1587#[derive(Clone, Debug)]
1588#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1589#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1590pub struct ListOutputsResult {
1591    pub total_outputs: u32,
1592    #[cfg_attr(
1593        feature = "network",
1594        serde(with = "serde_helpers::option_bytes_as_array")
1595    )]
1596    #[cfg_attr(
1597        feature = "network",
1598        serde(skip_serializing_if = "Option::is_none", default)
1599    )]
1600    #[cfg_attr(feature = "network", serde(rename = "BEEF"))]
1601    pub beef: Option<Vec<u8>>,
1602    pub outputs: Vec<Output>,
1603}
1604
1605// ---------------------------------------------------------------------------
1606// RelinquishOutput
1607// ---------------------------------------------------------------------------
1608
1609/// Arguments for relinquishing ownership of an output.
1610#[derive(Clone, Debug)]
1611#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1612#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1613pub struct RelinquishOutputArgs {
1614    pub basket: BasketStringUnder300Bytes,
1615    pub output: OutpointString,
1616}
1617
1618/// Result of relinquishing an output.
1619#[derive(Clone, Debug)]
1620#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1621#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1622pub struct RelinquishOutputResult {
1623    pub relinquished: bool,
1624}
1625
1626// ---------------------------------------------------------------------------
1627// Key/Crypto types
1628// ---------------------------------------------------------------------------
1629
1630/// Arguments for getting a public key.
1631#[derive(Clone, Debug)]
1632#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1633#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1634pub struct GetPublicKeyArgs {
1635    // TS types this as `identityKey?: true` (literal `true | undefined`),
1636    // so the wire form is either present-and-true or absent. Skip when false
1637    // to match TS's `JSON.stringify(undefined)` omission.
1638    #[cfg_attr(feature = "network", serde(default, skip_serializing_if = "is_false"))]
1639    pub identity_key: bool,
1640    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1641    #[cfg_attr(
1642        feature = "network",
1643        serde(default, skip_serializing_if = "Option::is_none")
1644    )]
1645    pub protocol_id: Option<Protocol>,
1646    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1647    #[cfg_attr(
1648        feature = "network",
1649        serde(default, skip_serializing_if = "Option::is_none")
1650    )]
1651    pub key_id: Option<String>,
1652    #[cfg_attr(
1653        feature = "network",
1654        serde(skip_serializing_if = "Option::is_none", default)
1655    )]
1656    pub counterparty: Option<Counterparty>,
1657    #[cfg_attr(feature = "network", serde(default))]
1658    pub privileged: bool,
1659    #[cfg_attr(
1660        feature = "network",
1661        serde(default, skip_serializing_if = "Option::is_none")
1662    )]
1663    pub privileged_reason: Option<String>,
1664    #[cfg_attr(
1665        feature = "network",
1666        serde(skip_serializing_if = "Option::is_none", default)
1667    )]
1668    pub for_self: Option<bool>,
1669    #[cfg_attr(
1670        feature = "network",
1671        serde(skip_serializing_if = "Option::is_none", default)
1672    )]
1673    pub seek_permission: Option<bool>,
1674}
1675
1676/// Result of getting a public key.
1677#[derive(Clone, Debug)]
1678#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1679#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1680pub struct GetPublicKeyResult {
1681    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1682    pub public_key: PublicKey,
1683}
1684
1685/// Arguments for encryption.
1686#[derive(Clone, Debug)]
1687#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1688#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1689pub struct EncryptArgs {
1690    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1691    pub protocol_id: Protocol,
1692    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1693    pub key_id: String,
1694    #[cfg_attr(feature = "network", serde(default))]
1695    pub counterparty: Counterparty,
1696    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1697    pub plaintext: Vec<u8>,
1698    #[cfg_attr(feature = "network", serde(default))]
1699    pub privileged: bool,
1700    #[cfg_attr(
1701        feature = "network",
1702        serde(default, skip_serializing_if = "Option::is_none")
1703    )]
1704    pub privileged_reason: Option<String>,
1705    #[cfg_attr(
1706        feature = "network",
1707        serde(default, skip_serializing_if = "Option::is_none")
1708    )]
1709    pub seek_permission: Option<bool>,
1710}
1711
1712/// Result of encryption.
1713#[derive(Clone, Debug)]
1714#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1715#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1716pub struct EncryptResult {
1717    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1718    pub ciphertext: Vec<u8>,
1719}
1720
1721/// Arguments for decryption.
1722#[derive(Clone, Debug)]
1723#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1724#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1725pub struct DecryptArgs {
1726    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1727    pub protocol_id: Protocol,
1728    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1729    pub key_id: String,
1730    #[cfg_attr(feature = "network", serde(default))]
1731    pub counterparty: Counterparty,
1732    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1733    pub ciphertext: Vec<u8>,
1734    #[cfg_attr(feature = "network", serde(default))]
1735    pub privileged: bool,
1736    #[cfg_attr(
1737        feature = "network",
1738        serde(default, skip_serializing_if = "Option::is_none")
1739    )]
1740    pub privileged_reason: Option<String>,
1741    #[cfg_attr(
1742        feature = "network",
1743        serde(default, skip_serializing_if = "Option::is_none")
1744    )]
1745    pub seek_permission: Option<bool>,
1746}
1747
1748/// Result of decryption.
1749#[derive(Clone, Debug)]
1750#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1751#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1752pub struct DecryptResult {
1753    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1754    pub plaintext: Vec<u8>,
1755}
1756
1757/// Arguments for creating an HMAC.
1758#[derive(Clone, Debug)]
1759#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1760#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1761pub struct CreateHmacArgs {
1762    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1763    pub protocol_id: Protocol,
1764    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1765    pub key_id: String,
1766    #[cfg_attr(feature = "network", serde(default))]
1767    pub counterparty: Counterparty,
1768    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1769    pub data: Vec<u8>,
1770    #[cfg_attr(feature = "network", serde(default))]
1771    pub privileged: bool,
1772    #[cfg_attr(
1773        feature = "network",
1774        serde(default, skip_serializing_if = "Option::is_none")
1775    )]
1776    pub privileged_reason: Option<String>,
1777    #[cfg_attr(
1778        feature = "network",
1779        serde(default, skip_serializing_if = "Option::is_none")
1780    )]
1781    pub seek_permission: Option<bool>,
1782}
1783
1784/// Result of creating an HMAC.
1785#[derive(Clone, Debug)]
1786#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1787#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1788pub struct CreateHmacResult {
1789    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1790    pub hmac: Vec<u8>,
1791}
1792
1793/// Arguments for verifying an HMAC.
1794#[derive(Clone, Debug)]
1795#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1796#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1797pub struct VerifyHmacArgs {
1798    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1799    pub protocol_id: Protocol,
1800    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1801    pub key_id: String,
1802    #[cfg_attr(feature = "network", serde(default))]
1803    pub counterparty: Counterparty,
1804    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1805    pub data: Vec<u8>,
1806    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1807    pub hmac: Vec<u8>,
1808    #[cfg_attr(feature = "network", serde(default))]
1809    pub privileged: bool,
1810    #[cfg_attr(
1811        feature = "network",
1812        serde(default, skip_serializing_if = "Option::is_none")
1813    )]
1814    pub privileged_reason: Option<String>,
1815    #[cfg_attr(
1816        feature = "network",
1817        serde(default, skip_serializing_if = "Option::is_none")
1818    )]
1819    pub seek_permission: Option<bool>,
1820}
1821
1822/// Result of verifying an HMAC.
1823#[derive(Clone, Debug)]
1824#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1825#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1826pub struct VerifyHmacResult {
1827    pub valid: bool,
1828}
1829
1830/// Arguments for creating a digital signature.
1831#[derive(Clone, Debug)]
1832#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1833#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1834pub struct CreateSignatureArgs {
1835    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1836    pub protocol_id: Protocol,
1837    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1838    pub key_id: String,
1839    #[cfg_attr(feature = "network", serde(default))]
1840    pub counterparty: Counterparty,
1841    #[cfg_attr(
1842        feature = "network",
1843        serde(with = "serde_helpers::option_bytes_as_array")
1844    )]
1845    #[cfg_attr(
1846        feature = "network",
1847        serde(skip_serializing_if = "Option::is_none", default)
1848    )]
1849    pub data: Option<Vec<u8>>,
1850    #[cfg_attr(
1851        feature = "network",
1852        serde(with = "serde_helpers::option_bytes_as_array")
1853    )]
1854    #[cfg_attr(
1855        feature = "network",
1856        serde(skip_serializing_if = "Option::is_none", default)
1857    )]
1858    pub hash_to_directly_sign: Option<Vec<u8>>,
1859    #[cfg_attr(feature = "network", serde(default))]
1860    pub privileged: bool,
1861    #[cfg_attr(
1862        feature = "network",
1863        serde(default, skip_serializing_if = "Option::is_none")
1864    )]
1865    pub privileged_reason: Option<String>,
1866    #[cfg_attr(
1867        feature = "network",
1868        serde(default, skip_serializing_if = "Option::is_none")
1869    )]
1870    pub seek_permission: Option<bool>,
1871}
1872
1873/// Result of creating a digital signature.
1874#[derive(Clone, Debug)]
1875#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1876#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1877pub struct CreateSignatureResult {
1878    // bytes_as_array: TS SDK returns Byte[] (number array), Go SDK uses BytesList.
1879    // NOT bytes_as_hex — BSV Desktop JSON API returns [48, 69, ...] not "3045...".
1880    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1881    pub signature: Vec<u8>,
1882}
1883
1884/// Arguments for verifying a digital signature.
1885#[derive(Clone, Debug)]
1886#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1887#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1888pub struct VerifySignatureArgs {
1889    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1890    pub protocol_id: Protocol,
1891    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1892    pub key_id: String,
1893    #[cfg_attr(feature = "network", serde(default))]
1894    pub counterparty: Counterparty,
1895    #[cfg_attr(
1896        feature = "network",
1897        serde(with = "serde_helpers::option_bytes_as_array")
1898    )]
1899    #[cfg_attr(
1900        feature = "network",
1901        serde(skip_serializing_if = "Option::is_none", default)
1902    )]
1903    pub data: Option<Vec<u8>>,
1904    #[cfg_attr(
1905        feature = "network",
1906        serde(with = "serde_helpers::option_bytes_as_array")
1907    )]
1908    #[cfg_attr(
1909        feature = "network",
1910        serde(skip_serializing_if = "Option::is_none", default)
1911    )]
1912    pub hash_to_directly_verify: Option<Vec<u8>>,
1913    // bytes_as_array: matches TS Byte[] and Go BytesList format.
1914    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1915    pub signature: Vec<u8>,
1916    #[cfg_attr(
1917        feature = "network",
1918        serde(default, skip_serializing_if = "Option::is_none")
1919    )]
1920    pub for_self: Option<bool>,
1921    #[cfg_attr(feature = "network", serde(default))]
1922    pub privileged: bool,
1923    #[cfg_attr(
1924        feature = "network",
1925        serde(default, skip_serializing_if = "Option::is_none")
1926    )]
1927    pub privileged_reason: Option<String>,
1928    #[cfg_attr(
1929        feature = "network",
1930        serde(default, skip_serializing_if = "Option::is_none")
1931    )]
1932    pub seek_permission: Option<bool>,
1933}
1934
1935/// Result of verifying a digital signature.
1936#[derive(Clone, Debug)]
1937#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1938#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1939pub struct VerifySignatureResult {
1940    pub valid: bool,
1941}
1942
1943// ---------------------------------------------------------------------------
1944// Certificate operations
1945// ---------------------------------------------------------------------------
1946
1947/// Arguments for acquiring a new certificate.
1948#[derive(Clone, Debug)]
1949#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1950#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1951pub struct AcquireCertificateArgs {
1952    #[cfg_attr(feature = "network", serde(rename = "type"))]
1953    pub cert_type: CertificateType,
1954    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1955    pub certifier: PublicKey,
1956    pub acquisition_protocol: AcquisitionProtocol,
1957    #[cfg_attr(
1958        feature = "network",
1959        serde(skip_serializing_if = "HashMap::is_empty", default)
1960    )]
1961    pub fields: HashMap<String, String>,
1962    #[cfg_attr(
1963        feature = "network",
1964        serde(default, skip_serializing_if = "Option::is_none")
1965    )]
1966    pub serial_number: Option<SerialNumber>,
1967    #[cfg_attr(
1968        feature = "network",
1969        serde(default, skip_serializing_if = "Option::is_none")
1970    )]
1971    pub revocation_outpoint: Option<String>,
1972    #[cfg_attr(
1973        feature = "network",
1974        serde(with = "serde_helpers::option_bytes_as_hex")
1975    )]
1976    #[cfg_attr(
1977        feature = "network",
1978        serde(skip_serializing_if = "Option::is_none", default)
1979    )]
1980    pub signature: Option<Vec<u8>>,
1981    #[cfg_attr(
1982        feature = "network",
1983        serde(default, skip_serializing_if = "Option::is_none")
1984    )]
1985    pub certifier_url: Option<String>,
1986    #[cfg_attr(
1987        feature = "network",
1988        serde(default, skip_serializing_if = "Option::is_none")
1989    )]
1990    pub keyring_revealer: Option<KeyringRevealer>,
1991    #[cfg_attr(
1992        feature = "network",
1993        serde(default, skip_serializing_if = "Option::is_none")
1994    )]
1995    pub keyring_for_subject: Option<HashMap<String, String>>,
1996    #[cfg_attr(feature = "network", serde(default))]
1997    pub privileged: bool,
1998    #[cfg_attr(
1999        feature = "network",
2000        serde(default, skip_serializing_if = "Option::is_none")
2001    )]
2002    pub privileged_reason: Option<String>,
2003}
2004
2005/// Arguments for listing certificates.
2006#[derive(Clone, Debug)]
2007#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2008#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2009pub struct ListCertificatesArgs {
2010    #[cfg_attr(
2011        feature = "network",
2012        serde(with = "serde_helpers::vec_public_key_hex", default)
2013    )]
2014    pub certifiers: Vec<PublicKey>,
2015    #[cfg_attr(
2016        feature = "network",
2017        serde(skip_serializing_if = "Vec::is_empty", default)
2018    )]
2019    pub types: Vec<CertificateType>,
2020    #[cfg_attr(feature = "network", serde(default))]
2021    pub limit: PositiveIntegerDefault10Max10000,
2022    #[cfg_attr(
2023        feature = "network",
2024        serde(default, skip_serializing_if = "Option::is_none")
2025    )]
2026    pub offset: Option<PositiveIntegerOrZero>,
2027    #[cfg_attr(
2028        feature = "network",
2029        serde(
2030            default = "BooleanDefaultFalse::none",
2031            skip_serializing_if = "BooleanDefaultFalse::is_none"
2032        )
2033    )]
2034    pub privileged: BooleanDefaultFalse,
2035    #[cfg_attr(
2036        feature = "network",
2037        serde(default, skip_serializing_if = "Option::is_none")
2038    )]
2039    pub privileged_reason: Option<String>,
2040    /// Optional partial certificate filter for exact matching.
2041    /// When provided, only certificates matching these fields are returned.
2042    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
2043    pub partial: Option<PartialCertificate>,
2044}
2045
2046/// A certificate with its keyring and verifier.
2047#[derive(Clone, Debug)]
2048#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2049#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2050pub struct CertificateResult {
2051    #[cfg_attr(feature = "network", serde(flatten))]
2052    pub certificate: Certificate,
2053    #[cfg_attr(
2054        feature = "network",
2055        serde(default, skip_serializing_if = "Option::is_none")
2056    )]
2057    pub keyring: Option<HashMap<String, String>>,
2058    #[cfg_attr(
2059        feature = "network",
2060        serde(with = "serde_helpers::option_bytes_as_hex")
2061    )]
2062    #[cfg_attr(
2063        feature = "network",
2064        serde(skip_serializing_if = "Option::is_none", default)
2065    )]
2066    pub verifier: Option<Vec<u8>>,
2067}
2068
2069/// Paginated list of certificates.
2070#[derive(Clone, Debug)]
2071#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2072#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2073pub struct ListCertificatesResult {
2074    pub total_certificates: u32,
2075    pub certificates: Vec<CertificateResult>,
2076}
2077
2078/// Arguments for creating a verifiable certificate.
2079#[derive(Clone, Debug)]
2080#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2081#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2082pub struct ProveCertificateArgs {
2083    pub certificate: PartialCertificate,
2084    pub fields_to_reveal: Vec<String>,
2085    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2086    pub verifier: PublicKey,
2087    #[cfg_attr(
2088        feature = "network",
2089        serde(
2090            default = "BooleanDefaultFalse::none",
2091            skip_serializing_if = "BooleanDefaultFalse::is_none"
2092        )
2093    )]
2094    pub privileged: BooleanDefaultFalse,
2095    #[cfg_attr(
2096        feature = "network",
2097        serde(default, skip_serializing_if = "Option::is_none")
2098    )]
2099    pub privileged_reason: Option<String>,
2100}
2101
2102/// Result of creating a verifiable certificate.
2103#[derive(Clone, Debug)]
2104#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2105#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2106pub struct ProveCertificateResult {
2107    pub keyring_for_verifier: HashMap<String, String>,
2108    #[cfg_attr(
2109        feature = "network",
2110        serde(default, skip_serializing_if = "Option::is_none")
2111    )]
2112    pub certificate: Option<Certificate>,
2113    #[cfg_attr(
2114        feature = "network",
2115        serde(with = "serde_helpers::option_public_key_hex")
2116    )]
2117    #[cfg_attr(feature = "network", serde(default))]
2118    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
2119    pub verifier: Option<PublicKey>,
2120}
2121
2122/// Arguments for relinquishing ownership of a certificate.
2123#[derive(Clone, Debug)]
2124#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2125#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2126pub struct RelinquishCertificateArgs {
2127    #[cfg_attr(feature = "network", serde(rename = "type"))]
2128    pub cert_type: CertificateType,
2129    pub serial_number: SerialNumber,
2130    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2131    pub certifier: PublicKey,
2132}
2133
2134/// Result of relinquishing a certificate.
2135#[derive(Clone, Debug)]
2136#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2137#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2138pub struct RelinquishCertificateResult {
2139    pub relinquished: bool,
2140}
2141
2142// ---------------------------------------------------------------------------
2143// Discovery types
2144// ---------------------------------------------------------------------------
2145
2146/// Information about an entity that issues identity certificates.
2147#[derive(Clone, Debug)]
2148#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2149#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2150pub struct IdentityCertifier {
2151    pub name: String,
2152    pub icon_url: String,
2153    pub description: String,
2154    pub trust: u8,
2155}
2156
2157/// An identity certificate with decoded fields and certifier info.
2158#[derive(Clone, Debug)]
2159#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2160#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2161pub struct IdentityCertificate {
2162    #[cfg_attr(feature = "network", serde(flatten))]
2163    pub certificate: Certificate,
2164    pub certifier_info: IdentityCertifier,
2165    pub publicly_revealed_keyring: HashMap<String, String>,
2166    pub decrypted_fields: HashMap<String, String>,
2167}
2168
2169/// Arguments for discovering certificates by identity key.
2170#[derive(Clone, Debug)]
2171#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2172#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2173pub struct DiscoverByIdentityKeyArgs {
2174    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2175    pub identity_key: PublicKey,
2176    #[cfg_attr(
2177        feature = "network",
2178        serde(default, skip_serializing_if = "Option::is_none")
2179    )]
2180    pub limit: Option<u32>,
2181    #[cfg_attr(
2182        feature = "network",
2183        serde(default, skip_serializing_if = "Option::is_none")
2184    )]
2185    pub offset: Option<u32>,
2186    #[cfg_attr(
2187        feature = "network",
2188        serde(default, skip_serializing_if = "Option::is_none")
2189    )]
2190    pub seek_permission: Option<bool>,
2191}
2192
2193/// Arguments for discovering certificates by attributes.
2194#[derive(Clone, Debug)]
2195#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2196#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2197pub struct DiscoverByAttributesArgs {
2198    pub attributes: HashMap<String, String>,
2199    #[cfg_attr(
2200        feature = "network",
2201        serde(default, skip_serializing_if = "Option::is_none")
2202    )]
2203    pub limit: Option<u32>,
2204    #[cfg_attr(
2205        feature = "network",
2206        serde(default, skip_serializing_if = "Option::is_none")
2207    )]
2208    pub offset: Option<u32>,
2209    #[cfg_attr(
2210        feature = "network",
2211        serde(default, skip_serializing_if = "Option::is_none")
2212    )]
2213    pub seek_permission: Option<bool>,
2214}
2215
2216/// Paginated list of identity certificates found during discovery.
2217#[derive(Clone, Debug)]
2218#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2219#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2220pub struct DiscoverCertificatesResult {
2221    pub total_certificates: u32,
2222    pub certificates: Vec<IdentityCertificate>,
2223}
2224
2225// ---------------------------------------------------------------------------
2226// Key linkage types
2227// ---------------------------------------------------------------------------
2228
2229/// Arguments for revealing key linkage between counterparties.
2230#[derive(Clone, Debug)]
2231#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2232#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2233pub struct RevealCounterpartyKeyLinkageArgs {
2234    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2235    pub counterparty: PublicKey,
2236    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2237    pub verifier: PublicKey,
2238    #[cfg_attr(
2239        feature = "network",
2240        serde(default, skip_serializing_if = "Option::is_none")
2241    )]
2242    pub privileged: Option<bool>,
2243    #[cfg_attr(
2244        feature = "network",
2245        serde(default, skip_serializing_if = "Option::is_none")
2246    )]
2247    pub privileged_reason: Option<String>,
2248}
2249
2250/// Result of revealing counterparty key linkage.
2251#[derive(Clone, Debug)]
2252#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2253#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2254pub struct RevealCounterpartyKeyLinkageResult {
2255    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2256    pub prover: PublicKey,
2257    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2258    pub counterparty: PublicKey,
2259    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2260    pub verifier: PublicKey,
2261    pub revelation_time: String,
2262    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
2263    pub encrypted_linkage: Vec<u8>,
2264    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
2265    pub encrypted_linkage_proof: Vec<u8>,
2266}
2267
2268/// Arguments for revealing specific key linkage.
2269#[derive(Clone, Debug)]
2270#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2271#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2272pub struct RevealSpecificKeyLinkageArgs {
2273    pub counterparty: Counterparty,
2274    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2275    pub verifier: PublicKey,
2276    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
2277    pub protocol_id: Protocol,
2278    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
2279    pub key_id: String,
2280    #[cfg_attr(
2281        feature = "network",
2282        serde(default, skip_serializing_if = "Option::is_none")
2283    )]
2284    pub privileged: Option<bool>,
2285    #[cfg_attr(
2286        feature = "network",
2287        serde(default, skip_serializing_if = "Option::is_none")
2288    )]
2289    pub privileged_reason: Option<String>,
2290}
2291
2292/// Result of revealing specific key linkage.
2293#[derive(Clone, Debug)]
2294#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2295#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2296pub struct RevealSpecificKeyLinkageResult {
2297    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
2298    pub encrypted_linkage: Vec<u8>,
2299    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
2300    pub encrypted_linkage_proof: Vec<u8>,
2301    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2302    pub prover: PublicKey,
2303    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2304    pub verifier: PublicKey,
2305    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
2306    pub counterparty: PublicKey,
2307    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
2308    pub protocol_id: Protocol,
2309    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
2310    pub key_id: String,
2311    pub proof_type: u8,
2312}
2313
2314// ---------------------------------------------------------------------------
2315// Auth/Info types
2316// ---------------------------------------------------------------------------
2317
2318/// Whether the current session is authenticated.
2319#[derive(Clone, Debug)]
2320#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2321#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2322pub struct AuthenticatedResult {
2323    pub authenticated: bool,
2324}
2325
2326/// Current blockchain height.
2327#[derive(Clone, Debug)]
2328#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2329#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2330pub struct GetHeightResult {
2331    pub height: u32,
2332}
2333
2334/// Arguments for retrieving a blockchain header at a specific height.
2335#[derive(Clone, Debug)]
2336#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2337#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2338pub struct GetHeaderArgs {
2339    pub height: u32,
2340}
2341
2342/// Blockchain header data for the requested height.
2343#[derive(Clone, Debug)]
2344#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2345#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2346pub struct GetHeaderResult {
2347    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_hex"))]
2348    pub header: Vec<u8>,
2349}
2350
2351/// Current blockchain network.
2352#[derive(Clone, Debug)]
2353#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2354#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2355pub struct GetNetworkResult {
2356    pub network: Network,
2357}
2358
2359/// Version information about the wallet implementation.
2360#[derive(Clone, Debug)]
2361#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
2362#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
2363pub struct GetVersionResult {
2364    pub version: String,
2365}
2366
2367// ---------------------------------------------------------------------------
2368// WalletInterface trait
2369// ---------------------------------------------------------------------------
2370
2371/// The core wallet interface with all 28 async methods.
2372///
2373/// Uses `#[async_trait]` for object safety -- supports both static dispatch
2374/// (`W: WalletInterface`) and dynamic dispatch (`dyn WalletInterface`).
2375///
2376/// Every method takes `originator: Option<&str>` as the last parameter,
2377/// identifying the calling application domain.
2378#[async_trait]
2379pub trait WalletInterface: Send + Sync {
2380    // -- Action methods --
2381
2382    async fn create_action(
2383        &self,
2384        args: CreateActionArgs,
2385        originator: Option<&str>,
2386    ) -> Result<CreateActionResult, WalletError>;
2387
2388    async fn sign_action(
2389        &self,
2390        args: SignActionArgs,
2391        originator: Option<&str>,
2392    ) -> Result<SignActionResult, WalletError>;
2393
2394    async fn abort_action(
2395        &self,
2396        args: AbortActionArgs,
2397        originator: Option<&str>,
2398    ) -> Result<AbortActionResult, WalletError>;
2399
2400    async fn list_actions(
2401        &self,
2402        args: ListActionsArgs,
2403        originator: Option<&str>,
2404    ) -> Result<ListActionsResult, WalletError>;
2405
2406    async fn internalize_action(
2407        &self,
2408        args: InternalizeActionArgs,
2409        originator: Option<&str>,
2410    ) -> Result<InternalizeActionResult, WalletError>;
2411
2412    // -- Output methods --
2413
2414    async fn list_outputs(
2415        &self,
2416        args: ListOutputsArgs,
2417        originator: Option<&str>,
2418    ) -> Result<ListOutputsResult, WalletError>;
2419
2420    async fn relinquish_output(
2421        &self,
2422        args: RelinquishOutputArgs,
2423        originator: Option<&str>,
2424    ) -> Result<RelinquishOutputResult, WalletError>;
2425
2426    // -- Key/Crypto methods --
2427
2428    async fn get_public_key(
2429        &self,
2430        args: GetPublicKeyArgs,
2431        originator: Option<&str>,
2432    ) -> Result<GetPublicKeyResult, WalletError>;
2433
2434    async fn reveal_counterparty_key_linkage(
2435        &self,
2436        args: RevealCounterpartyKeyLinkageArgs,
2437        originator: Option<&str>,
2438    ) -> Result<RevealCounterpartyKeyLinkageResult, WalletError>;
2439
2440    async fn reveal_specific_key_linkage(
2441        &self,
2442        args: RevealSpecificKeyLinkageArgs,
2443        originator: Option<&str>,
2444    ) -> Result<RevealSpecificKeyLinkageResult, WalletError>;
2445
2446    async fn encrypt(
2447        &self,
2448        args: EncryptArgs,
2449        originator: Option<&str>,
2450    ) -> Result<EncryptResult, WalletError>;
2451
2452    async fn decrypt(
2453        &self,
2454        args: DecryptArgs,
2455        originator: Option<&str>,
2456    ) -> Result<DecryptResult, WalletError>;
2457
2458    async fn create_hmac(
2459        &self,
2460        args: CreateHmacArgs,
2461        originator: Option<&str>,
2462    ) -> Result<CreateHmacResult, WalletError>;
2463
2464    async fn verify_hmac(
2465        &self,
2466        args: VerifyHmacArgs,
2467        originator: Option<&str>,
2468    ) -> Result<VerifyHmacResult, WalletError>;
2469
2470    async fn create_signature(
2471        &self,
2472        args: CreateSignatureArgs,
2473        originator: Option<&str>,
2474    ) -> Result<CreateSignatureResult, WalletError>;
2475
2476    async fn verify_signature(
2477        &self,
2478        args: VerifySignatureArgs,
2479        originator: Option<&str>,
2480    ) -> Result<VerifySignatureResult, WalletError>;
2481
2482    // -- Certificate methods --
2483
2484    async fn acquire_certificate(
2485        &self,
2486        args: AcquireCertificateArgs,
2487        originator: Option<&str>,
2488    ) -> Result<Certificate, WalletError>;
2489
2490    async fn list_certificates(
2491        &self,
2492        args: ListCertificatesArgs,
2493        originator: Option<&str>,
2494    ) -> Result<ListCertificatesResult, WalletError>;
2495
2496    async fn prove_certificate(
2497        &self,
2498        args: ProveCertificateArgs,
2499        originator: Option<&str>,
2500    ) -> Result<ProveCertificateResult, WalletError>;
2501
2502    async fn relinquish_certificate(
2503        &self,
2504        args: RelinquishCertificateArgs,
2505        originator: Option<&str>,
2506    ) -> Result<RelinquishCertificateResult, WalletError>;
2507
2508    // -- Discovery methods --
2509
2510    async fn discover_by_identity_key(
2511        &self,
2512        args: DiscoverByIdentityKeyArgs,
2513        originator: Option<&str>,
2514    ) -> Result<DiscoverCertificatesResult, WalletError>;
2515
2516    async fn discover_by_attributes(
2517        &self,
2518        args: DiscoverByAttributesArgs,
2519        originator: Option<&str>,
2520    ) -> Result<DiscoverCertificatesResult, WalletError>;
2521
2522    // -- Auth/Info methods --
2523
2524    async fn is_authenticated(
2525        &self,
2526        originator: Option<&str>,
2527    ) -> Result<AuthenticatedResult, WalletError>;
2528
2529    async fn wait_for_authentication(
2530        &self,
2531        originator: Option<&str>,
2532    ) -> Result<AuthenticatedResult, WalletError>;
2533
2534    async fn get_height(&self, originator: Option<&str>) -> Result<GetHeightResult, WalletError>;
2535
2536    async fn get_header_for_height(
2537        &self,
2538        args: GetHeaderArgs,
2539        originator: Option<&str>,
2540    ) -> Result<GetHeaderResult, WalletError>;
2541
2542    async fn get_network(&self, originator: Option<&str>) -> Result<GetNetworkResult, WalletError>;
2543
2544    async fn get_version(&self, originator: Option<&str>) -> Result<GetVersionResult, WalletError>;
2545}
2546
2547#[cfg(test)]
2548mod tests {
2549    use super::*;
2550
2551    #[test]
2552    fn test_serial_number_from_string_hex_valid() {
2553        let hex = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2";
2554        let sn = SerialNumber::from_string(hex).unwrap();
2555        assert_eq!(sn.0[0], 0xa1);
2556        assert_eq!(sn.0[31], 0xb2);
2557    }
2558
2559    #[test]
2560    fn test_serial_number_from_string_base64_valid() {
2561        // 32 bytes of zeros -> base64 is "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
2562        let b64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
2563        let sn = SerialNumber::from_string(b64).unwrap();
2564        assert_eq!(sn.0, [0u8; 32]);
2565    }
2566
2567    #[test]
2568    fn test_serial_number_from_string_base64_nonzero() {
2569        // All 0xFF bytes: base64 = "//////////////////////////////////////////8="
2570        let b64 = "//////////////////////////////////////////8=";
2571        let sn = SerialNumber::from_string(b64).unwrap();
2572        assert_eq!(sn.0, [0xffu8; 32]);
2573    }
2574
2575    #[test]
2576    fn test_serial_number_from_string_invalid_length() {
2577        assert!(SerialNumber::from_string("abc").is_err());
2578        assert!(SerialNumber::from_string("").is_err());
2579        assert!(SerialNumber::from_string("a1b2c3").is_err());
2580    }
2581
2582    #[test]
2583    fn test_serial_number_from_string_invalid_chars() {
2584        // 64 chars but not valid hex
2585        let bad_hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
2586        assert!(SerialNumber::from_string(bad_hex).is_err());
2587    }
2588}
2589
2590#[cfg(test)]
2591mod review_action_result_tests {
2592    use super::*;
2593
2594    #[test]
2595    fn test_review_action_result_status_values() {
2596        assert_eq!(ReviewActionResultStatus::Success.as_str(), "success");
2597        assert_eq!(
2598            ReviewActionResultStatus::DoubleSpend.as_str(),
2599            "doubleSpend"
2600        );
2601        assert_eq!(
2602            ReviewActionResultStatus::ServiceError.as_str(),
2603            "serviceError"
2604        );
2605        assert_eq!(ReviewActionResultStatus::InvalidTx.as_str(), "invalidTx");
2606    }
2607
2608    #[cfg(feature = "network")]
2609    #[test]
2610    fn test_review_action_result_serde_roundtrip() {
2611        let r = ReviewActionResult {
2612            txid: "aabb".to_string(),
2613            status: ReviewActionResultStatus::DoubleSpend,
2614            competing_txs: Some(vec!["ccdd".to_string()]),
2615            competing_beef: None,
2616        };
2617        let json = serde_json::to_string(&r).unwrap();
2618        assert!(json.contains("\"doubleSpend\""));
2619        assert!(json.contains("\"competingTxs\""));
2620        // competing_beef should be omitted since None
2621        assert!(!json.contains("\"competingBeef\""));
2622        let r2: ReviewActionResult = serde_json::from_str(&json).unwrap();
2623        assert_eq!(r2.status, ReviewActionResultStatus::DoubleSpend);
2624        assert_eq!(r2.competing_txs.unwrap()[0], "ccdd");
2625    }
2626}