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// ---------------------------------------------------------------------------
368// Enums
369// ---------------------------------------------------------------------------
370
371/// Current state of a transaction action.
372#[derive(Clone, Debug, PartialEq, Eq)]
373#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
374#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
375pub enum ActionStatus {
376    Completed,
377    Unprocessed,
378    Sending,
379    Unproven,
380    Unsigned,
381    #[cfg_attr(feature = "network", serde(rename = "nosend"))]
382    NoSend,
383    #[cfg_attr(feature = "network", serde(rename = "nonfinal"))]
384    NonFinal,
385    Failed,
386}
387
388impl ActionStatus {
389    pub fn as_str(&self) -> &'static str {
390        match self {
391            ActionStatus::Completed => "completed",
392            ActionStatus::Unprocessed => "unprocessed",
393            ActionStatus::Sending => "sending",
394            ActionStatus::Unproven => "unproven",
395            ActionStatus::Unsigned => "unsigned",
396            ActionStatus::NoSend => "nosend",
397            ActionStatus::NonFinal => "nonfinal",
398            ActionStatus::Failed => "failed",
399        }
400    }
401}
402
403/// Status of a transaction result (subset of ActionStatus).
404#[derive(Clone, Debug, PartialEq, Eq)]
405#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
406#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
407pub enum ActionResultStatus {
408    Unproven,
409    Sending,
410    Failed,
411}
412
413impl ActionResultStatus {
414    pub fn as_str(&self) -> &'static str {
415        match self {
416            ActionResultStatus::Unproven => "unproven",
417            ActionResultStatus::Sending => "sending",
418            ActionResultStatus::Failed => "failed",
419        }
420    }
421}
422
423/// How multiple criteria are combined in queries.
424#[derive(Clone, Debug, PartialEq, Eq)]
425#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
426#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
427pub enum QueryMode {
428    Any,
429    All,
430}
431
432impl QueryMode {
433    pub fn as_str(&self) -> &'static str {
434        match self {
435            QueryMode::Any => "any",
436            QueryMode::All => "all",
437        }
438    }
439}
440
441/// What additional data to include with output listings.
442#[derive(Clone, Debug, PartialEq, Eq)]
443#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
444pub enum OutputInclude {
445    #[cfg_attr(feature = "network", serde(rename = "locking scripts"))]
446    LockingScripts,
447    #[cfg_attr(feature = "network", serde(rename = "entire transactions"))]
448    EntireTransactions,
449}
450
451impl OutputInclude {
452    pub fn as_str(&self) -> &'static str {
453        match self {
454            OutputInclude::LockingScripts => "locking scripts",
455            OutputInclude::EntireTransactions => "entire transactions",
456        }
457    }
458}
459
460/// Protocol for internalizing transaction outputs.
461#[derive(Clone, Debug, PartialEq, Eq)]
462#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
463pub enum InternalizeProtocol {
464    #[cfg_attr(feature = "network", serde(rename = "wallet payment"))]
465    WalletPayment,
466    #[cfg_attr(feature = "network", serde(rename = "basket insertion"))]
467    BasketInsertion,
468}
469
470impl InternalizeProtocol {
471    pub fn as_str(&self) -> &'static str {
472        match self {
473            InternalizeProtocol::WalletPayment => "wallet payment",
474            InternalizeProtocol::BasketInsertion => "basket insertion",
475        }
476    }
477}
478
479/// Protocol for certificate acquisition.
480#[derive(Clone, Debug, PartialEq, Eq)]
481#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
482#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
483pub enum AcquisitionProtocol {
484    Direct,
485    Issuance,
486}
487
488impl AcquisitionProtocol {
489    pub fn as_str(&self) -> &'static str {
490        match self {
491            AcquisitionProtocol::Direct => "direct",
492            AcquisitionProtocol::Issuance => "issuance",
493        }
494    }
495}
496
497/// Blockchain network type.
498#[derive(Clone, Debug, PartialEq, Eq)]
499#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
500#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
501pub enum Network {
502    Mainnet,
503    Testnet,
504}
505
506impl Network {
507    pub fn as_str(&self) -> &'static str {
508        match self {
509            Network::Mainnet => "mainnet",
510            Network::Testnet => "testnet",
511        }
512    }
513}
514
515/// Trust level for self-referential operations.
516#[derive(Clone, Debug, PartialEq, Eq)]
517#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
518#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
519pub enum TrustSelf {
520    Known,
521}
522
523impl TrustSelf {
524    pub fn as_str(&self) -> &'static str {
525        match self {
526            TrustSelf::Known => "known",
527        }
528    }
529}
530
531// ---------------------------------------------------------------------------
532// Core types: Certificate, CertificateType, SerialNumber, KeyringRevealer
533// ---------------------------------------------------------------------------
534
535/// Newtype wrapper for certificate type identifier (32 bytes).
536/// Serializes as base64 string matching Go SDK Bytes32Base64.
537#[derive(Clone, Debug, PartialEq, Eq, Hash)]
538pub struct CertificateType(pub [u8; 32]);
539
540#[cfg(feature = "network")]
541impl serde::Serialize for CertificateType {
542    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
543        serde_helpers::bytes32_base64::serialize(&self.0, serializer)
544    }
545}
546
547#[cfg(feature = "network")]
548impl<'de> serde::Deserialize<'de> for CertificateType {
549    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
550        serde_helpers::bytes32_base64::deserialize(deserializer).map(CertificateType)
551    }
552}
553
554impl CertificateType {
555    /// Parse a CertificateType from a base64, hex, or raw string.
556    ///
557    /// Accepts base64 (44 chars or ending with '='), hex (64 hex chars),
558    /// or raw byte strings (<=32 bytes).
559    pub fn from_string(s: &str) -> Result<Self, WalletError> {
560        let bytes = if s.len() == 44 || (!s.is_empty() && s.ends_with('=')) {
561            SerialNumber::base64_decode_sn(s)?
562        } else if s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit()) {
563            crate::primitives::utils::from_hex(s)
564                .map_err(|e| WalletError::InvalidParameter(format!("hex: {}", e)))?
565        } else if s.len() <= 32 {
566            let mut buf = [0u8; 32];
567            buf[..s.len()].copy_from_slice(s.as_bytes());
568            return Ok(CertificateType(buf));
569        } else {
570            return Err(WalletError::InvalidParameter(format!(
571                "CertificateType: unsupported string length {}",
572                s.len()
573            )));
574        };
575        if bytes.len() != 32 {
576            return Err(WalletError::InvalidParameter(format!(
577                "CertificateType must decode to 32 bytes, got {}",
578                bytes.len()
579            )));
580        }
581        let mut buf = [0u8; 32];
582        buf.copy_from_slice(&bytes);
583        Ok(CertificateType(buf))
584    }
585
586    pub fn bytes(&self) -> &[u8; 32] {
587        &self.0
588    }
589}
590
591/// Newtype wrapper for certificate serial number (32 bytes).
592/// Serializes as base64 string matching Go SDK Bytes32Base64.
593#[derive(Clone, Debug, PartialEq, Eq, Hash)]
594pub struct SerialNumber(pub [u8; 32]);
595
596#[cfg(feature = "network")]
597impl serde::Serialize for SerialNumber {
598    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
599        serde_helpers::bytes32_base64::serialize(&self.0, serializer)
600    }
601}
602
603#[cfg(feature = "network")]
604impl<'de> serde::Deserialize<'de> for SerialNumber {
605    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
606        serde_helpers::bytes32_base64::deserialize(deserializer).map(SerialNumber)
607    }
608}
609
610impl SerialNumber {
611    /// Parse a SerialNumber from a base64 or hex string.
612    ///
613    /// Accepts:
614    /// - 44-character base64 string (with optional padding, decodes to 32 bytes)
615    /// - 64-character hex string (decodes to 32 bytes)
616    ///
617    /// Returns an error for other formats or if the decoded length is not 32 bytes.
618    pub fn from_string(s: &str) -> Result<Self, WalletError> {
619        let bytes = if s.len() == 44 || (!s.is_empty() && s.ends_with('=')) {
620            // Base64 format (32 bytes -> 44 base64 chars with padding)
621            Self::base64_decode_sn(s)?
622        } else if s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit()) {
623            // Hex format (32 bytes -> 64 hex chars)
624            crate::primitives::utils::from_hex(s)
625                .map_err(|e| WalletError::InvalidParameter(format!("hex: {}", e)))?
626        } else {
627            return Err(WalletError::InvalidParameter(format!(
628                "SerialNumber string must be 44 (base64) or 64 (hex) chars, got {}",
629                s.len()
630            )));
631        };
632        if bytes.len() != 32 {
633            return Err(WalletError::InvalidParameter(
634                "SerialNumber must decode to 32 bytes".into(),
635            ));
636        }
637        let mut buf = [0u8; 32];
638        buf.copy_from_slice(&bytes);
639        Ok(SerialNumber(buf))
640    }
641
642    /// Inline base64 decoder for SerialNumber (self-contained, no cross-module dependency).
643    pub(crate) fn base64_decode_sn(s: &str) -> Result<Vec<u8>, WalletError> {
644        fn b64_val(c: u8) -> Result<u8, WalletError> {
645            match c {
646                b'A'..=b'Z' => Ok(c - b'A'),
647                b'a'..=b'z' => Ok(c - b'a' + 26),
648                b'0'..=b'9' => Ok(c - b'0' + 52),
649                b'+' => Ok(62),
650                b'/' => Ok(63),
651                _ => Err(WalletError::InvalidParameter(format!(
652                    "invalid base64 character: {}",
653                    c as char
654                ))),
655            }
656        }
657        let bytes = s.as_bytes();
658        let mut result = Vec::new();
659        let mut i = 0;
660        while i < bytes.len() {
661            if bytes[i] == b'=' {
662                break;
663            }
664            let a = b64_val(bytes[i])?;
665            let b = if i + 1 < bytes.len() && bytes[i + 1] != b'=' {
666                b64_val(bytes[i + 1])?
667            } else {
668                0
669            };
670            let c = if i + 2 < bytes.len() && bytes[i + 2] != b'=' {
671                b64_val(bytes[i + 2])?
672            } else {
673                0
674            };
675            let d = if i + 3 < bytes.len() && bytes[i + 3] != b'=' {
676                b64_val(bytes[i + 3])?
677            } else {
678                0
679            };
680            let n = (a as u32) << 18 | (b as u32) << 12 | (c as u32) << 6 | (d as u32);
681            result.push((n >> 16) as u8);
682            if i + 2 < bytes.len() && bytes[i + 2] != b'=' {
683                result.push((n >> 8) as u8);
684            }
685            if i + 3 < bytes.len() && bytes[i + 3] != b'=' {
686                result.push(n as u8);
687            }
688            i += 4;
689        }
690        Ok(result)
691    }
692
693    pub fn bytes(&self) -> &[u8; 32] {
694        &self.0
695    }
696}
697
698/// A certificate in the wallet.
699#[derive(Clone, Debug)]
700#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
701#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
702pub struct Certificate {
703    #[cfg_attr(feature = "network", serde(rename = "type"))]
704    pub cert_type: CertificateType,
705    pub serial_number: SerialNumber,
706    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
707    pub subject: PublicKey,
708    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
709    pub certifier: PublicKey,
710    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
711    pub revocation_outpoint: Option<String>,
712    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
713    pub fields: Option<HashMap<String, String>>,
714    #[cfg_attr(
715        feature = "network",
716        serde(with = "serde_helpers::option_bytes_as_hex")
717    )]
718    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
719    pub signature: Option<Vec<u8>>,
720}
721
722/// A partial certificate where all fields are optional.
723/// Used for ProveCertificateArgs to match TS SDK's `Partial<WalletCertificate>`.
724#[derive(Clone, Debug)]
725#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
726#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
727pub struct PartialCertificate {
728    #[cfg_attr(feature = "network", serde(rename = "type"))]
729    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
730    pub cert_type: Option<CertificateType>,
731    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
732    pub serial_number: Option<SerialNumber>,
733    #[cfg_attr(
734        feature = "network",
735        serde(with = "serde_helpers::option_public_key_hex")
736    )]
737    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
738    pub subject: Option<PublicKey>,
739    #[cfg_attr(
740        feature = "network",
741        serde(with = "serde_helpers::option_public_key_hex")
742    )]
743    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
744    pub certifier: Option<PublicKey>,
745    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
746    pub revocation_outpoint: Option<String>,
747    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
748    pub fields: Option<HashMap<String, String>>,
749    #[cfg_attr(
750        feature = "network",
751        serde(with = "serde_helpers::option_bytes_as_hex")
752    )]
753    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
754    pub signature: Option<Vec<u8>>,
755}
756
757impl From<Certificate> for PartialCertificate {
758    fn from(c: Certificate) -> Self {
759        PartialCertificate {
760            cert_type: Some(c.cert_type),
761            serial_number: Some(c.serial_number),
762            subject: Some(c.subject),
763            certifier: Some(c.certifier),
764            revocation_outpoint: c.revocation_outpoint,
765            fields: c.fields,
766            signature: c.signature,
767        }
768    }
769}
770
771/// Identifies who reveals a keyring.
772#[derive(Clone, Debug)]
773pub enum KeyringRevealer {
774    /// The certifier reveals the keyring.
775    Certifier,
776    /// A specific public key reveals the keyring.
777    PubKey(PublicKey),
778}
779
780#[cfg(feature = "network")]
781impl serde::Serialize for KeyringRevealer {
782    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
783        match self {
784            KeyringRevealer::Certifier => serializer.serialize_str("certifier"),
785            KeyringRevealer::PubKey(pk) => serializer.serialize_str(&pk.to_der_hex()),
786        }
787    }
788}
789
790#[cfg(feature = "network")]
791impl<'de> serde::Deserialize<'de> for KeyringRevealer {
792    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
793        let s = String::deserialize(deserializer)?;
794        if s == "certifier" || s.is_empty() {
795            Ok(KeyringRevealer::Certifier)
796        } else {
797            PublicKey::from_string(&s)
798                .map(KeyringRevealer::PubKey)
799                .map_err(serde::de::Error::custom)
800        }
801    }
802}
803
804// ---------------------------------------------------------------------------
805// Action types (CreateAction, SignAction, AbortAction)
806// ---------------------------------------------------------------------------
807
808/// An input to be spent in a new transaction.
809#[derive(Clone, Debug)]
810#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
811#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
812pub struct CreateActionInput {
813    pub outpoint: OutpointString,
814    pub input_description: String,
815    #[cfg_attr(
816        feature = "network",
817        serde(with = "serde_helpers::option_bytes_as_hex")
818    )]
819    #[cfg_attr(
820        feature = "network",
821        serde(skip_serializing_if = "Option::is_none", default)
822    )]
823    pub unlocking_script: Option<Vec<u8>>,
824    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
825    pub unlocking_script_length: Option<u32>,
826    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
827    pub sequence_number: Option<u32>,
828}
829
830/// An output to be created in a new transaction.
831#[derive(Clone, Debug)]
832#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
833#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
834pub struct CreateActionOutput {
835    #[cfg_attr(
836        feature = "network",
837        serde(with = "serde_helpers::option_bytes_as_hex")
838    )]
839    #[cfg_attr(
840        feature = "network",
841        serde(skip_serializing_if = "Option::is_none", default)
842    )]
843    pub locking_script: Option<Vec<u8>>,
844    pub satoshis: SatoshiValue,
845    pub output_description: String,
846    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
847    pub basket: Option<BasketStringUnder300Bytes>,
848    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
849    pub custom_instructions: Option<String>,
850    #[cfg_attr(
851        feature = "network",
852        serde(skip_serializing_if = "Vec::is_empty", default)
853    )]
854    pub tags: Vec<OutputTagStringUnder300Bytes>,
855}
856
857/// Optional parameters for creating a new transaction.
858#[derive(Clone, Debug, Default)]
859#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
860#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
861pub struct CreateActionOptions {
862    pub sign_and_process: BooleanDefaultTrue,
863    pub accept_delayed_broadcast: BooleanDefaultTrue,
864    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
865    pub trust_self: Option<TrustSelf>,
866    #[cfg_attr(
867        feature = "network",
868        serde(skip_serializing_if = "Vec::is_empty", default)
869    )]
870    pub known_txids: Vec<TXIDHexString>,
871    pub return_txid_only: BooleanDefaultFalse,
872    pub no_send: BooleanDefaultFalse,
873    #[cfg_attr(
874        feature = "network",
875        serde(skip_serializing_if = "Vec::is_empty", default)
876    )]
877    pub no_send_change: Vec<OutpointString>,
878    #[cfg_attr(
879        feature = "network",
880        serde(skip_serializing_if = "Vec::is_empty", default)
881    )]
882    pub send_with: Vec<TXIDHexString>,
883    pub randomize_outputs: BooleanDefaultTrue,
884}
885
886/// Arguments for creating a new transaction.
887#[derive(Clone, Debug)]
888#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
889#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
890pub struct CreateActionArgs {
891    pub description: DescriptionString5to50Bytes,
892    #[cfg_attr(
893        feature = "network",
894        serde(with = "serde_helpers::option_bytes_as_array")
895    )]
896    #[cfg_attr(
897        feature = "network",
898        serde(skip_serializing_if = "Option::is_none", default)
899    )]
900    #[cfg_attr(feature = "network", serde(rename = "inputBEEF"))]
901    pub input_beef: Option<Vec<u8>>,
902    #[cfg_attr(
903        feature = "network",
904        serde(skip_serializing_if = "Vec::is_empty", default)
905    )]
906    pub inputs: Vec<CreateActionInput>,
907    #[cfg_attr(
908        feature = "network",
909        serde(skip_serializing_if = "Vec::is_empty", default)
910    )]
911    pub outputs: Vec<CreateActionOutput>,
912    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
913    pub lock_time: Option<u32>,
914    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
915    pub version: Option<u32>,
916    #[cfg_attr(
917        feature = "network",
918        serde(skip_serializing_if = "Vec::is_empty", default)
919    )]
920    pub labels: Vec<LabelStringUnder300Bytes>,
921    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
922    pub options: Option<CreateActionOptions>,
923    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
924    pub reference: Option<String>,
925}
926
927/// Data needed to complete signing of a partial transaction.
928#[derive(Clone, Debug)]
929#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
930#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
931pub struct SignableTransaction {
932    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
933    pub tx: Vec<u8>,
934    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
935    pub reference: Vec<u8>,
936}
937
938/// Status of a transaction sent as part of a batch.
939#[derive(Clone, Debug)]
940#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
941#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
942pub struct SendWithResult {
943    pub txid: TXIDHexString,
944    pub status: ActionResultStatus,
945}
946
947/// Status of a review action result from undelayed broadcast.
948#[derive(Clone, Debug, PartialEq, Eq)]
949#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
950#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
951pub enum ReviewActionResultStatus {
952    Success,
953    DoubleSpend,
954    ServiceError,
955    InvalidTx,
956}
957
958impl ReviewActionResultStatus {
959    pub fn as_str(&self) -> &'static str {
960        match self {
961            ReviewActionResultStatus::Success => "success",
962            ReviewActionResultStatus::DoubleSpend => "doubleSpend",
963            ReviewActionResultStatus::ServiceError => "serviceError",
964            ReviewActionResultStatus::InvalidTx => "invalidTx",
965        }
966    }
967}
968
969/// Result of reviewing a non-delayed broadcast action.
970#[derive(Clone, Debug)]
971#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
972#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
973pub struct ReviewActionResult {
974    pub txid: TXIDHexString,
975    pub status: ReviewActionResultStatus,
976    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
977    pub competing_txs: Option<Vec<String>>,
978    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
979    pub competing_beef: Option<Vec<u8>>,
980}
981
982/// Result of creating a transaction.
983#[derive(Clone, Debug)]
984#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
985#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
986pub struct CreateActionResult {
987    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
988    pub txid: Option<TXIDHexString>,
989    #[cfg_attr(
990        feature = "network",
991        serde(with = "serde_helpers::option_bytes_as_array")
992    )]
993    #[cfg_attr(
994        feature = "network",
995        serde(skip_serializing_if = "Option::is_none", default)
996    )]
997    pub tx: Option<Vec<u8>>,
998    #[cfg_attr(
999        feature = "network",
1000        serde(skip_serializing_if = "Vec::is_empty", default)
1001    )]
1002    pub no_send_change: Vec<OutpointString>,
1003    #[cfg_attr(
1004        feature = "network",
1005        serde(skip_serializing_if = "Vec::is_empty", default)
1006    )]
1007    pub send_with_results: Vec<SendWithResult>,
1008    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1009    pub signable_transaction: Option<SignableTransaction>,
1010}
1011
1012/// Unlocking script and sequence number for a specific input.
1013#[derive(Clone, Debug)]
1014#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1015#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1016pub struct SignActionSpend {
1017    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_hex"))]
1018    pub unlocking_script: Vec<u8>,
1019    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1020    pub sequence_number: Option<u32>,
1021}
1022
1023/// Controls signing and broadcasting behavior.
1024#[derive(Clone, Debug, Default)]
1025#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1026#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1027pub struct SignActionOptions {
1028    pub accept_delayed_broadcast: BooleanDefaultTrue,
1029    pub return_txid_only: BooleanDefaultFalse,
1030    pub no_send: BooleanDefaultFalse,
1031    #[cfg_attr(
1032        feature = "network",
1033        serde(skip_serializing_if = "Vec::is_empty", default)
1034    )]
1035    pub send_with: Vec<TXIDHexString>,
1036}
1037
1038/// Arguments for signing a previously created transaction.
1039#[derive(Clone, Debug)]
1040#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1041#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1042pub struct SignActionArgs {
1043    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1044    pub reference: Vec<u8>,
1045    pub spends: HashMap<u32, SignActionSpend>,
1046    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1047    pub options: Option<SignActionOptions>,
1048}
1049
1050/// Result of a successful signing operation.
1051#[derive(Clone, Debug)]
1052#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1053#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1054pub struct SignActionResult {
1055    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1056    pub txid: Option<TXIDHexString>,
1057    #[cfg_attr(
1058        feature = "network",
1059        serde(with = "serde_helpers::option_bytes_as_array")
1060    )]
1061    #[cfg_attr(
1062        feature = "network",
1063        serde(skip_serializing_if = "Option::is_none", default)
1064    )]
1065    pub tx: Option<Vec<u8>>,
1066    #[cfg_attr(
1067        feature = "network",
1068        serde(skip_serializing_if = "Vec::is_empty", default)
1069    )]
1070    pub send_with_results: Vec<SendWithResult>,
1071}
1072
1073/// Arguments for aborting a transaction.
1074#[derive(Clone, Debug)]
1075#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1076#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1077pub struct AbortActionArgs {
1078    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1079    pub reference: Vec<u8>,
1080}
1081
1082/// Result of aborting a transaction.
1083#[derive(Clone, Debug)]
1084#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1085#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1086pub struct AbortActionResult {
1087    pub aborted: bool,
1088}
1089
1090// ---------------------------------------------------------------------------
1091// Action detail types (for listing)
1092// ---------------------------------------------------------------------------
1093
1094/// A transaction input with full details.
1095#[derive(Clone, Debug)]
1096#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1097#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1098pub struct ActionInput {
1099    pub source_outpoint: OutpointString,
1100    pub source_satoshis: SatoshiValue,
1101    #[cfg_attr(
1102        feature = "network",
1103        serde(with = "serde_helpers::option_bytes_as_hex")
1104    )]
1105    #[cfg_attr(
1106        feature = "network",
1107        serde(skip_serializing_if = "Option::is_none", default)
1108    )]
1109    pub source_locking_script: Option<Vec<u8>>,
1110    #[cfg_attr(
1111        feature = "network",
1112        serde(with = "serde_helpers::option_bytes_as_hex")
1113    )]
1114    #[cfg_attr(
1115        feature = "network",
1116        serde(skip_serializing_if = "Option::is_none", default)
1117    )]
1118    pub unlocking_script: Option<Vec<u8>>,
1119    pub input_description: String,
1120    pub sequence_number: u32,
1121}
1122
1123/// A transaction output with full details.
1124#[derive(Clone, Debug)]
1125#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1126#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1127pub struct ActionOutput {
1128    pub satoshis: SatoshiValue,
1129    #[cfg_attr(
1130        feature = "network",
1131        serde(with = "serde_helpers::option_bytes_as_hex")
1132    )]
1133    #[cfg_attr(
1134        feature = "network",
1135        serde(skip_serializing_if = "Option::is_none", default)
1136    )]
1137    pub locking_script: Option<Vec<u8>>,
1138    pub spendable: bool,
1139    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1140    pub custom_instructions: Option<String>,
1141    pub tags: Vec<String>,
1142    pub output_index: u32,
1143    pub output_description: String,
1144    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1145    pub basket: Option<String>,
1146}
1147
1148/// Full details about a wallet transaction.
1149#[derive(Clone, Debug)]
1150#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1151#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1152pub struct Action {
1153    pub txid: TXIDHexString,
1154    pub satoshis: i64,
1155    pub status: ActionStatus,
1156    pub is_outgoing: bool,
1157    pub description: String,
1158    #[cfg_attr(
1159        feature = "network",
1160        serde(skip_serializing_if = "Vec::is_empty", default)
1161    )]
1162    pub labels: Vec<String>,
1163    pub version: u32,
1164    pub lock_time: u32,
1165    #[cfg_attr(
1166        feature = "network",
1167        serde(skip_serializing_if = "Vec::is_empty", default)
1168    )]
1169    pub inputs: Vec<ActionInput>,
1170    #[cfg_attr(
1171        feature = "network",
1172        serde(skip_serializing_if = "Vec::is_empty", default)
1173    )]
1174    pub outputs: Vec<ActionOutput>,
1175}
1176
1177/// Maximum number of actions or outputs that can be returned.
1178pub const MAX_ACTIONS_LIMIT: u32 = 10000;
1179
1180// ---------------------------------------------------------------------------
1181// ListActions
1182// ---------------------------------------------------------------------------
1183
1184/// Filtering and pagination options for listing wallet transactions.
1185#[derive(Clone, Debug)]
1186#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1187#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1188pub struct ListActionsArgs {
1189    pub labels: Vec<LabelStringUnder300Bytes>,
1190    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1191    pub label_query_mode: Option<QueryMode>,
1192    pub include_labels: BooleanDefaultFalse,
1193    pub include_inputs: BooleanDefaultFalse,
1194    pub include_input_source_locking_scripts: BooleanDefaultFalse,
1195    pub include_input_unlocking_scripts: BooleanDefaultFalse,
1196    pub include_outputs: BooleanDefaultFalse,
1197    pub include_output_locking_scripts: BooleanDefaultFalse,
1198    pub limit: PositiveIntegerDefault10Max10000,
1199    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1200    pub offset: Option<PositiveIntegerOrZero>,
1201    pub seek_permission: BooleanDefaultTrue,
1202}
1203
1204/// Paginated list of wallet transactions.
1205#[derive(Clone, Debug)]
1206#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1207#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1208pub struct ListActionsResult {
1209    pub total_actions: u32,
1210    pub actions: Vec<Action>,
1211}
1212
1213// ---------------------------------------------------------------------------
1214// InternalizeAction
1215// ---------------------------------------------------------------------------
1216
1217/// Derivation and identity data for wallet payment outputs.
1218#[derive(Clone, Debug)]
1219#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1220#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1221pub struct Payment {
1222    // bytes_as_base64: TS SDK types these as Base64String, Go uses default
1223    // json.Marshal for []byte which produces base64. BSV Desktop expects strings.
1224    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_base64"))]
1225    pub derivation_prefix: Vec<u8>,
1226    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_base64"))]
1227    pub derivation_suffix: Vec<u8>,
1228    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1229    pub sender_identity_key: PublicKey,
1230}
1231
1232/// Metadata for outputs being inserted into baskets.
1233#[derive(Clone, Debug)]
1234#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1235#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1236pub struct BasketInsertion {
1237    pub basket: BasketStringUnder300Bytes,
1238    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1239    pub custom_instructions: Option<String>,
1240    #[cfg_attr(
1241        feature = "network",
1242        serde(skip_serializing_if = "Vec::is_empty", default)
1243    )]
1244    pub tags: Vec<OutputTagStringUnder300Bytes>,
1245}
1246
1247/// How to process a transaction output -- as payment or basket insertion.
1248///
1249/// An enum with two variants, encoding the protocol in the variant itself.
1250/// This makes impossible states unrepresentable: a WalletPayment always has
1251/// a Payment, and a BasketInsertion always has a BasketInsertion.
1252#[derive(Clone, Debug)]
1253#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1254#[cfg_attr(feature = "network", serde(tag = "protocol", rename_all = "camelCase"))]
1255pub enum InternalizeOutput {
1256    #[cfg_attr(feature = "network", serde(rename = "wallet payment"))]
1257    WalletPayment {
1258        #[cfg_attr(feature = "network", serde(rename = "outputIndex"))]
1259        output_index: u32,
1260        #[cfg_attr(feature = "network", serde(rename = "paymentRemittance"))]
1261        payment: Payment,
1262    },
1263    #[cfg_attr(feature = "network", serde(rename = "basket insertion"))]
1264    BasketInsertion {
1265        #[cfg_attr(feature = "network", serde(rename = "outputIndex"))]
1266        output_index: u32,
1267        #[cfg_attr(feature = "network", serde(rename = "insertionRemittance"))]
1268        insertion: BasketInsertion,
1269    },
1270}
1271
1272/// Arguments for importing an external transaction into the wallet.
1273#[derive(Clone, Debug)]
1274#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1275#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1276pub struct InternalizeActionArgs {
1277    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1278    pub tx: Vec<u8>,
1279    pub description: String,
1280    #[cfg_attr(
1281        feature = "network",
1282        serde(skip_serializing_if = "Vec::is_empty", default)
1283    )]
1284    pub labels: Vec<LabelStringUnder300Bytes>,
1285    pub seek_permission: BooleanDefaultTrue,
1286    pub outputs: Vec<InternalizeOutput>,
1287}
1288
1289/// Result of internalizing a transaction.
1290#[derive(Clone, Debug)]
1291#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1292#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1293pub struct InternalizeActionResult {
1294    pub accepted: bool,
1295}
1296
1297// ---------------------------------------------------------------------------
1298// ListOutputs
1299// ---------------------------------------------------------------------------
1300
1301/// Filtering and options for listing wallet outputs.
1302#[derive(Clone, Debug)]
1303#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1304#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1305pub struct ListOutputsArgs {
1306    pub basket: BasketStringUnder300Bytes,
1307    #[cfg_attr(
1308        feature = "network",
1309        serde(skip_serializing_if = "Vec::is_empty", default)
1310    )]
1311    pub tags: Vec<OutputTagStringUnder300Bytes>,
1312    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1313    pub tag_query_mode: Option<QueryMode>,
1314    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1315    pub include: Option<OutputInclude>,
1316    #[cfg_attr(
1317        feature = "network",
1318        serde(skip_serializing_if = "BooleanDefaultFalse::is_none")
1319    )]
1320    pub include_custom_instructions: BooleanDefaultFalse,
1321    #[cfg_attr(
1322        feature = "network",
1323        serde(skip_serializing_if = "BooleanDefaultFalse::is_none")
1324    )]
1325    pub include_tags: BooleanDefaultFalse,
1326    #[cfg_attr(
1327        feature = "network",
1328        serde(skip_serializing_if = "BooleanDefaultFalse::is_none")
1329    )]
1330    pub include_labels: BooleanDefaultFalse,
1331    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1332    pub limit: PositiveIntegerDefault10Max10000,
1333    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1334    pub offset: Option<PositiveIntegerOrZero>,
1335    #[cfg_attr(
1336        feature = "network",
1337        serde(skip_serializing_if = "BooleanDefaultTrue::is_none")
1338    )]
1339    pub seek_permission: BooleanDefaultTrue,
1340}
1341
1342/// A wallet UTXO with its metadata.
1343#[derive(Clone, Debug)]
1344#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1345#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1346pub struct Output {
1347    pub satoshis: SatoshiValue,
1348    #[cfg_attr(
1349        feature = "network",
1350        serde(with = "serde_helpers::option_bytes_as_hex")
1351    )]
1352    #[cfg_attr(
1353        feature = "network",
1354        serde(skip_serializing_if = "Option::is_none", default)
1355    )]
1356    pub locking_script: Option<Vec<u8>>,
1357    pub spendable: bool,
1358    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1359    pub custom_instructions: Option<String>,
1360    #[cfg_attr(
1361        feature = "network",
1362        serde(skip_serializing_if = "Vec::is_empty", default)
1363    )]
1364    pub tags: Vec<String>,
1365    pub outpoint: OutpointString,
1366    #[cfg_attr(
1367        feature = "network",
1368        serde(skip_serializing_if = "Vec::is_empty", default)
1369    )]
1370    pub labels: Vec<String>,
1371}
1372
1373/// Paginated list of wallet outputs.
1374#[derive(Clone, Debug)]
1375#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1376#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1377pub struct ListOutputsResult {
1378    pub total_outputs: u32,
1379    #[cfg_attr(
1380        feature = "network",
1381        serde(with = "serde_helpers::option_bytes_as_array")
1382    )]
1383    #[cfg_attr(
1384        feature = "network",
1385        serde(skip_serializing_if = "Option::is_none", default)
1386    )]
1387    #[cfg_attr(feature = "network", serde(rename = "BEEF"))]
1388    pub beef: Option<Vec<u8>>,
1389    pub outputs: Vec<Output>,
1390}
1391
1392// ---------------------------------------------------------------------------
1393// RelinquishOutput
1394// ---------------------------------------------------------------------------
1395
1396/// Arguments for relinquishing ownership of an output.
1397#[derive(Clone, Debug)]
1398#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1399#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1400pub struct RelinquishOutputArgs {
1401    pub basket: BasketStringUnder300Bytes,
1402    pub output: OutpointString,
1403}
1404
1405/// Result of relinquishing an output.
1406#[derive(Clone, Debug)]
1407#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1408#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1409pub struct RelinquishOutputResult {
1410    pub relinquished: bool,
1411}
1412
1413// ---------------------------------------------------------------------------
1414// Key/Crypto types
1415// ---------------------------------------------------------------------------
1416
1417/// Arguments for getting a public key.
1418#[derive(Clone, Debug)]
1419#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1420#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1421pub struct GetPublicKeyArgs {
1422    pub identity_key: bool,
1423    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1424    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1425    pub protocol_id: Option<Protocol>,
1426    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1427    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1428    pub key_id: Option<String>,
1429    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1430    pub counterparty: Option<Counterparty>,
1431    pub privileged: bool,
1432    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1433    pub privileged_reason: Option<String>,
1434    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1435    pub for_self: Option<bool>,
1436    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1437    pub seek_permission: Option<bool>,
1438}
1439
1440/// Result of getting a public key.
1441#[derive(Clone, Debug)]
1442#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1443#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1444pub struct GetPublicKeyResult {
1445    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1446    pub public_key: PublicKey,
1447}
1448
1449/// Arguments for encryption.
1450#[derive(Clone, Debug)]
1451#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1452#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1453pub struct EncryptArgs {
1454    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1455    pub protocol_id: Protocol,
1456    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1457    pub key_id: String,
1458    pub counterparty: Counterparty,
1459    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1460    pub plaintext: Vec<u8>,
1461    pub privileged: bool,
1462    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1463    pub privileged_reason: Option<String>,
1464    pub seek_permission: Option<bool>,
1465}
1466
1467/// Result of encryption.
1468#[derive(Clone, Debug)]
1469#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1470#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1471pub struct EncryptResult {
1472    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1473    pub ciphertext: Vec<u8>,
1474}
1475
1476/// Arguments for decryption.
1477#[derive(Clone, Debug)]
1478#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1479#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1480pub struct DecryptArgs {
1481    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1482    pub protocol_id: Protocol,
1483    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1484    pub key_id: String,
1485    pub counterparty: Counterparty,
1486    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1487    pub ciphertext: Vec<u8>,
1488    pub privileged: bool,
1489    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1490    pub privileged_reason: Option<String>,
1491    pub seek_permission: Option<bool>,
1492}
1493
1494/// Result of decryption.
1495#[derive(Clone, Debug)]
1496#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1497#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1498pub struct DecryptResult {
1499    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1500    pub plaintext: Vec<u8>,
1501}
1502
1503/// Arguments for creating an HMAC.
1504#[derive(Clone, Debug)]
1505#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1506#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1507pub struct CreateHmacArgs {
1508    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1509    pub protocol_id: Protocol,
1510    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1511    pub key_id: String,
1512    pub counterparty: Counterparty,
1513    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1514    pub data: Vec<u8>,
1515    pub privileged: bool,
1516    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1517    pub privileged_reason: Option<String>,
1518    pub seek_permission: Option<bool>,
1519}
1520
1521/// Result of creating an HMAC.
1522#[derive(Clone, Debug)]
1523#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1524#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1525pub struct CreateHmacResult {
1526    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1527    pub hmac: Vec<u8>,
1528}
1529
1530/// Arguments for verifying an HMAC.
1531#[derive(Clone, Debug)]
1532#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1533#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1534pub struct VerifyHmacArgs {
1535    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1536    pub protocol_id: Protocol,
1537    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1538    pub key_id: String,
1539    pub counterparty: Counterparty,
1540    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1541    pub data: Vec<u8>,
1542    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1543    pub hmac: Vec<u8>,
1544    pub privileged: bool,
1545    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1546    pub privileged_reason: Option<String>,
1547    pub seek_permission: Option<bool>,
1548}
1549
1550/// Result of verifying an HMAC.
1551#[derive(Clone, Debug)]
1552#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1553#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1554pub struct VerifyHmacResult {
1555    pub valid: bool,
1556}
1557
1558/// Arguments for creating a digital signature.
1559#[derive(Clone, Debug)]
1560#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1561#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1562pub struct CreateSignatureArgs {
1563    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1564    pub protocol_id: Protocol,
1565    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1566    pub key_id: String,
1567    pub counterparty: Counterparty,
1568    #[cfg_attr(
1569        feature = "network",
1570        serde(with = "serde_helpers::option_bytes_as_array")
1571    )]
1572    #[cfg_attr(
1573        feature = "network",
1574        serde(skip_serializing_if = "Option::is_none", default)
1575    )]
1576    pub data: Option<Vec<u8>>,
1577    #[cfg_attr(
1578        feature = "network",
1579        serde(with = "serde_helpers::option_bytes_as_array")
1580    )]
1581    #[cfg_attr(
1582        feature = "network",
1583        serde(skip_serializing_if = "Option::is_none", default)
1584    )]
1585    pub hash_to_directly_sign: Option<Vec<u8>>,
1586    pub privileged: bool,
1587    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1588    pub privileged_reason: Option<String>,
1589    pub seek_permission: Option<bool>,
1590}
1591
1592/// Result of creating a digital signature.
1593#[derive(Clone, Debug)]
1594#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1595#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1596pub struct CreateSignatureResult {
1597    // bytes_as_array: TS SDK returns Byte[] (number array), Go SDK uses BytesList.
1598    // NOT bytes_as_hex — BSV Desktop JSON API returns [48, 69, ...] not "3045...".
1599    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1600    pub signature: Vec<u8>,
1601}
1602
1603/// Arguments for verifying a digital signature.
1604#[derive(Clone, Debug)]
1605#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1606#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1607pub struct VerifySignatureArgs {
1608    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1609    pub protocol_id: Protocol,
1610    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1611    pub key_id: String,
1612    pub counterparty: Counterparty,
1613    #[cfg_attr(
1614        feature = "network",
1615        serde(with = "serde_helpers::option_bytes_as_array")
1616    )]
1617    #[cfg_attr(
1618        feature = "network",
1619        serde(skip_serializing_if = "Option::is_none", default)
1620    )]
1621    pub data: Option<Vec<u8>>,
1622    #[cfg_attr(
1623        feature = "network",
1624        serde(with = "serde_helpers::option_bytes_as_array")
1625    )]
1626    #[cfg_attr(
1627        feature = "network",
1628        serde(skip_serializing_if = "Option::is_none", default)
1629    )]
1630    pub hash_to_directly_verify: Option<Vec<u8>>,
1631    // bytes_as_array: matches TS Byte[] and Go BytesList format.
1632    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1633    pub signature: Vec<u8>,
1634    pub for_self: Option<bool>,
1635    pub privileged: bool,
1636    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1637    pub privileged_reason: Option<String>,
1638    pub seek_permission: Option<bool>,
1639}
1640
1641/// Result of verifying a digital signature.
1642#[derive(Clone, Debug)]
1643#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1644#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1645pub struct VerifySignatureResult {
1646    pub valid: bool,
1647}
1648
1649// ---------------------------------------------------------------------------
1650// Certificate operations
1651// ---------------------------------------------------------------------------
1652
1653/// Arguments for acquiring a new certificate.
1654#[derive(Clone, Debug)]
1655#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1656#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1657pub struct AcquireCertificateArgs {
1658    #[cfg_attr(feature = "network", serde(rename = "type"))]
1659    pub cert_type: CertificateType,
1660    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1661    pub certifier: PublicKey,
1662    pub acquisition_protocol: AcquisitionProtocol,
1663    #[cfg_attr(
1664        feature = "network",
1665        serde(skip_serializing_if = "HashMap::is_empty", default)
1666    )]
1667    pub fields: HashMap<String, String>,
1668    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1669    pub serial_number: Option<SerialNumber>,
1670    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1671    pub revocation_outpoint: Option<String>,
1672    #[cfg_attr(
1673        feature = "network",
1674        serde(with = "serde_helpers::option_bytes_as_hex")
1675    )]
1676    #[cfg_attr(
1677        feature = "network",
1678        serde(skip_serializing_if = "Option::is_none", default)
1679    )]
1680    pub signature: Option<Vec<u8>>,
1681    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1682    pub certifier_url: Option<String>,
1683    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1684    pub keyring_revealer: Option<KeyringRevealer>,
1685    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1686    pub keyring_for_subject: Option<HashMap<String, String>>,
1687    pub privileged: bool,
1688    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1689    pub privileged_reason: Option<String>,
1690}
1691
1692/// Arguments for listing certificates.
1693#[derive(Clone, Debug)]
1694#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1695#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1696pub struct ListCertificatesArgs {
1697    #[cfg_attr(feature = "network", serde(with = "serde_helpers::vec_public_key_hex"))]
1698    pub certifiers: Vec<PublicKey>,
1699    pub types: Vec<CertificateType>,
1700    pub limit: PositiveIntegerDefault10Max10000,
1701    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1702    pub offset: Option<PositiveIntegerOrZero>,
1703    pub privileged: BooleanDefaultFalse,
1704    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1705    pub privileged_reason: Option<String>,
1706    /// Optional partial certificate filter for exact matching.
1707    /// When provided, only certificates matching these fields are returned.
1708    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1709    pub partial: Option<PartialCertificate>,
1710}
1711
1712/// A certificate with its keyring and verifier.
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 CertificateResult {
1717    #[cfg_attr(feature = "network", serde(flatten))]
1718    pub certificate: Certificate,
1719    pub keyring: HashMap<String, String>,
1720    #[cfg_attr(
1721        feature = "network",
1722        serde(with = "serde_helpers::option_bytes_as_hex")
1723    )]
1724    #[cfg_attr(
1725        feature = "network",
1726        serde(skip_serializing_if = "Option::is_none", default)
1727    )]
1728    pub verifier: Option<Vec<u8>>,
1729}
1730
1731/// Paginated list of certificates.
1732#[derive(Clone, Debug)]
1733#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1734#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1735pub struct ListCertificatesResult {
1736    pub total_certificates: u32,
1737    pub certificates: Vec<CertificateResult>,
1738}
1739
1740/// Arguments for creating a verifiable certificate.
1741#[derive(Clone, Debug)]
1742#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1743#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1744pub struct ProveCertificateArgs {
1745    pub certificate: PartialCertificate,
1746    pub fields_to_reveal: Vec<String>,
1747    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1748    pub verifier: PublicKey,
1749    pub privileged: BooleanDefaultFalse,
1750    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1751    pub privileged_reason: Option<String>,
1752}
1753
1754/// Result of creating a verifiable certificate.
1755#[derive(Clone, Debug)]
1756#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1757#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1758pub struct ProveCertificateResult {
1759    pub keyring_for_verifier: HashMap<String, String>,
1760    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1761    pub certificate: Option<Certificate>,
1762    #[cfg_attr(
1763        feature = "network",
1764        serde(with = "serde_helpers::option_public_key_hex")
1765    )]
1766    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1767    pub verifier: Option<PublicKey>,
1768}
1769
1770/// Arguments for relinquishing ownership of a certificate.
1771#[derive(Clone, Debug)]
1772#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1773#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1774pub struct RelinquishCertificateArgs {
1775    #[cfg_attr(feature = "network", serde(rename = "type"))]
1776    pub cert_type: CertificateType,
1777    pub serial_number: SerialNumber,
1778    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1779    pub certifier: PublicKey,
1780}
1781
1782/// Result of relinquishing a certificate.
1783#[derive(Clone, Debug)]
1784#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1785#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1786pub struct RelinquishCertificateResult {
1787    pub relinquished: bool,
1788}
1789
1790// ---------------------------------------------------------------------------
1791// Discovery types
1792// ---------------------------------------------------------------------------
1793
1794/// Information about an entity that issues identity certificates.
1795#[derive(Clone, Debug)]
1796#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1797#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1798pub struct IdentityCertifier {
1799    pub name: String,
1800    pub icon_url: String,
1801    pub description: String,
1802    pub trust: u8,
1803}
1804
1805/// An identity certificate with decoded fields and certifier info.
1806#[derive(Clone, Debug)]
1807#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1808#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1809pub struct IdentityCertificate {
1810    #[cfg_attr(feature = "network", serde(flatten))]
1811    pub certificate: Certificate,
1812    pub certifier_info: IdentityCertifier,
1813    pub publicly_revealed_keyring: HashMap<String, String>,
1814    pub decrypted_fields: HashMap<String, String>,
1815}
1816
1817/// Arguments for discovering certificates by identity key.
1818#[derive(Clone, Debug)]
1819#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1820#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1821pub struct DiscoverByIdentityKeyArgs {
1822    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1823    pub identity_key: PublicKey,
1824    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1825    pub limit: Option<u32>,
1826    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1827    pub offset: Option<u32>,
1828    pub seek_permission: Option<bool>,
1829}
1830
1831/// Arguments for discovering certificates by attributes.
1832#[derive(Clone, Debug)]
1833#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1834#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1835pub struct DiscoverByAttributesArgs {
1836    pub attributes: HashMap<String, String>,
1837    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1838    pub limit: Option<u32>,
1839    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1840    pub offset: Option<u32>,
1841    pub seek_permission: Option<bool>,
1842}
1843
1844/// Paginated list of identity certificates found during discovery.
1845#[derive(Clone, Debug)]
1846#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1847#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1848pub struct DiscoverCertificatesResult {
1849    pub total_certificates: u32,
1850    pub certificates: Vec<IdentityCertificate>,
1851}
1852
1853// ---------------------------------------------------------------------------
1854// Key linkage types
1855// ---------------------------------------------------------------------------
1856
1857/// Arguments for revealing key linkage between counterparties.
1858#[derive(Clone, Debug)]
1859#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1860#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1861pub struct RevealCounterpartyKeyLinkageArgs {
1862    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1863    pub counterparty: PublicKey,
1864    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1865    pub verifier: PublicKey,
1866    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1867    pub privileged: Option<bool>,
1868    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1869    pub privileged_reason: Option<String>,
1870}
1871
1872/// Result of revealing counterparty key linkage.
1873#[derive(Clone, Debug)]
1874#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1875#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1876pub struct RevealCounterpartyKeyLinkageResult {
1877    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1878    pub prover: PublicKey,
1879    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1880    pub counterparty: PublicKey,
1881    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1882    pub verifier: PublicKey,
1883    pub revelation_time: String,
1884    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1885    pub encrypted_linkage: Vec<u8>,
1886    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1887    pub encrypted_linkage_proof: Vec<u8>,
1888}
1889
1890/// Arguments for revealing specific key linkage.
1891#[derive(Clone, Debug)]
1892#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1893#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1894pub struct RevealSpecificKeyLinkageArgs {
1895    pub counterparty: Counterparty,
1896    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1897    pub verifier: PublicKey,
1898    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1899    pub protocol_id: Protocol,
1900    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1901    pub key_id: String,
1902    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1903    pub privileged: Option<bool>,
1904    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1905    pub privileged_reason: Option<String>,
1906}
1907
1908/// Result of revealing specific key linkage.
1909#[derive(Clone, Debug)]
1910#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1911#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1912pub struct RevealSpecificKeyLinkageResult {
1913    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1914    pub encrypted_linkage: Vec<u8>,
1915    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1916    pub encrypted_linkage_proof: Vec<u8>,
1917    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1918    pub prover: PublicKey,
1919    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1920    pub verifier: PublicKey,
1921    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1922    pub counterparty: PublicKey,
1923    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1924    pub protocol_id: Protocol,
1925    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1926    pub key_id: String,
1927    pub proof_type: u8,
1928}
1929
1930// ---------------------------------------------------------------------------
1931// Auth/Info types
1932// ---------------------------------------------------------------------------
1933
1934/// Whether the current session is authenticated.
1935#[derive(Clone, Debug)]
1936#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1937#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1938pub struct AuthenticatedResult {
1939    pub authenticated: bool,
1940}
1941
1942/// Current blockchain height.
1943#[derive(Clone, Debug)]
1944#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1945#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1946pub struct GetHeightResult {
1947    pub height: u32,
1948}
1949
1950/// Arguments for retrieving a blockchain header at a specific height.
1951#[derive(Clone, Debug)]
1952#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1953#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1954pub struct GetHeaderArgs {
1955    pub height: u32,
1956}
1957
1958/// Blockchain header data for the requested height.
1959#[derive(Clone, Debug)]
1960#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1961#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1962pub struct GetHeaderResult {
1963    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_hex"))]
1964    pub header: Vec<u8>,
1965}
1966
1967/// Current blockchain network.
1968#[derive(Clone, Debug)]
1969#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1970#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1971pub struct GetNetworkResult {
1972    pub network: Network,
1973}
1974
1975/// Version information about the wallet implementation.
1976#[derive(Clone, Debug)]
1977#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1978#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1979pub struct GetVersionResult {
1980    pub version: String,
1981}
1982
1983// ---------------------------------------------------------------------------
1984// WalletInterface trait
1985// ---------------------------------------------------------------------------
1986
1987/// The core wallet interface with all 28 async methods.
1988///
1989/// Uses `#[async_trait]` for object safety -- supports both static dispatch
1990/// (`W: WalletInterface`) and dynamic dispatch (`dyn WalletInterface`).
1991///
1992/// Every method takes `originator: Option<&str>` as the last parameter,
1993/// identifying the calling application domain.
1994#[async_trait]
1995pub trait WalletInterface: Send + Sync {
1996    // -- Action methods --
1997
1998    async fn create_action(
1999        &self,
2000        args: CreateActionArgs,
2001        originator: Option<&str>,
2002    ) -> Result<CreateActionResult, WalletError>;
2003
2004    async fn sign_action(
2005        &self,
2006        args: SignActionArgs,
2007        originator: Option<&str>,
2008    ) -> Result<SignActionResult, WalletError>;
2009
2010    async fn abort_action(
2011        &self,
2012        args: AbortActionArgs,
2013        originator: Option<&str>,
2014    ) -> Result<AbortActionResult, WalletError>;
2015
2016    async fn list_actions(
2017        &self,
2018        args: ListActionsArgs,
2019        originator: Option<&str>,
2020    ) -> Result<ListActionsResult, WalletError>;
2021
2022    async fn internalize_action(
2023        &self,
2024        args: InternalizeActionArgs,
2025        originator: Option<&str>,
2026    ) -> Result<InternalizeActionResult, WalletError>;
2027
2028    // -- Output methods --
2029
2030    async fn list_outputs(
2031        &self,
2032        args: ListOutputsArgs,
2033        originator: Option<&str>,
2034    ) -> Result<ListOutputsResult, WalletError>;
2035
2036    async fn relinquish_output(
2037        &self,
2038        args: RelinquishOutputArgs,
2039        originator: Option<&str>,
2040    ) -> Result<RelinquishOutputResult, WalletError>;
2041
2042    // -- Key/Crypto methods --
2043
2044    async fn get_public_key(
2045        &self,
2046        args: GetPublicKeyArgs,
2047        originator: Option<&str>,
2048    ) -> Result<GetPublicKeyResult, WalletError>;
2049
2050    async fn reveal_counterparty_key_linkage(
2051        &self,
2052        args: RevealCounterpartyKeyLinkageArgs,
2053        originator: Option<&str>,
2054    ) -> Result<RevealCounterpartyKeyLinkageResult, WalletError>;
2055
2056    async fn reveal_specific_key_linkage(
2057        &self,
2058        args: RevealSpecificKeyLinkageArgs,
2059        originator: Option<&str>,
2060    ) -> Result<RevealSpecificKeyLinkageResult, WalletError>;
2061
2062    async fn encrypt(
2063        &self,
2064        args: EncryptArgs,
2065        originator: Option<&str>,
2066    ) -> Result<EncryptResult, WalletError>;
2067
2068    async fn decrypt(
2069        &self,
2070        args: DecryptArgs,
2071        originator: Option<&str>,
2072    ) -> Result<DecryptResult, WalletError>;
2073
2074    async fn create_hmac(
2075        &self,
2076        args: CreateHmacArgs,
2077        originator: Option<&str>,
2078    ) -> Result<CreateHmacResult, WalletError>;
2079
2080    async fn verify_hmac(
2081        &self,
2082        args: VerifyHmacArgs,
2083        originator: Option<&str>,
2084    ) -> Result<VerifyHmacResult, WalletError>;
2085
2086    async fn create_signature(
2087        &self,
2088        args: CreateSignatureArgs,
2089        originator: Option<&str>,
2090    ) -> Result<CreateSignatureResult, WalletError>;
2091
2092    async fn verify_signature(
2093        &self,
2094        args: VerifySignatureArgs,
2095        originator: Option<&str>,
2096    ) -> Result<VerifySignatureResult, WalletError>;
2097
2098    // -- Certificate methods --
2099
2100    async fn acquire_certificate(
2101        &self,
2102        args: AcquireCertificateArgs,
2103        originator: Option<&str>,
2104    ) -> Result<Certificate, WalletError>;
2105
2106    async fn list_certificates(
2107        &self,
2108        args: ListCertificatesArgs,
2109        originator: Option<&str>,
2110    ) -> Result<ListCertificatesResult, WalletError>;
2111
2112    async fn prove_certificate(
2113        &self,
2114        args: ProveCertificateArgs,
2115        originator: Option<&str>,
2116    ) -> Result<ProveCertificateResult, WalletError>;
2117
2118    async fn relinquish_certificate(
2119        &self,
2120        args: RelinquishCertificateArgs,
2121        originator: Option<&str>,
2122    ) -> Result<RelinquishCertificateResult, WalletError>;
2123
2124    // -- Discovery methods --
2125
2126    async fn discover_by_identity_key(
2127        &self,
2128        args: DiscoverByIdentityKeyArgs,
2129        originator: Option<&str>,
2130    ) -> Result<DiscoverCertificatesResult, WalletError>;
2131
2132    async fn discover_by_attributes(
2133        &self,
2134        args: DiscoverByAttributesArgs,
2135        originator: Option<&str>,
2136    ) -> Result<DiscoverCertificatesResult, WalletError>;
2137
2138    // -- Auth/Info methods --
2139
2140    async fn is_authenticated(
2141        &self,
2142        originator: Option<&str>,
2143    ) -> Result<AuthenticatedResult, WalletError>;
2144
2145    async fn wait_for_authentication(
2146        &self,
2147        originator: Option<&str>,
2148    ) -> Result<AuthenticatedResult, WalletError>;
2149
2150    async fn get_height(&self, originator: Option<&str>) -> Result<GetHeightResult, WalletError>;
2151
2152    async fn get_header_for_height(
2153        &self,
2154        args: GetHeaderArgs,
2155        originator: Option<&str>,
2156    ) -> Result<GetHeaderResult, WalletError>;
2157
2158    async fn get_network(&self, originator: Option<&str>) -> Result<GetNetworkResult, WalletError>;
2159
2160    async fn get_version(&self, originator: Option<&str>) -> Result<GetVersionResult, WalletError>;
2161}
2162
2163#[cfg(test)]
2164mod tests {
2165    use super::*;
2166
2167    #[test]
2168    fn test_serial_number_from_string_hex_valid() {
2169        let hex = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2";
2170        let sn = SerialNumber::from_string(hex).unwrap();
2171        assert_eq!(sn.0[0], 0xa1);
2172        assert_eq!(sn.0[31], 0xb2);
2173    }
2174
2175    #[test]
2176    fn test_serial_number_from_string_base64_valid() {
2177        // 32 bytes of zeros -> base64 is "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
2178        let b64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
2179        let sn = SerialNumber::from_string(b64).unwrap();
2180        assert_eq!(sn.0, [0u8; 32]);
2181    }
2182
2183    #[test]
2184    fn test_serial_number_from_string_base64_nonzero() {
2185        // All 0xFF bytes: base64 = "//////////////////////////////////////////8="
2186        let b64 = "//////////////////////////////////////////8=";
2187        let sn = SerialNumber::from_string(b64).unwrap();
2188        assert_eq!(sn.0, [0xffu8; 32]);
2189    }
2190
2191    #[test]
2192    fn test_serial_number_from_string_invalid_length() {
2193        assert!(SerialNumber::from_string("abc").is_err());
2194        assert!(SerialNumber::from_string("").is_err());
2195        assert!(SerialNumber::from_string("a1b2c3").is_err());
2196    }
2197
2198    #[test]
2199    fn test_serial_number_from_string_invalid_chars() {
2200        // 64 chars but not valid hex
2201        let bad_hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
2202        assert!(SerialNumber::from_string(bad_hex).is_err());
2203    }
2204}
2205
2206#[cfg(test)]
2207mod review_action_result_tests {
2208    use super::*;
2209
2210    #[test]
2211    fn test_review_action_result_status_values() {
2212        assert_eq!(ReviewActionResultStatus::Success.as_str(), "success");
2213        assert_eq!(
2214            ReviewActionResultStatus::DoubleSpend.as_str(),
2215            "doubleSpend"
2216        );
2217        assert_eq!(
2218            ReviewActionResultStatus::ServiceError.as_str(),
2219            "serviceError"
2220        );
2221        assert_eq!(ReviewActionResultStatus::InvalidTx.as_str(), "invalidTx");
2222    }
2223
2224    #[cfg(feature = "network")]
2225    #[test]
2226    fn test_review_action_result_serde_roundtrip() {
2227        let r = ReviewActionResult {
2228            txid: "aabb".to_string(),
2229            status: ReviewActionResultStatus::DoubleSpend,
2230            competing_txs: Some(vec!["ccdd".to_string()]),
2231            competing_beef: None,
2232        };
2233        let json = serde_json::to_string(&r).unwrap();
2234        assert!(json.contains("\"doubleSpend\""));
2235        assert!(json.contains("\"competingTxs\""));
2236        // competing_beef should be omitted since None
2237        assert!(!json.contains("\"competingBeef\""));
2238        let r2: ReviewActionResult = serde_json::from_str(&json).unwrap();
2239        assert_eq!(r2.status, ReviewActionResultStatus::DoubleSpend);
2240        assert_eq!(r2.competing_txs.unwrap()[0], "ccdd");
2241    }
2242}