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    pub for_self: Option<bool>,
1435    pub seek_permission: Option<bool>,
1436}
1437
1438/// Result of getting a public key.
1439#[derive(Clone, Debug)]
1440#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1441#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1442pub struct GetPublicKeyResult {
1443    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1444    pub public_key: PublicKey,
1445}
1446
1447/// Arguments for encryption.
1448#[derive(Clone, Debug)]
1449#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1450#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1451pub struct EncryptArgs {
1452    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1453    pub protocol_id: Protocol,
1454    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1455    pub key_id: String,
1456    pub counterparty: Counterparty,
1457    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1458    pub plaintext: Vec<u8>,
1459    pub privileged: bool,
1460    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1461    pub privileged_reason: Option<String>,
1462    pub seek_permission: Option<bool>,
1463}
1464
1465/// Result of encryption.
1466#[derive(Clone, Debug)]
1467#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1468#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1469pub struct EncryptResult {
1470    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1471    pub ciphertext: Vec<u8>,
1472}
1473
1474/// Arguments for decryption.
1475#[derive(Clone, Debug)]
1476#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1477#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1478pub struct DecryptArgs {
1479    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1480    pub protocol_id: Protocol,
1481    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1482    pub key_id: String,
1483    pub counterparty: Counterparty,
1484    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1485    pub ciphertext: Vec<u8>,
1486    pub privileged: bool,
1487    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1488    pub privileged_reason: Option<String>,
1489    pub seek_permission: Option<bool>,
1490}
1491
1492/// Result of decryption.
1493#[derive(Clone, Debug)]
1494#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1495#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1496pub struct DecryptResult {
1497    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1498    pub plaintext: Vec<u8>,
1499}
1500
1501/// Arguments for creating an HMAC.
1502#[derive(Clone, Debug)]
1503#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1504#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1505pub struct CreateHmacArgs {
1506    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1507    pub protocol_id: Protocol,
1508    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1509    pub key_id: String,
1510    pub counterparty: Counterparty,
1511    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1512    pub data: Vec<u8>,
1513    pub privileged: bool,
1514    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1515    pub privileged_reason: Option<String>,
1516    pub seek_permission: Option<bool>,
1517}
1518
1519/// Result of creating an HMAC.
1520#[derive(Clone, Debug)]
1521#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1522#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1523pub struct CreateHmacResult {
1524    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1525    pub hmac: Vec<u8>,
1526}
1527
1528/// Arguments for verifying an HMAC.
1529#[derive(Clone, Debug)]
1530#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1531#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1532pub struct VerifyHmacArgs {
1533    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1534    pub protocol_id: Protocol,
1535    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1536    pub key_id: String,
1537    pub counterparty: Counterparty,
1538    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1539    pub data: Vec<u8>,
1540    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1541    pub hmac: Vec<u8>,
1542    pub privileged: bool,
1543    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1544    pub privileged_reason: Option<String>,
1545    pub seek_permission: Option<bool>,
1546}
1547
1548/// Result of verifying an HMAC.
1549#[derive(Clone, Debug)]
1550#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1551#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1552pub struct VerifyHmacResult {
1553    pub valid: bool,
1554}
1555
1556/// Arguments for creating a digital signature.
1557#[derive(Clone, Debug)]
1558#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1559#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1560pub struct CreateSignatureArgs {
1561    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1562    pub protocol_id: Protocol,
1563    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1564    pub key_id: String,
1565    pub counterparty: Counterparty,
1566    #[cfg_attr(
1567        feature = "network",
1568        serde(with = "serde_helpers::option_bytes_as_array")
1569    )]
1570    #[cfg_attr(
1571        feature = "network",
1572        serde(skip_serializing_if = "Option::is_none", default)
1573    )]
1574    pub data: Option<Vec<u8>>,
1575    #[cfg_attr(
1576        feature = "network",
1577        serde(with = "serde_helpers::option_bytes_as_array")
1578    )]
1579    #[cfg_attr(
1580        feature = "network",
1581        serde(skip_serializing_if = "Option::is_none", default)
1582    )]
1583    pub hash_to_directly_sign: Option<Vec<u8>>,
1584    pub privileged: bool,
1585    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1586    pub privileged_reason: Option<String>,
1587    pub seek_permission: Option<bool>,
1588}
1589
1590/// Result of creating a digital signature.
1591#[derive(Clone, Debug)]
1592#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1593#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1594pub struct CreateSignatureResult {
1595    // bytes_as_array: TS SDK returns Byte[] (number array), Go SDK uses BytesList.
1596    // NOT bytes_as_hex — BSV Desktop JSON API returns [48, 69, ...] not "3045...".
1597    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1598    pub signature: Vec<u8>,
1599}
1600
1601/// Arguments for verifying a digital signature.
1602#[derive(Clone, Debug)]
1603#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1604#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1605pub struct VerifySignatureArgs {
1606    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1607    pub protocol_id: Protocol,
1608    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1609    pub key_id: String,
1610    pub counterparty: Counterparty,
1611    #[cfg_attr(
1612        feature = "network",
1613        serde(with = "serde_helpers::option_bytes_as_array")
1614    )]
1615    #[cfg_attr(
1616        feature = "network",
1617        serde(skip_serializing_if = "Option::is_none", default)
1618    )]
1619    pub data: Option<Vec<u8>>,
1620    #[cfg_attr(
1621        feature = "network",
1622        serde(with = "serde_helpers::option_bytes_as_array")
1623    )]
1624    #[cfg_attr(
1625        feature = "network",
1626        serde(skip_serializing_if = "Option::is_none", default)
1627    )]
1628    pub hash_to_directly_verify: Option<Vec<u8>>,
1629    // bytes_as_array: matches TS Byte[] and Go BytesList format.
1630    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1631    pub signature: Vec<u8>,
1632    pub for_self: Option<bool>,
1633    pub privileged: bool,
1634    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1635    pub privileged_reason: Option<String>,
1636    pub seek_permission: Option<bool>,
1637}
1638
1639/// Result of verifying a digital signature.
1640#[derive(Clone, Debug)]
1641#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1642#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1643pub struct VerifySignatureResult {
1644    pub valid: bool,
1645}
1646
1647// ---------------------------------------------------------------------------
1648// Certificate operations
1649// ---------------------------------------------------------------------------
1650
1651/// Arguments for acquiring a new certificate.
1652#[derive(Clone, Debug)]
1653#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1654#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1655pub struct AcquireCertificateArgs {
1656    #[cfg_attr(feature = "network", serde(rename = "type"))]
1657    pub cert_type: CertificateType,
1658    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1659    pub certifier: PublicKey,
1660    pub acquisition_protocol: AcquisitionProtocol,
1661    #[cfg_attr(
1662        feature = "network",
1663        serde(skip_serializing_if = "HashMap::is_empty", default)
1664    )]
1665    pub fields: HashMap<String, String>,
1666    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1667    pub serial_number: Option<SerialNumber>,
1668    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1669    pub revocation_outpoint: Option<String>,
1670    #[cfg_attr(
1671        feature = "network",
1672        serde(with = "serde_helpers::option_bytes_as_hex")
1673    )]
1674    #[cfg_attr(
1675        feature = "network",
1676        serde(skip_serializing_if = "Option::is_none", default)
1677    )]
1678    pub signature: Option<Vec<u8>>,
1679    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1680    pub certifier_url: Option<String>,
1681    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1682    pub keyring_revealer: Option<KeyringRevealer>,
1683    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1684    pub keyring_for_subject: Option<HashMap<String, String>>,
1685    pub privileged: bool,
1686    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1687    pub privileged_reason: Option<String>,
1688}
1689
1690/// Arguments for listing certificates.
1691#[derive(Clone, Debug)]
1692#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1693#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1694pub struct ListCertificatesArgs {
1695    #[cfg_attr(feature = "network", serde(with = "serde_helpers::vec_public_key_hex"))]
1696    pub certifiers: Vec<PublicKey>,
1697    pub types: Vec<CertificateType>,
1698    pub limit: PositiveIntegerDefault10Max10000,
1699    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1700    pub offset: Option<PositiveIntegerOrZero>,
1701    pub privileged: BooleanDefaultFalse,
1702    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1703    pub privileged_reason: Option<String>,
1704    /// Optional partial certificate filter for exact matching.
1705    /// When provided, only certificates matching these fields are returned.
1706    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1707    pub partial: Option<PartialCertificate>,
1708}
1709
1710/// A certificate with its keyring and verifier.
1711#[derive(Clone, Debug)]
1712#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1713#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1714pub struct CertificateResult {
1715    #[cfg_attr(feature = "network", serde(flatten))]
1716    pub certificate: Certificate,
1717    pub keyring: HashMap<String, String>,
1718    #[cfg_attr(
1719        feature = "network",
1720        serde(with = "serde_helpers::option_bytes_as_hex")
1721    )]
1722    #[cfg_attr(
1723        feature = "network",
1724        serde(skip_serializing_if = "Option::is_none", default)
1725    )]
1726    pub verifier: Option<Vec<u8>>,
1727}
1728
1729/// Paginated list of certificates.
1730#[derive(Clone, Debug)]
1731#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1732#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1733pub struct ListCertificatesResult {
1734    pub total_certificates: u32,
1735    pub certificates: Vec<CertificateResult>,
1736}
1737
1738/// Arguments for creating a verifiable certificate.
1739#[derive(Clone, Debug)]
1740#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1741#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1742pub struct ProveCertificateArgs {
1743    pub certificate: PartialCertificate,
1744    pub fields_to_reveal: Vec<String>,
1745    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1746    pub verifier: PublicKey,
1747    pub privileged: BooleanDefaultFalse,
1748    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1749    pub privileged_reason: Option<String>,
1750}
1751
1752/// Result of creating a verifiable certificate.
1753#[derive(Clone, Debug)]
1754#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1755#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1756pub struct ProveCertificateResult {
1757    pub keyring_for_verifier: HashMap<String, String>,
1758    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1759    pub certificate: Option<Certificate>,
1760    #[cfg_attr(
1761        feature = "network",
1762        serde(with = "serde_helpers::option_public_key_hex")
1763    )]
1764    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1765    pub verifier: Option<PublicKey>,
1766}
1767
1768/// Arguments for relinquishing ownership of a certificate.
1769#[derive(Clone, Debug)]
1770#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1771#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1772pub struct RelinquishCertificateArgs {
1773    #[cfg_attr(feature = "network", serde(rename = "type"))]
1774    pub cert_type: CertificateType,
1775    pub serial_number: SerialNumber,
1776    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1777    pub certifier: PublicKey,
1778}
1779
1780/// Result of relinquishing a certificate.
1781#[derive(Clone, Debug)]
1782#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1783#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1784pub struct RelinquishCertificateResult {
1785    pub relinquished: bool,
1786}
1787
1788// ---------------------------------------------------------------------------
1789// Discovery types
1790// ---------------------------------------------------------------------------
1791
1792/// Information about an entity that issues identity certificates.
1793#[derive(Clone, Debug)]
1794#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1795#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1796pub struct IdentityCertifier {
1797    pub name: String,
1798    pub icon_url: String,
1799    pub description: String,
1800    pub trust: u8,
1801}
1802
1803/// An identity certificate with decoded fields and certifier info.
1804#[derive(Clone, Debug)]
1805#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1806#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1807pub struct IdentityCertificate {
1808    #[cfg_attr(feature = "network", serde(flatten))]
1809    pub certificate: Certificate,
1810    pub certifier_info: IdentityCertifier,
1811    pub publicly_revealed_keyring: HashMap<String, String>,
1812    pub decrypted_fields: HashMap<String, String>,
1813}
1814
1815/// Arguments for discovering certificates by identity key.
1816#[derive(Clone, Debug)]
1817#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1818#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1819pub struct DiscoverByIdentityKeyArgs {
1820    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1821    pub identity_key: PublicKey,
1822    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1823    pub limit: Option<u32>,
1824    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1825    pub offset: Option<u32>,
1826    pub seek_permission: Option<bool>,
1827}
1828
1829/// Arguments for discovering certificates by attributes.
1830#[derive(Clone, Debug)]
1831#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1832#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1833pub struct DiscoverByAttributesArgs {
1834    pub attributes: HashMap<String, String>,
1835    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1836    pub limit: Option<u32>,
1837    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1838    pub offset: Option<u32>,
1839    pub seek_permission: Option<bool>,
1840}
1841
1842/// Paginated list of identity certificates found during discovery.
1843#[derive(Clone, Debug)]
1844#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1845#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1846pub struct DiscoverCertificatesResult {
1847    pub total_certificates: u32,
1848    pub certificates: Vec<IdentityCertificate>,
1849}
1850
1851// ---------------------------------------------------------------------------
1852// Key linkage types
1853// ---------------------------------------------------------------------------
1854
1855/// Arguments for revealing key linkage between counterparties.
1856#[derive(Clone, Debug)]
1857#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1858#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1859pub struct RevealCounterpartyKeyLinkageArgs {
1860    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1861    pub counterparty: PublicKey,
1862    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1863    pub verifier: PublicKey,
1864    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1865    pub privileged: Option<bool>,
1866    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1867    pub privileged_reason: Option<String>,
1868}
1869
1870/// Result of revealing counterparty key linkage.
1871#[derive(Clone, Debug)]
1872#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1873#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1874pub struct RevealCounterpartyKeyLinkageResult {
1875    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1876    pub prover: PublicKey,
1877    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1878    pub counterparty: PublicKey,
1879    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1880    pub verifier: PublicKey,
1881    pub revelation_time: String,
1882    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1883    pub encrypted_linkage: Vec<u8>,
1884    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1885    pub encrypted_linkage_proof: Vec<u8>,
1886}
1887
1888/// Arguments for revealing specific key linkage.
1889#[derive(Clone, Debug)]
1890#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1891#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1892pub struct RevealSpecificKeyLinkageArgs {
1893    pub counterparty: Counterparty,
1894    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1895    pub verifier: PublicKey,
1896    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1897    pub protocol_id: Protocol,
1898    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1899    pub key_id: String,
1900    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1901    pub privileged: Option<bool>,
1902    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1903    pub privileged_reason: Option<String>,
1904}
1905
1906/// Result of revealing specific key linkage.
1907#[derive(Clone, Debug)]
1908#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1909#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1910pub struct RevealSpecificKeyLinkageResult {
1911    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1912    pub encrypted_linkage: Vec<u8>,
1913    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1914    pub encrypted_linkage_proof: Vec<u8>,
1915    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1916    pub prover: PublicKey,
1917    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1918    pub verifier: PublicKey,
1919    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1920    pub counterparty: PublicKey,
1921    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1922    pub protocol_id: Protocol,
1923    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1924    pub key_id: String,
1925    pub proof_type: u8,
1926}
1927
1928// ---------------------------------------------------------------------------
1929// Auth/Info types
1930// ---------------------------------------------------------------------------
1931
1932/// Whether the current session is authenticated.
1933#[derive(Clone, Debug)]
1934#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1935#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1936pub struct AuthenticatedResult {
1937    pub authenticated: bool,
1938}
1939
1940/// Current blockchain height.
1941#[derive(Clone, Debug)]
1942#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1943#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1944pub struct GetHeightResult {
1945    pub height: u32,
1946}
1947
1948/// Arguments for retrieving a blockchain header at a specific height.
1949#[derive(Clone, Debug)]
1950#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1951#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1952pub struct GetHeaderArgs {
1953    pub height: u32,
1954}
1955
1956/// Blockchain header data for the requested height.
1957#[derive(Clone, Debug)]
1958#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1959#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1960pub struct GetHeaderResult {
1961    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_hex"))]
1962    pub header: Vec<u8>,
1963}
1964
1965/// Current blockchain network.
1966#[derive(Clone, Debug)]
1967#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1968#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1969pub struct GetNetworkResult {
1970    pub network: Network,
1971}
1972
1973/// Version information about the wallet implementation.
1974#[derive(Clone, Debug)]
1975#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1976#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1977pub struct GetVersionResult {
1978    pub version: String,
1979}
1980
1981// ---------------------------------------------------------------------------
1982// WalletInterface trait
1983// ---------------------------------------------------------------------------
1984
1985/// The core wallet interface with all 28 async methods.
1986///
1987/// Uses `#[async_trait]` for object safety -- supports both static dispatch
1988/// (`W: WalletInterface`) and dynamic dispatch (`dyn WalletInterface`).
1989///
1990/// Every method takes `originator: Option<&str>` as the last parameter,
1991/// identifying the calling application domain.
1992#[async_trait]
1993pub trait WalletInterface: Send + Sync {
1994    // -- Action methods --
1995
1996    async fn create_action(
1997        &self,
1998        args: CreateActionArgs,
1999        originator: Option<&str>,
2000    ) -> Result<CreateActionResult, WalletError>;
2001
2002    async fn sign_action(
2003        &self,
2004        args: SignActionArgs,
2005        originator: Option<&str>,
2006    ) -> Result<SignActionResult, WalletError>;
2007
2008    async fn abort_action(
2009        &self,
2010        args: AbortActionArgs,
2011        originator: Option<&str>,
2012    ) -> Result<AbortActionResult, WalletError>;
2013
2014    async fn list_actions(
2015        &self,
2016        args: ListActionsArgs,
2017        originator: Option<&str>,
2018    ) -> Result<ListActionsResult, WalletError>;
2019
2020    async fn internalize_action(
2021        &self,
2022        args: InternalizeActionArgs,
2023        originator: Option<&str>,
2024    ) -> Result<InternalizeActionResult, WalletError>;
2025
2026    // -- Output methods --
2027
2028    async fn list_outputs(
2029        &self,
2030        args: ListOutputsArgs,
2031        originator: Option<&str>,
2032    ) -> Result<ListOutputsResult, WalletError>;
2033
2034    async fn relinquish_output(
2035        &self,
2036        args: RelinquishOutputArgs,
2037        originator: Option<&str>,
2038    ) -> Result<RelinquishOutputResult, WalletError>;
2039
2040    // -- Key/Crypto methods --
2041
2042    async fn get_public_key(
2043        &self,
2044        args: GetPublicKeyArgs,
2045        originator: Option<&str>,
2046    ) -> Result<GetPublicKeyResult, WalletError>;
2047
2048    async fn reveal_counterparty_key_linkage(
2049        &self,
2050        args: RevealCounterpartyKeyLinkageArgs,
2051        originator: Option<&str>,
2052    ) -> Result<RevealCounterpartyKeyLinkageResult, WalletError>;
2053
2054    async fn reveal_specific_key_linkage(
2055        &self,
2056        args: RevealSpecificKeyLinkageArgs,
2057        originator: Option<&str>,
2058    ) -> Result<RevealSpecificKeyLinkageResult, WalletError>;
2059
2060    async fn encrypt(
2061        &self,
2062        args: EncryptArgs,
2063        originator: Option<&str>,
2064    ) -> Result<EncryptResult, WalletError>;
2065
2066    async fn decrypt(
2067        &self,
2068        args: DecryptArgs,
2069        originator: Option<&str>,
2070    ) -> Result<DecryptResult, WalletError>;
2071
2072    async fn create_hmac(
2073        &self,
2074        args: CreateHmacArgs,
2075        originator: Option<&str>,
2076    ) -> Result<CreateHmacResult, WalletError>;
2077
2078    async fn verify_hmac(
2079        &self,
2080        args: VerifyHmacArgs,
2081        originator: Option<&str>,
2082    ) -> Result<VerifyHmacResult, WalletError>;
2083
2084    async fn create_signature(
2085        &self,
2086        args: CreateSignatureArgs,
2087        originator: Option<&str>,
2088    ) -> Result<CreateSignatureResult, WalletError>;
2089
2090    async fn verify_signature(
2091        &self,
2092        args: VerifySignatureArgs,
2093        originator: Option<&str>,
2094    ) -> Result<VerifySignatureResult, WalletError>;
2095
2096    // -- Certificate methods --
2097
2098    async fn acquire_certificate(
2099        &self,
2100        args: AcquireCertificateArgs,
2101        originator: Option<&str>,
2102    ) -> Result<Certificate, WalletError>;
2103
2104    async fn list_certificates(
2105        &self,
2106        args: ListCertificatesArgs,
2107        originator: Option<&str>,
2108    ) -> Result<ListCertificatesResult, WalletError>;
2109
2110    async fn prove_certificate(
2111        &self,
2112        args: ProveCertificateArgs,
2113        originator: Option<&str>,
2114    ) -> Result<ProveCertificateResult, WalletError>;
2115
2116    async fn relinquish_certificate(
2117        &self,
2118        args: RelinquishCertificateArgs,
2119        originator: Option<&str>,
2120    ) -> Result<RelinquishCertificateResult, WalletError>;
2121
2122    // -- Discovery methods --
2123
2124    async fn discover_by_identity_key(
2125        &self,
2126        args: DiscoverByIdentityKeyArgs,
2127        originator: Option<&str>,
2128    ) -> Result<DiscoverCertificatesResult, WalletError>;
2129
2130    async fn discover_by_attributes(
2131        &self,
2132        args: DiscoverByAttributesArgs,
2133        originator: Option<&str>,
2134    ) -> Result<DiscoverCertificatesResult, WalletError>;
2135
2136    // -- Auth/Info methods --
2137
2138    async fn is_authenticated(
2139        &self,
2140        originator: Option<&str>,
2141    ) -> Result<AuthenticatedResult, WalletError>;
2142
2143    async fn wait_for_authentication(
2144        &self,
2145        originator: Option<&str>,
2146    ) -> Result<AuthenticatedResult, WalletError>;
2147
2148    async fn get_height(&self, originator: Option<&str>) -> Result<GetHeightResult, WalletError>;
2149
2150    async fn get_header_for_height(
2151        &self,
2152        args: GetHeaderArgs,
2153        originator: Option<&str>,
2154    ) -> Result<GetHeaderResult, WalletError>;
2155
2156    async fn get_network(&self, originator: Option<&str>) -> Result<GetNetworkResult, WalletError>;
2157
2158    async fn get_version(&self, originator: Option<&str>) -> Result<GetVersionResult, WalletError>;
2159}
2160
2161#[cfg(test)]
2162mod tests {
2163    use super::*;
2164
2165    #[test]
2166    fn test_serial_number_from_string_hex_valid() {
2167        let hex = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2";
2168        let sn = SerialNumber::from_string(hex).unwrap();
2169        assert_eq!(sn.0[0], 0xa1);
2170        assert_eq!(sn.0[31], 0xb2);
2171    }
2172
2173    #[test]
2174    fn test_serial_number_from_string_base64_valid() {
2175        // 32 bytes of zeros -> base64 is "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
2176        let b64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
2177        let sn = SerialNumber::from_string(b64).unwrap();
2178        assert_eq!(sn.0, [0u8; 32]);
2179    }
2180
2181    #[test]
2182    fn test_serial_number_from_string_base64_nonzero() {
2183        // All 0xFF bytes: base64 = "//////////////////////////////////////////8="
2184        let b64 = "//////////////////////////////////////////8=";
2185        let sn = SerialNumber::from_string(b64).unwrap();
2186        assert_eq!(sn.0, [0xffu8; 32]);
2187    }
2188
2189    #[test]
2190    fn test_serial_number_from_string_invalid_length() {
2191        assert!(SerialNumber::from_string("abc").is_err());
2192        assert!(SerialNumber::from_string("").is_err());
2193        assert!(SerialNumber::from_string("a1b2c3").is_err());
2194    }
2195
2196    #[test]
2197    fn test_serial_number_from_string_invalid_chars() {
2198        // 64 chars but not valid hex
2199        let bad_hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
2200        assert!(SerialNumber::from_string(bad_hex).is_err());
2201    }
2202}
2203
2204#[cfg(test)]
2205mod review_action_result_tests {
2206    use super::*;
2207
2208    #[test]
2209    fn test_review_action_result_status_values() {
2210        assert_eq!(ReviewActionResultStatus::Success.as_str(), "success");
2211        assert_eq!(
2212            ReviewActionResultStatus::DoubleSpend.as_str(),
2213            "doubleSpend"
2214        );
2215        assert_eq!(
2216            ReviewActionResultStatus::ServiceError.as_str(),
2217            "serviceError"
2218        );
2219        assert_eq!(ReviewActionResultStatus::InvalidTx.as_str(), "invalidTx");
2220    }
2221
2222    #[cfg(feature = "network")]
2223    #[test]
2224    fn test_review_action_result_serde_roundtrip() {
2225        let r = ReviewActionResult {
2226            txid: "aabb".to_string(),
2227            status: ReviewActionResultStatus::DoubleSpend,
2228            competing_txs: Some(vec!["ccdd".to_string()]),
2229            competing_beef: None,
2230        };
2231        let json = serde_json::to_string(&r).unwrap();
2232        assert!(json.contains("\"doubleSpend\""));
2233        assert!(json.contains("\"competingTxs\""));
2234        // competing_beef should be omitted since None
2235        assert!(!json.contains("\"competingBeef\""));
2236        let r2: ReviewActionResult = serde_json::from_str(&json).unwrap();
2237        assert_eq!(r2.status, ReviewActionResultStatus::DoubleSpend);
2238        assert_eq!(r2.competing_txs.unwrap()[0], "ccdd");
2239    }
2240}