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/// Result of creating a transaction.
948#[derive(Clone, Debug)]
949#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
950#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
951pub struct CreateActionResult {
952    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
953    pub txid: Option<TXIDHexString>,
954    #[cfg_attr(
955        feature = "network",
956        serde(with = "serde_helpers::option_bytes_as_array")
957    )]
958    #[cfg_attr(
959        feature = "network",
960        serde(skip_serializing_if = "Option::is_none", default)
961    )]
962    pub tx: Option<Vec<u8>>,
963    #[cfg_attr(
964        feature = "network",
965        serde(skip_serializing_if = "Vec::is_empty", default)
966    )]
967    pub no_send_change: Vec<OutpointString>,
968    #[cfg_attr(
969        feature = "network",
970        serde(skip_serializing_if = "Vec::is_empty", default)
971    )]
972    pub send_with_results: Vec<SendWithResult>,
973    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
974    pub signable_transaction: Option<SignableTransaction>,
975}
976
977/// Unlocking script and sequence number for a specific input.
978#[derive(Clone, Debug)]
979#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
980#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
981pub struct SignActionSpend {
982    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_hex"))]
983    pub unlocking_script: Vec<u8>,
984    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
985    pub sequence_number: Option<u32>,
986}
987
988/// Controls signing and broadcasting behavior.
989#[derive(Clone, Debug, Default)]
990#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
991#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
992pub struct SignActionOptions {
993    pub accept_delayed_broadcast: BooleanDefaultTrue,
994    pub return_txid_only: BooleanDefaultFalse,
995    pub no_send: BooleanDefaultFalse,
996    #[cfg_attr(
997        feature = "network",
998        serde(skip_serializing_if = "Vec::is_empty", default)
999    )]
1000    pub send_with: Vec<TXIDHexString>,
1001}
1002
1003/// Arguments for signing a previously created transaction.
1004#[derive(Clone, Debug)]
1005#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1006#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1007pub struct SignActionArgs {
1008    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1009    pub reference: Vec<u8>,
1010    pub spends: HashMap<u32, SignActionSpend>,
1011    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1012    pub options: Option<SignActionOptions>,
1013}
1014
1015/// Result of a successful signing operation.
1016#[derive(Clone, Debug)]
1017#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1018#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1019pub struct SignActionResult {
1020    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1021    pub txid: Option<TXIDHexString>,
1022    #[cfg_attr(
1023        feature = "network",
1024        serde(with = "serde_helpers::option_bytes_as_array")
1025    )]
1026    #[cfg_attr(
1027        feature = "network",
1028        serde(skip_serializing_if = "Option::is_none", default)
1029    )]
1030    pub tx: Option<Vec<u8>>,
1031    #[cfg_attr(
1032        feature = "network",
1033        serde(skip_serializing_if = "Vec::is_empty", default)
1034    )]
1035    pub send_with_results: Vec<SendWithResult>,
1036}
1037
1038/// Arguments for aborting a 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 AbortActionArgs {
1043    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1044    pub reference: Vec<u8>,
1045}
1046
1047/// Result of aborting a transaction.
1048#[derive(Clone, Debug)]
1049#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1050#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1051pub struct AbortActionResult {
1052    pub aborted: bool,
1053}
1054
1055// ---------------------------------------------------------------------------
1056// Action detail types (for listing)
1057// ---------------------------------------------------------------------------
1058
1059/// A transaction input with full details.
1060#[derive(Clone, Debug)]
1061#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1062#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1063pub struct ActionInput {
1064    pub source_outpoint: OutpointString,
1065    pub source_satoshis: SatoshiValue,
1066    #[cfg_attr(
1067        feature = "network",
1068        serde(with = "serde_helpers::option_bytes_as_hex")
1069    )]
1070    #[cfg_attr(
1071        feature = "network",
1072        serde(skip_serializing_if = "Option::is_none", default)
1073    )]
1074    pub source_locking_script: Option<Vec<u8>>,
1075    #[cfg_attr(
1076        feature = "network",
1077        serde(with = "serde_helpers::option_bytes_as_hex")
1078    )]
1079    #[cfg_attr(
1080        feature = "network",
1081        serde(skip_serializing_if = "Option::is_none", default)
1082    )]
1083    pub unlocking_script: Option<Vec<u8>>,
1084    pub input_description: String,
1085    pub sequence_number: u32,
1086}
1087
1088/// A transaction output with full details.
1089#[derive(Clone, Debug)]
1090#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1091#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1092pub struct ActionOutput {
1093    pub satoshis: SatoshiValue,
1094    #[cfg_attr(
1095        feature = "network",
1096        serde(with = "serde_helpers::option_bytes_as_hex")
1097    )]
1098    #[cfg_attr(
1099        feature = "network",
1100        serde(skip_serializing_if = "Option::is_none", default)
1101    )]
1102    pub locking_script: Option<Vec<u8>>,
1103    pub spendable: bool,
1104    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1105    pub custom_instructions: Option<String>,
1106    pub tags: Vec<String>,
1107    pub output_index: u32,
1108    pub output_description: String,
1109    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1110    pub basket: Option<String>,
1111}
1112
1113/// Full details about a wallet transaction.
1114#[derive(Clone, Debug)]
1115#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1116#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1117pub struct Action {
1118    pub txid: TXIDHexString,
1119    pub satoshis: i64,
1120    pub status: ActionStatus,
1121    pub is_outgoing: bool,
1122    pub description: String,
1123    #[cfg_attr(
1124        feature = "network",
1125        serde(skip_serializing_if = "Vec::is_empty", default)
1126    )]
1127    pub labels: Vec<String>,
1128    pub version: u32,
1129    pub lock_time: u32,
1130    #[cfg_attr(
1131        feature = "network",
1132        serde(skip_serializing_if = "Vec::is_empty", default)
1133    )]
1134    pub inputs: Vec<ActionInput>,
1135    #[cfg_attr(
1136        feature = "network",
1137        serde(skip_serializing_if = "Vec::is_empty", default)
1138    )]
1139    pub outputs: Vec<ActionOutput>,
1140}
1141
1142/// Maximum number of actions or outputs that can be returned.
1143pub const MAX_ACTIONS_LIMIT: u32 = 10000;
1144
1145// ---------------------------------------------------------------------------
1146// ListActions
1147// ---------------------------------------------------------------------------
1148
1149/// Filtering and pagination options for listing wallet transactions.
1150#[derive(Clone, Debug)]
1151#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1152#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1153pub struct ListActionsArgs {
1154    pub labels: Vec<LabelStringUnder300Bytes>,
1155    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1156    pub label_query_mode: Option<QueryMode>,
1157    pub include_labels: BooleanDefaultFalse,
1158    pub include_inputs: BooleanDefaultFalse,
1159    pub include_input_source_locking_scripts: BooleanDefaultFalse,
1160    pub include_input_unlocking_scripts: BooleanDefaultFalse,
1161    pub include_outputs: BooleanDefaultFalse,
1162    pub include_output_locking_scripts: BooleanDefaultFalse,
1163    pub limit: PositiveIntegerDefault10Max10000,
1164    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1165    pub offset: Option<PositiveIntegerOrZero>,
1166    pub seek_permission: BooleanDefaultTrue,
1167}
1168
1169/// Paginated list of wallet transactions.
1170#[derive(Clone, Debug)]
1171#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1172#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1173pub struct ListActionsResult {
1174    pub total_actions: u32,
1175    pub actions: Vec<Action>,
1176}
1177
1178// ---------------------------------------------------------------------------
1179// InternalizeAction
1180// ---------------------------------------------------------------------------
1181
1182/// Derivation and identity data for wallet payment outputs.
1183#[derive(Clone, Debug)]
1184#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1185#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1186pub struct Payment {
1187    // bytes_as_base64: TS SDK types these as Base64String, Go uses default
1188    // json.Marshal for []byte which produces base64. BSV Desktop expects strings.
1189    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_base64"))]
1190    pub derivation_prefix: Vec<u8>,
1191    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_base64"))]
1192    pub derivation_suffix: Vec<u8>,
1193    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1194    pub sender_identity_key: PublicKey,
1195}
1196
1197/// Metadata for outputs being inserted into baskets.
1198#[derive(Clone, Debug)]
1199#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1200#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1201pub struct BasketInsertion {
1202    pub basket: BasketStringUnder300Bytes,
1203    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1204    pub custom_instructions: Option<String>,
1205    #[cfg_attr(
1206        feature = "network",
1207        serde(skip_serializing_if = "Vec::is_empty", default)
1208    )]
1209    pub tags: Vec<OutputTagStringUnder300Bytes>,
1210}
1211
1212/// How to process a transaction output -- as payment or basket insertion.
1213///
1214/// An enum with two variants, encoding the protocol in the variant itself.
1215/// This makes impossible states unrepresentable: a WalletPayment always has
1216/// a Payment, and a BasketInsertion always has a BasketInsertion.
1217#[derive(Clone, Debug)]
1218#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1219#[cfg_attr(feature = "network", serde(tag = "protocol", rename_all = "camelCase"))]
1220pub enum InternalizeOutput {
1221    #[cfg_attr(feature = "network", serde(rename = "wallet payment"))]
1222    WalletPayment {
1223        output_index: u32,
1224        #[cfg_attr(feature = "network", serde(rename = "paymentRemittance"))]
1225        payment: Payment,
1226    },
1227    #[cfg_attr(feature = "network", serde(rename = "basket insertion"))]
1228    BasketInsertion {
1229        output_index: u32,
1230        #[cfg_attr(feature = "network", serde(rename = "insertionRemittance"))]
1231        insertion: BasketInsertion,
1232    },
1233}
1234
1235/// Arguments for importing an external transaction into the wallet.
1236#[derive(Clone, Debug)]
1237#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1238#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1239pub struct InternalizeActionArgs {
1240    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1241    pub tx: Vec<u8>,
1242    pub description: String,
1243    #[cfg_attr(
1244        feature = "network",
1245        serde(skip_serializing_if = "Vec::is_empty", default)
1246    )]
1247    pub labels: Vec<LabelStringUnder300Bytes>,
1248    pub seek_permission: BooleanDefaultTrue,
1249    pub outputs: Vec<InternalizeOutput>,
1250}
1251
1252/// Result of internalizing a transaction.
1253#[derive(Clone, Debug)]
1254#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1255#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1256pub struct InternalizeActionResult {
1257    pub accepted: bool,
1258}
1259
1260// ---------------------------------------------------------------------------
1261// ListOutputs
1262// ---------------------------------------------------------------------------
1263
1264/// Filtering and options for listing wallet outputs.
1265#[derive(Clone, Debug)]
1266#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1267#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1268pub struct ListOutputsArgs {
1269    pub basket: BasketStringUnder300Bytes,
1270    #[cfg_attr(
1271        feature = "network",
1272        serde(skip_serializing_if = "Vec::is_empty", default)
1273    )]
1274    pub tags: Vec<OutputTagStringUnder300Bytes>,
1275    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1276    pub tag_query_mode: Option<QueryMode>,
1277    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1278    pub include: Option<OutputInclude>,
1279    #[cfg_attr(
1280        feature = "network",
1281        serde(skip_serializing_if = "BooleanDefaultFalse::is_none")
1282    )]
1283    pub include_custom_instructions: BooleanDefaultFalse,
1284    #[cfg_attr(
1285        feature = "network",
1286        serde(skip_serializing_if = "BooleanDefaultFalse::is_none")
1287    )]
1288    pub include_tags: BooleanDefaultFalse,
1289    #[cfg_attr(
1290        feature = "network",
1291        serde(skip_serializing_if = "BooleanDefaultFalse::is_none")
1292    )]
1293    pub include_labels: BooleanDefaultFalse,
1294    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1295    pub limit: PositiveIntegerDefault10Max10000,
1296    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1297    pub offset: Option<PositiveIntegerOrZero>,
1298    #[cfg_attr(
1299        feature = "network",
1300        serde(skip_serializing_if = "BooleanDefaultTrue::is_none")
1301    )]
1302    pub seek_permission: BooleanDefaultTrue,
1303}
1304
1305/// A wallet UTXO with its metadata.
1306#[derive(Clone, Debug)]
1307#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1308#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1309pub struct Output {
1310    pub satoshis: SatoshiValue,
1311    #[cfg_attr(
1312        feature = "network",
1313        serde(with = "serde_helpers::option_bytes_as_hex")
1314    )]
1315    #[cfg_attr(
1316        feature = "network",
1317        serde(skip_serializing_if = "Option::is_none", default)
1318    )]
1319    pub locking_script: Option<Vec<u8>>,
1320    pub spendable: bool,
1321    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1322    pub custom_instructions: Option<String>,
1323    #[cfg_attr(
1324        feature = "network",
1325        serde(skip_serializing_if = "Vec::is_empty", default)
1326    )]
1327    pub tags: Vec<String>,
1328    pub outpoint: OutpointString,
1329    #[cfg_attr(
1330        feature = "network",
1331        serde(skip_serializing_if = "Vec::is_empty", default)
1332    )]
1333    pub labels: Vec<String>,
1334}
1335
1336/// Paginated list of wallet outputs.
1337#[derive(Clone, Debug)]
1338#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1339#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1340pub struct ListOutputsResult {
1341    pub total_outputs: u32,
1342    #[cfg_attr(
1343        feature = "network",
1344        serde(with = "serde_helpers::option_bytes_as_array")
1345    )]
1346    #[cfg_attr(
1347        feature = "network",
1348        serde(skip_serializing_if = "Option::is_none", default)
1349    )]
1350    #[cfg_attr(feature = "network", serde(rename = "BEEF"))]
1351    pub beef: Option<Vec<u8>>,
1352    pub outputs: Vec<Output>,
1353}
1354
1355// ---------------------------------------------------------------------------
1356// RelinquishOutput
1357// ---------------------------------------------------------------------------
1358
1359/// Arguments for relinquishing ownership of an output.
1360#[derive(Clone, Debug)]
1361#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1362#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1363pub struct RelinquishOutputArgs {
1364    pub basket: BasketStringUnder300Bytes,
1365    pub output: OutpointString,
1366}
1367
1368/// Result of relinquishing an output.
1369#[derive(Clone, Debug)]
1370#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1371#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1372pub struct RelinquishOutputResult {
1373    pub relinquished: bool,
1374}
1375
1376// ---------------------------------------------------------------------------
1377// Key/Crypto types
1378// ---------------------------------------------------------------------------
1379
1380/// Arguments for getting a public key.
1381#[derive(Clone, Debug)]
1382#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1383#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1384pub struct GetPublicKeyArgs {
1385    pub identity_key: bool,
1386    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1387    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1388    pub protocol_id: Option<Protocol>,
1389    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1390    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1391    pub key_id: Option<String>,
1392    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1393    pub counterparty: Option<Counterparty>,
1394    pub privileged: bool,
1395    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1396    pub privileged_reason: Option<String>,
1397    pub for_self: Option<bool>,
1398    pub seek_permission: Option<bool>,
1399}
1400
1401/// Result of getting a public key.
1402#[derive(Clone, Debug)]
1403#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1404#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1405pub struct GetPublicKeyResult {
1406    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1407    pub public_key: PublicKey,
1408}
1409
1410/// Arguments for encryption.
1411#[derive(Clone, Debug)]
1412#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1413#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1414pub struct EncryptArgs {
1415    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1416    pub protocol_id: Protocol,
1417    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1418    pub key_id: String,
1419    pub counterparty: Counterparty,
1420    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1421    pub plaintext: Vec<u8>,
1422    pub privileged: bool,
1423    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1424    pub privileged_reason: Option<String>,
1425    pub seek_permission: Option<bool>,
1426}
1427
1428/// Result of encryption.
1429#[derive(Clone, Debug)]
1430#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1431#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1432pub struct EncryptResult {
1433    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1434    pub ciphertext: Vec<u8>,
1435}
1436
1437/// Arguments for decryption.
1438#[derive(Clone, Debug)]
1439#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1440#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1441pub struct DecryptArgs {
1442    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1443    pub protocol_id: Protocol,
1444    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1445    pub key_id: String,
1446    pub counterparty: Counterparty,
1447    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1448    pub ciphertext: Vec<u8>,
1449    pub privileged: bool,
1450    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1451    pub privileged_reason: Option<String>,
1452    pub seek_permission: Option<bool>,
1453}
1454
1455/// Result of decryption.
1456#[derive(Clone, Debug)]
1457#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1458#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1459pub struct DecryptResult {
1460    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1461    pub plaintext: Vec<u8>,
1462}
1463
1464/// Arguments for creating an HMAC.
1465#[derive(Clone, Debug)]
1466#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1467#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1468pub struct CreateHmacArgs {
1469    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1470    pub protocol_id: Protocol,
1471    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1472    pub key_id: String,
1473    pub counterparty: Counterparty,
1474    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1475    pub data: Vec<u8>,
1476    pub privileged: bool,
1477    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1478    pub privileged_reason: Option<String>,
1479    pub seek_permission: Option<bool>,
1480}
1481
1482/// Result of creating an HMAC.
1483#[derive(Clone, Debug)]
1484#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1485#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1486pub struct CreateHmacResult {
1487    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1488    pub hmac: Vec<u8>,
1489}
1490
1491/// Arguments for verifying an HMAC.
1492#[derive(Clone, Debug)]
1493#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1494#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1495pub struct VerifyHmacArgs {
1496    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1497    pub protocol_id: Protocol,
1498    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1499    pub key_id: String,
1500    pub counterparty: Counterparty,
1501    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1502    pub data: Vec<u8>,
1503    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1504    pub hmac: Vec<u8>,
1505    pub privileged: bool,
1506    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1507    pub privileged_reason: Option<String>,
1508    pub seek_permission: Option<bool>,
1509}
1510
1511/// Result of verifying an HMAC.
1512#[derive(Clone, Debug)]
1513#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1514#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1515pub struct VerifyHmacResult {
1516    pub valid: bool,
1517}
1518
1519/// Arguments for creating a digital signature.
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 CreateSignatureArgs {
1524    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1525    pub protocol_id: Protocol,
1526    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1527    pub key_id: String,
1528    pub counterparty: Counterparty,
1529    #[cfg_attr(
1530        feature = "network",
1531        serde(with = "serde_helpers::option_bytes_as_array")
1532    )]
1533    #[cfg_attr(
1534        feature = "network",
1535        serde(skip_serializing_if = "Option::is_none", default)
1536    )]
1537    pub data: Option<Vec<u8>>,
1538    #[cfg_attr(
1539        feature = "network",
1540        serde(with = "serde_helpers::option_bytes_as_array")
1541    )]
1542    #[cfg_attr(
1543        feature = "network",
1544        serde(skip_serializing_if = "Option::is_none", default)
1545    )]
1546    pub hash_to_directly_sign: Option<Vec<u8>>,
1547    pub privileged: bool,
1548    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1549    pub privileged_reason: Option<String>,
1550    pub seek_permission: Option<bool>,
1551}
1552
1553/// Result of creating a digital signature.
1554#[derive(Clone, Debug)]
1555#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1556#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1557pub struct CreateSignatureResult {
1558    // bytes_as_array: TS SDK returns Byte[] (number array), Go SDK uses BytesList.
1559    // NOT bytes_as_hex — BSV Desktop JSON API returns [48, 69, ...] not "3045...".
1560    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1561    pub signature: Vec<u8>,
1562}
1563
1564/// Arguments for verifying a digital signature.
1565#[derive(Clone, Debug)]
1566#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1567#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1568pub struct VerifySignatureArgs {
1569    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1570    pub protocol_id: Protocol,
1571    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1572    pub key_id: String,
1573    pub counterparty: Counterparty,
1574    #[cfg_attr(
1575        feature = "network",
1576        serde(with = "serde_helpers::option_bytes_as_array")
1577    )]
1578    #[cfg_attr(
1579        feature = "network",
1580        serde(skip_serializing_if = "Option::is_none", default)
1581    )]
1582    pub data: Option<Vec<u8>>,
1583    #[cfg_attr(
1584        feature = "network",
1585        serde(with = "serde_helpers::option_bytes_as_array")
1586    )]
1587    #[cfg_attr(
1588        feature = "network",
1589        serde(skip_serializing_if = "Option::is_none", default)
1590    )]
1591    pub hash_to_directly_verify: Option<Vec<u8>>,
1592    // bytes_as_array: matches TS Byte[] and Go BytesList format.
1593    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1594    pub signature: Vec<u8>,
1595    pub for_self: Option<bool>,
1596    pub privileged: bool,
1597    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1598    pub privileged_reason: Option<String>,
1599    pub seek_permission: Option<bool>,
1600}
1601
1602/// Result of verifying a digital signature.
1603#[derive(Clone, Debug)]
1604#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1605#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1606pub struct VerifySignatureResult {
1607    pub valid: bool,
1608}
1609
1610// ---------------------------------------------------------------------------
1611// Certificate operations
1612// ---------------------------------------------------------------------------
1613
1614/// Arguments for acquiring a new certificate.
1615#[derive(Clone, Debug)]
1616#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1617#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1618pub struct AcquireCertificateArgs {
1619    #[cfg_attr(feature = "network", serde(rename = "type"))]
1620    pub cert_type: CertificateType,
1621    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1622    pub certifier: PublicKey,
1623    pub acquisition_protocol: AcquisitionProtocol,
1624    #[cfg_attr(
1625        feature = "network",
1626        serde(skip_serializing_if = "HashMap::is_empty", default)
1627    )]
1628    pub fields: HashMap<String, String>,
1629    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1630    pub serial_number: Option<SerialNumber>,
1631    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1632    pub revocation_outpoint: Option<String>,
1633    #[cfg_attr(
1634        feature = "network",
1635        serde(with = "serde_helpers::option_bytes_as_hex")
1636    )]
1637    #[cfg_attr(
1638        feature = "network",
1639        serde(skip_serializing_if = "Option::is_none", default)
1640    )]
1641    pub signature: Option<Vec<u8>>,
1642    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1643    pub certifier_url: Option<String>,
1644    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1645    pub keyring_revealer: Option<KeyringRevealer>,
1646    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1647    pub keyring_for_subject: Option<HashMap<String, String>>,
1648    pub privileged: bool,
1649    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1650    pub privileged_reason: Option<String>,
1651}
1652
1653/// Arguments for listing certificates.
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 ListCertificatesArgs {
1658    #[cfg_attr(feature = "network", serde(with = "serde_helpers::vec_public_key_hex"))]
1659    pub certifiers: Vec<PublicKey>,
1660    pub types: Vec<CertificateType>,
1661    pub limit: PositiveIntegerDefault10Max10000,
1662    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1663    pub offset: Option<PositiveIntegerOrZero>,
1664    pub privileged: BooleanDefaultFalse,
1665    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1666    pub privileged_reason: Option<String>,
1667    /// Optional partial certificate filter for exact matching.
1668    /// When provided, only certificates matching these fields are returned.
1669    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1670    pub partial: Option<PartialCertificate>,
1671}
1672
1673/// A certificate with its keyring and verifier.
1674#[derive(Clone, Debug)]
1675#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1676#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1677pub struct CertificateResult {
1678    #[cfg_attr(feature = "network", serde(flatten))]
1679    pub certificate: Certificate,
1680    pub keyring: HashMap<String, String>,
1681    #[cfg_attr(
1682        feature = "network",
1683        serde(with = "serde_helpers::option_bytes_as_hex")
1684    )]
1685    #[cfg_attr(
1686        feature = "network",
1687        serde(skip_serializing_if = "Option::is_none", default)
1688    )]
1689    pub verifier: Option<Vec<u8>>,
1690}
1691
1692/// Paginated list of 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 ListCertificatesResult {
1697    pub total_certificates: u32,
1698    pub certificates: Vec<CertificateResult>,
1699}
1700
1701/// Arguments for creating a verifiable certificate.
1702#[derive(Clone, Debug)]
1703#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1704#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1705pub struct ProveCertificateArgs {
1706    pub certificate: PartialCertificate,
1707    pub fields_to_reveal: Vec<String>,
1708    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1709    pub verifier: PublicKey,
1710    pub privileged: BooleanDefaultFalse,
1711    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1712    pub privileged_reason: Option<String>,
1713}
1714
1715/// Result of creating a verifiable certificate.
1716#[derive(Clone, Debug)]
1717#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1718#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1719pub struct ProveCertificateResult {
1720    pub keyring_for_verifier: HashMap<String, String>,
1721    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1722    pub certificate: Option<Certificate>,
1723    #[cfg_attr(
1724        feature = "network",
1725        serde(with = "serde_helpers::option_public_key_hex")
1726    )]
1727    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1728    pub verifier: Option<PublicKey>,
1729}
1730
1731/// Arguments for relinquishing ownership of a certificate.
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 RelinquishCertificateArgs {
1736    #[cfg_attr(feature = "network", serde(rename = "type"))]
1737    pub cert_type: CertificateType,
1738    pub serial_number: SerialNumber,
1739    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1740    pub certifier: PublicKey,
1741}
1742
1743/// Result of relinquishing a certificate.
1744#[derive(Clone, Debug)]
1745#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1746#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1747pub struct RelinquishCertificateResult {
1748    pub relinquished: bool,
1749}
1750
1751// ---------------------------------------------------------------------------
1752// Discovery types
1753// ---------------------------------------------------------------------------
1754
1755/// Information about an entity that issues identity certificates.
1756#[derive(Clone, Debug)]
1757#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1758#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1759pub struct IdentityCertifier {
1760    pub name: String,
1761    pub icon_url: String,
1762    pub description: String,
1763    pub trust: u8,
1764}
1765
1766/// An identity certificate with decoded fields and certifier info.
1767#[derive(Clone, Debug)]
1768#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1769#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1770pub struct IdentityCertificate {
1771    #[cfg_attr(feature = "network", serde(flatten))]
1772    pub certificate: Certificate,
1773    pub certifier_info: IdentityCertifier,
1774    pub publicly_revealed_keyring: HashMap<String, String>,
1775    pub decrypted_fields: HashMap<String, String>,
1776}
1777
1778/// Arguments for discovering certificates by identity key.
1779#[derive(Clone, Debug)]
1780#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1781#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1782pub struct DiscoverByIdentityKeyArgs {
1783    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1784    pub identity_key: PublicKey,
1785    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1786    pub limit: Option<u32>,
1787    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1788    pub offset: Option<u32>,
1789    pub seek_permission: Option<bool>,
1790}
1791
1792/// Arguments for discovering certificates by attributes.
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 DiscoverByAttributesArgs {
1797    pub attributes: HashMap<String, String>,
1798    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1799    pub limit: Option<u32>,
1800    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1801    pub offset: Option<u32>,
1802    pub seek_permission: Option<bool>,
1803}
1804
1805/// Paginated list of identity certificates found during discovery.
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 DiscoverCertificatesResult {
1810    pub total_certificates: u32,
1811    pub certificates: Vec<IdentityCertificate>,
1812}
1813
1814// ---------------------------------------------------------------------------
1815// Key linkage types
1816// ---------------------------------------------------------------------------
1817
1818/// Arguments for revealing key linkage between counterparties.
1819#[derive(Clone, Debug)]
1820#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1821#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1822pub struct RevealCounterpartyKeyLinkageArgs {
1823    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1824    pub counterparty: PublicKey,
1825    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1826    pub verifier: PublicKey,
1827    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1828    pub privileged: Option<bool>,
1829    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1830    pub privileged_reason: Option<String>,
1831}
1832
1833/// Result of revealing counterparty key linkage.
1834#[derive(Clone, Debug)]
1835#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1836#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1837pub struct RevealCounterpartyKeyLinkageResult {
1838    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1839    pub prover: PublicKey,
1840    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1841    pub counterparty: PublicKey,
1842    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1843    pub verifier: PublicKey,
1844    pub revelation_time: String,
1845    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1846    pub encrypted_linkage: Vec<u8>,
1847    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1848    pub encrypted_linkage_proof: Vec<u8>,
1849}
1850
1851/// Arguments for revealing specific key linkage.
1852#[derive(Clone, Debug)]
1853#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1854#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1855pub struct RevealSpecificKeyLinkageArgs {
1856    pub counterparty: Counterparty,
1857    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1858    pub verifier: PublicKey,
1859    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1860    pub protocol_id: Protocol,
1861    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1862    pub key_id: String,
1863    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1864    pub privileged: Option<bool>,
1865    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1866    pub privileged_reason: Option<String>,
1867}
1868
1869/// Result of revealing specific key linkage.
1870#[derive(Clone, Debug)]
1871#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1872#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1873pub struct RevealSpecificKeyLinkageResult {
1874    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1875    pub encrypted_linkage: Vec<u8>,
1876    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1877    pub encrypted_linkage_proof: Vec<u8>,
1878    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1879    pub prover: PublicKey,
1880    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1881    pub verifier: PublicKey,
1882    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1883    pub counterparty: PublicKey,
1884    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1885    pub protocol_id: Protocol,
1886    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1887    pub key_id: String,
1888    pub proof_type: u8,
1889}
1890
1891// ---------------------------------------------------------------------------
1892// Auth/Info types
1893// ---------------------------------------------------------------------------
1894
1895/// Whether the current session is authenticated.
1896#[derive(Clone, Debug)]
1897#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1898#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1899pub struct AuthenticatedResult {
1900    pub authenticated: bool,
1901}
1902
1903/// Current blockchain height.
1904#[derive(Clone, Debug)]
1905#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1906#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1907pub struct GetHeightResult {
1908    pub height: u32,
1909}
1910
1911/// Arguments for retrieving a blockchain header at a specific height.
1912#[derive(Clone, Debug)]
1913#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1914#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1915pub struct GetHeaderArgs {
1916    pub height: u32,
1917}
1918
1919/// Blockchain header data for the requested height.
1920#[derive(Clone, Debug)]
1921#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1922#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1923pub struct GetHeaderResult {
1924    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_hex"))]
1925    pub header: Vec<u8>,
1926}
1927
1928/// Current blockchain network.
1929#[derive(Clone, Debug)]
1930#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1931#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1932pub struct GetNetworkResult {
1933    pub network: Network,
1934}
1935
1936/// Version information about the wallet implementation.
1937#[derive(Clone, Debug)]
1938#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1939#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1940pub struct GetVersionResult {
1941    pub version: String,
1942}
1943
1944// ---------------------------------------------------------------------------
1945// WalletInterface trait
1946// ---------------------------------------------------------------------------
1947
1948/// The core wallet interface with all 28 async methods.
1949///
1950/// Uses `#[async_trait]` for object safety -- supports both static dispatch
1951/// (`W: WalletInterface`) and dynamic dispatch (`dyn WalletInterface`).
1952///
1953/// Every method takes `originator: Option<&str>` as the last parameter,
1954/// identifying the calling application domain.
1955#[async_trait]
1956pub trait WalletInterface: Send + Sync {
1957    // -- Action methods --
1958
1959    async fn create_action(
1960        &self,
1961        args: CreateActionArgs,
1962        originator: Option<&str>,
1963    ) -> Result<CreateActionResult, WalletError>;
1964
1965    async fn sign_action(
1966        &self,
1967        args: SignActionArgs,
1968        originator: Option<&str>,
1969    ) -> Result<SignActionResult, WalletError>;
1970
1971    async fn abort_action(
1972        &self,
1973        args: AbortActionArgs,
1974        originator: Option<&str>,
1975    ) -> Result<AbortActionResult, WalletError>;
1976
1977    async fn list_actions(
1978        &self,
1979        args: ListActionsArgs,
1980        originator: Option<&str>,
1981    ) -> Result<ListActionsResult, WalletError>;
1982
1983    async fn internalize_action(
1984        &self,
1985        args: InternalizeActionArgs,
1986        originator: Option<&str>,
1987    ) -> Result<InternalizeActionResult, WalletError>;
1988
1989    // -- Output methods --
1990
1991    async fn list_outputs(
1992        &self,
1993        args: ListOutputsArgs,
1994        originator: Option<&str>,
1995    ) -> Result<ListOutputsResult, WalletError>;
1996
1997    async fn relinquish_output(
1998        &self,
1999        args: RelinquishOutputArgs,
2000        originator: Option<&str>,
2001    ) -> Result<RelinquishOutputResult, WalletError>;
2002
2003    // -- Key/Crypto methods --
2004
2005    async fn get_public_key(
2006        &self,
2007        args: GetPublicKeyArgs,
2008        originator: Option<&str>,
2009    ) -> Result<GetPublicKeyResult, WalletError>;
2010
2011    async fn reveal_counterparty_key_linkage(
2012        &self,
2013        args: RevealCounterpartyKeyLinkageArgs,
2014        originator: Option<&str>,
2015    ) -> Result<RevealCounterpartyKeyLinkageResult, WalletError>;
2016
2017    async fn reveal_specific_key_linkage(
2018        &self,
2019        args: RevealSpecificKeyLinkageArgs,
2020        originator: Option<&str>,
2021    ) -> Result<RevealSpecificKeyLinkageResult, WalletError>;
2022
2023    async fn encrypt(
2024        &self,
2025        args: EncryptArgs,
2026        originator: Option<&str>,
2027    ) -> Result<EncryptResult, WalletError>;
2028
2029    async fn decrypt(
2030        &self,
2031        args: DecryptArgs,
2032        originator: Option<&str>,
2033    ) -> Result<DecryptResult, WalletError>;
2034
2035    async fn create_hmac(
2036        &self,
2037        args: CreateHmacArgs,
2038        originator: Option<&str>,
2039    ) -> Result<CreateHmacResult, WalletError>;
2040
2041    async fn verify_hmac(
2042        &self,
2043        args: VerifyHmacArgs,
2044        originator: Option<&str>,
2045    ) -> Result<VerifyHmacResult, WalletError>;
2046
2047    async fn create_signature(
2048        &self,
2049        args: CreateSignatureArgs,
2050        originator: Option<&str>,
2051    ) -> Result<CreateSignatureResult, WalletError>;
2052
2053    async fn verify_signature(
2054        &self,
2055        args: VerifySignatureArgs,
2056        originator: Option<&str>,
2057    ) -> Result<VerifySignatureResult, WalletError>;
2058
2059    // -- Certificate methods --
2060
2061    async fn acquire_certificate(
2062        &self,
2063        args: AcquireCertificateArgs,
2064        originator: Option<&str>,
2065    ) -> Result<Certificate, WalletError>;
2066
2067    async fn list_certificates(
2068        &self,
2069        args: ListCertificatesArgs,
2070        originator: Option<&str>,
2071    ) -> Result<ListCertificatesResult, WalletError>;
2072
2073    async fn prove_certificate(
2074        &self,
2075        args: ProveCertificateArgs,
2076        originator: Option<&str>,
2077    ) -> Result<ProveCertificateResult, WalletError>;
2078
2079    async fn relinquish_certificate(
2080        &self,
2081        args: RelinquishCertificateArgs,
2082        originator: Option<&str>,
2083    ) -> Result<RelinquishCertificateResult, WalletError>;
2084
2085    // -- Discovery methods --
2086
2087    async fn discover_by_identity_key(
2088        &self,
2089        args: DiscoverByIdentityKeyArgs,
2090        originator: Option<&str>,
2091    ) -> Result<DiscoverCertificatesResult, WalletError>;
2092
2093    async fn discover_by_attributes(
2094        &self,
2095        args: DiscoverByAttributesArgs,
2096        originator: Option<&str>,
2097    ) -> Result<DiscoverCertificatesResult, WalletError>;
2098
2099    // -- Auth/Info methods --
2100
2101    async fn is_authenticated(
2102        &self,
2103        originator: Option<&str>,
2104    ) -> Result<AuthenticatedResult, WalletError>;
2105
2106    async fn wait_for_authentication(
2107        &self,
2108        originator: Option<&str>,
2109    ) -> Result<AuthenticatedResult, WalletError>;
2110
2111    async fn get_height(&self, originator: Option<&str>) -> Result<GetHeightResult, WalletError>;
2112
2113    async fn get_header_for_height(
2114        &self,
2115        args: GetHeaderArgs,
2116        originator: Option<&str>,
2117    ) -> Result<GetHeaderResult, WalletError>;
2118
2119    async fn get_network(&self, originator: Option<&str>) -> Result<GetNetworkResult, WalletError>;
2120
2121    async fn get_version(&self, originator: Option<&str>) -> Result<GetVersionResult, WalletError>;
2122}
2123
2124#[cfg(test)]
2125mod tests {
2126    use super::*;
2127
2128    #[test]
2129    fn test_serial_number_from_string_hex_valid() {
2130        let hex = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2";
2131        let sn = SerialNumber::from_string(hex).unwrap();
2132        assert_eq!(sn.0[0], 0xa1);
2133        assert_eq!(sn.0[31], 0xb2);
2134    }
2135
2136    #[test]
2137    fn test_serial_number_from_string_base64_valid() {
2138        // 32 bytes of zeros -> base64 is "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
2139        let b64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
2140        let sn = SerialNumber::from_string(b64).unwrap();
2141        assert_eq!(sn.0, [0u8; 32]);
2142    }
2143
2144    #[test]
2145    fn test_serial_number_from_string_base64_nonzero() {
2146        // All 0xFF bytes: base64 = "//////////////////////////////////////////8="
2147        let b64 = "//////////////////////////////////////////8=";
2148        let sn = SerialNumber::from_string(b64).unwrap();
2149        assert_eq!(sn.0, [0xffu8; 32]);
2150    }
2151
2152    #[test]
2153    fn test_serial_number_from_string_invalid_length() {
2154        assert!(SerialNumber::from_string("abc").is_err());
2155        assert!(SerialNumber::from_string("").is_err());
2156        assert!(SerialNumber::from_string("a1b2c3").is_err());
2157    }
2158
2159    #[test]
2160    fn test_serial_number_from_string_invalid_chars() {
2161        // 64 chars but not valid hex
2162        let bad_hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
2163        assert!(SerialNumber::from_string(bad_hex).is_err());
2164    }
2165}