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        #[cfg_attr(feature = "network", serde(rename = "outputIndex"))]
1224        output_index: u32,
1225        #[cfg_attr(feature = "network", serde(rename = "paymentRemittance"))]
1226        payment: Payment,
1227    },
1228    #[cfg_attr(feature = "network", serde(rename = "basket insertion"))]
1229    BasketInsertion {
1230        #[cfg_attr(feature = "network", serde(rename = "outputIndex"))]
1231        output_index: u32,
1232        #[cfg_attr(feature = "network", serde(rename = "insertionRemittance"))]
1233        insertion: BasketInsertion,
1234    },
1235}
1236
1237/// Arguments for importing an external transaction into the wallet.
1238#[derive(Clone, Debug)]
1239#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1240#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1241pub struct InternalizeActionArgs {
1242    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1243    pub tx: Vec<u8>,
1244    pub description: String,
1245    #[cfg_attr(
1246        feature = "network",
1247        serde(skip_serializing_if = "Vec::is_empty", default)
1248    )]
1249    pub labels: Vec<LabelStringUnder300Bytes>,
1250    pub seek_permission: BooleanDefaultTrue,
1251    pub outputs: Vec<InternalizeOutput>,
1252}
1253
1254/// Result of internalizing a transaction.
1255#[derive(Clone, Debug)]
1256#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1257#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1258pub struct InternalizeActionResult {
1259    pub accepted: bool,
1260}
1261
1262// ---------------------------------------------------------------------------
1263// ListOutputs
1264// ---------------------------------------------------------------------------
1265
1266/// Filtering and options for listing wallet outputs.
1267#[derive(Clone, Debug)]
1268#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1269#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1270pub struct ListOutputsArgs {
1271    pub basket: BasketStringUnder300Bytes,
1272    #[cfg_attr(
1273        feature = "network",
1274        serde(skip_serializing_if = "Vec::is_empty", default)
1275    )]
1276    pub tags: Vec<OutputTagStringUnder300Bytes>,
1277    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1278    pub tag_query_mode: Option<QueryMode>,
1279    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1280    pub include: Option<OutputInclude>,
1281    #[cfg_attr(
1282        feature = "network",
1283        serde(skip_serializing_if = "BooleanDefaultFalse::is_none")
1284    )]
1285    pub include_custom_instructions: BooleanDefaultFalse,
1286    #[cfg_attr(
1287        feature = "network",
1288        serde(skip_serializing_if = "BooleanDefaultFalse::is_none")
1289    )]
1290    pub include_tags: BooleanDefaultFalse,
1291    #[cfg_attr(
1292        feature = "network",
1293        serde(skip_serializing_if = "BooleanDefaultFalse::is_none")
1294    )]
1295    pub include_labels: BooleanDefaultFalse,
1296    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1297    pub limit: PositiveIntegerDefault10Max10000,
1298    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1299    pub offset: Option<PositiveIntegerOrZero>,
1300    #[cfg_attr(
1301        feature = "network",
1302        serde(skip_serializing_if = "BooleanDefaultTrue::is_none")
1303    )]
1304    pub seek_permission: BooleanDefaultTrue,
1305}
1306
1307/// A wallet UTXO with its metadata.
1308#[derive(Clone, Debug)]
1309#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1310#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1311pub struct Output {
1312    pub satoshis: SatoshiValue,
1313    #[cfg_attr(
1314        feature = "network",
1315        serde(with = "serde_helpers::option_bytes_as_hex")
1316    )]
1317    #[cfg_attr(
1318        feature = "network",
1319        serde(skip_serializing_if = "Option::is_none", default)
1320    )]
1321    pub locking_script: Option<Vec<u8>>,
1322    pub spendable: bool,
1323    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1324    pub custom_instructions: Option<String>,
1325    #[cfg_attr(
1326        feature = "network",
1327        serde(skip_serializing_if = "Vec::is_empty", default)
1328    )]
1329    pub tags: Vec<String>,
1330    pub outpoint: OutpointString,
1331    #[cfg_attr(
1332        feature = "network",
1333        serde(skip_serializing_if = "Vec::is_empty", default)
1334    )]
1335    pub labels: Vec<String>,
1336}
1337
1338/// Paginated list of wallet outputs.
1339#[derive(Clone, Debug)]
1340#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1341#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1342pub struct ListOutputsResult {
1343    pub total_outputs: u32,
1344    #[cfg_attr(
1345        feature = "network",
1346        serde(with = "serde_helpers::option_bytes_as_array")
1347    )]
1348    #[cfg_attr(
1349        feature = "network",
1350        serde(skip_serializing_if = "Option::is_none", default)
1351    )]
1352    #[cfg_attr(feature = "network", serde(rename = "BEEF"))]
1353    pub beef: Option<Vec<u8>>,
1354    pub outputs: Vec<Output>,
1355}
1356
1357// ---------------------------------------------------------------------------
1358// RelinquishOutput
1359// ---------------------------------------------------------------------------
1360
1361/// Arguments for relinquishing ownership of an output.
1362#[derive(Clone, Debug)]
1363#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1364#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1365pub struct RelinquishOutputArgs {
1366    pub basket: BasketStringUnder300Bytes,
1367    pub output: OutpointString,
1368}
1369
1370/// Result of relinquishing an output.
1371#[derive(Clone, Debug)]
1372#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1373#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1374pub struct RelinquishOutputResult {
1375    pub relinquished: bool,
1376}
1377
1378// ---------------------------------------------------------------------------
1379// Key/Crypto types
1380// ---------------------------------------------------------------------------
1381
1382/// Arguments for getting a public key.
1383#[derive(Clone, Debug)]
1384#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1385#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1386pub struct GetPublicKeyArgs {
1387    pub identity_key: bool,
1388    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1389    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1390    pub protocol_id: Option<Protocol>,
1391    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1392    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1393    pub key_id: Option<String>,
1394    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1395    pub counterparty: Option<Counterparty>,
1396    pub privileged: bool,
1397    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1398    pub privileged_reason: Option<String>,
1399    pub for_self: Option<bool>,
1400    pub seek_permission: Option<bool>,
1401}
1402
1403/// Result of getting a public key.
1404#[derive(Clone, Debug)]
1405#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1406#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1407pub struct GetPublicKeyResult {
1408    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1409    pub public_key: PublicKey,
1410}
1411
1412/// Arguments for encryption.
1413#[derive(Clone, Debug)]
1414#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1415#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1416pub struct EncryptArgs {
1417    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1418    pub protocol_id: Protocol,
1419    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1420    pub key_id: String,
1421    pub counterparty: Counterparty,
1422    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1423    pub plaintext: Vec<u8>,
1424    pub privileged: bool,
1425    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1426    pub privileged_reason: Option<String>,
1427    pub seek_permission: Option<bool>,
1428}
1429
1430/// Result of encryption.
1431#[derive(Clone, Debug)]
1432#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1433#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1434pub struct EncryptResult {
1435    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1436    pub ciphertext: Vec<u8>,
1437}
1438
1439/// Arguments for decryption.
1440#[derive(Clone, Debug)]
1441#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1442#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1443pub struct DecryptArgs {
1444    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1445    pub protocol_id: Protocol,
1446    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1447    pub key_id: String,
1448    pub counterparty: Counterparty,
1449    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1450    pub ciphertext: Vec<u8>,
1451    pub privileged: bool,
1452    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1453    pub privileged_reason: Option<String>,
1454    pub seek_permission: Option<bool>,
1455}
1456
1457/// Result of decryption.
1458#[derive(Clone, Debug)]
1459#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1460#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1461pub struct DecryptResult {
1462    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1463    pub plaintext: Vec<u8>,
1464}
1465
1466/// Arguments for creating an HMAC.
1467#[derive(Clone, Debug)]
1468#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1469#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1470pub struct CreateHmacArgs {
1471    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1472    pub protocol_id: Protocol,
1473    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1474    pub key_id: String,
1475    pub counterparty: Counterparty,
1476    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1477    pub data: Vec<u8>,
1478    pub privileged: bool,
1479    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1480    pub privileged_reason: Option<String>,
1481    pub seek_permission: Option<bool>,
1482}
1483
1484/// Result of creating an HMAC.
1485#[derive(Clone, Debug)]
1486#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1487#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1488pub struct CreateHmacResult {
1489    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1490    pub hmac: Vec<u8>,
1491}
1492
1493/// Arguments for verifying an HMAC.
1494#[derive(Clone, Debug)]
1495#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1496#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1497pub struct VerifyHmacArgs {
1498    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1499    pub protocol_id: Protocol,
1500    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1501    pub key_id: String,
1502    pub counterparty: Counterparty,
1503    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1504    pub data: Vec<u8>,
1505    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1506    pub hmac: Vec<u8>,
1507    pub privileged: bool,
1508    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1509    pub privileged_reason: Option<String>,
1510    pub seek_permission: Option<bool>,
1511}
1512
1513/// Result of verifying an HMAC.
1514#[derive(Clone, Debug)]
1515#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1516#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1517pub struct VerifyHmacResult {
1518    pub valid: bool,
1519}
1520
1521/// Arguments for creating a digital signature.
1522#[derive(Clone, Debug)]
1523#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1524#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1525pub struct CreateSignatureArgs {
1526    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1527    pub protocol_id: Protocol,
1528    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1529    pub key_id: String,
1530    pub counterparty: Counterparty,
1531    #[cfg_attr(
1532        feature = "network",
1533        serde(with = "serde_helpers::option_bytes_as_array")
1534    )]
1535    #[cfg_attr(
1536        feature = "network",
1537        serde(skip_serializing_if = "Option::is_none", default)
1538    )]
1539    pub data: Option<Vec<u8>>,
1540    #[cfg_attr(
1541        feature = "network",
1542        serde(with = "serde_helpers::option_bytes_as_array")
1543    )]
1544    #[cfg_attr(
1545        feature = "network",
1546        serde(skip_serializing_if = "Option::is_none", default)
1547    )]
1548    pub hash_to_directly_sign: Option<Vec<u8>>,
1549    pub privileged: bool,
1550    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1551    pub privileged_reason: Option<String>,
1552    pub seek_permission: Option<bool>,
1553}
1554
1555/// Result of creating a digital signature.
1556#[derive(Clone, Debug)]
1557#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1558#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1559pub struct CreateSignatureResult {
1560    // bytes_as_array: TS SDK returns Byte[] (number array), Go SDK uses BytesList.
1561    // NOT bytes_as_hex — BSV Desktop JSON API returns [48, 69, ...] not "3045...".
1562    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1563    pub signature: Vec<u8>,
1564}
1565
1566/// Arguments for verifying a digital signature.
1567#[derive(Clone, Debug)]
1568#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1569#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1570pub struct VerifySignatureArgs {
1571    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1572    pub protocol_id: Protocol,
1573    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1574    pub key_id: String,
1575    pub counterparty: Counterparty,
1576    #[cfg_attr(
1577        feature = "network",
1578        serde(with = "serde_helpers::option_bytes_as_array")
1579    )]
1580    #[cfg_attr(
1581        feature = "network",
1582        serde(skip_serializing_if = "Option::is_none", default)
1583    )]
1584    pub data: Option<Vec<u8>>,
1585    #[cfg_attr(
1586        feature = "network",
1587        serde(with = "serde_helpers::option_bytes_as_array")
1588    )]
1589    #[cfg_attr(
1590        feature = "network",
1591        serde(skip_serializing_if = "Option::is_none", default)
1592    )]
1593    pub hash_to_directly_verify: Option<Vec<u8>>,
1594    // bytes_as_array: matches TS Byte[] and Go BytesList format.
1595    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1596    pub signature: Vec<u8>,
1597    pub for_self: Option<bool>,
1598    pub privileged: bool,
1599    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1600    pub privileged_reason: Option<String>,
1601    pub seek_permission: Option<bool>,
1602}
1603
1604/// Result of verifying a digital signature.
1605#[derive(Clone, Debug)]
1606#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1607#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1608pub struct VerifySignatureResult {
1609    pub valid: bool,
1610}
1611
1612// ---------------------------------------------------------------------------
1613// Certificate operations
1614// ---------------------------------------------------------------------------
1615
1616/// Arguments for acquiring a new certificate.
1617#[derive(Clone, Debug)]
1618#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1619#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1620pub struct AcquireCertificateArgs {
1621    #[cfg_attr(feature = "network", serde(rename = "type"))]
1622    pub cert_type: CertificateType,
1623    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1624    pub certifier: PublicKey,
1625    pub acquisition_protocol: AcquisitionProtocol,
1626    #[cfg_attr(
1627        feature = "network",
1628        serde(skip_serializing_if = "HashMap::is_empty", default)
1629    )]
1630    pub fields: HashMap<String, String>,
1631    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1632    pub serial_number: Option<SerialNumber>,
1633    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1634    pub revocation_outpoint: Option<String>,
1635    #[cfg_attr(
1636        feature = "network",
1637        serde(with = "serde_helpers::option_bytes_as_hex")
1638    )]
1639    #[cfg_attr(
1640        feature = "network",
1641        serde(skip_serializing_if = "Option::is_none", default)
1642    )]
1643    pub signature: Option<Vec<u8>>,
1644    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1645    pub certifier_url: Option<String>,
1646    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1647    pub keyring_revealer: Option<KeyringRevealer>,
1648    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1649    pub keyring_for_subject: Option<HashMap<String, String>>,
1650    pub privileged: bool,
1651    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1652    pub privileged_reason: Option<String>,
1653}
1654
1655/// Arguments for listing certificates.
1656#[derive(Clone, Debug)]
1657#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1658#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1659pub struct ListCertificatesArgs {
1660    #[cfg_attr(feature = "network", serde(with = "serde_helpers::vec_public_key_hex"))]
1661    pub certifiers: Vec<PublicKey>,
1662    pub types: Vec<CertificateType>,
1663    pub limit: PositiveIntegerDefault10Max10000,
1664    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1665    pub offset: Option<PositiveIntegerOrZero>,
1666    pub privileged: BooleanDefaultFalse,
1667    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1668    pub privileged_reason: Option<String>,
1669    /// Optional partial certificate filter for exact matching.
1670    /// When provided, only certificates matching these fields are returned.
1671    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1672    pub partial: Option<PartialCertificate>,
1673}
1674
1675/// A certificate with its keyring and verifier.
1676#[derive(Clone, Debug)]
1677#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1678#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1679pub struct CertificateResult {
1680    #[cfg_attr(feature = "network", serde(flatten))]
1681    pub certificate: Certificate,
1682    pub keyring: HashMap<String, String>,
1683    #[cfg_attr(
1684        feature = "network",
1685        serde(with = "serde_helpers::option_bytes_as_hex")
1686    )]
1687    #[cfg_attr(
1688        feature = "network",
1689        serde(skip_serializing_if = "Option::is_none", default)
1690    )]
1691    pub verifier: Option<Vec<u8>>,
1692}
1693
1694/// Paginated list of certificates.
1695#[derive(Clone, Debug)]
1696#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1697#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1698pub struct ListCertificatesResult {
1699    pub total_certificates: u32,
1700    pub certificates: Vec<CertificateResult>,
1701}
1702
1703/// Arguments for creating a verifiable certificate.
1704#[derive(Clone, Debug)]
1705#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1706#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1707pub struct ProveCertificateArgs {
1708    pub certificate: PartialCertificate,
1709    pub fields_to_reveal: Vec<String>,
1710    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1711    pub verifier: PublicKey,
1712    pub privileged: BooleanDefaultFalse,
1713    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1714    pub privileged_reason: Option<String>,
1715}
1716
1717/// Result of creating a verifiable certificate.
1718#[derive(Clone, Debug)]
1719#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1720#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1721pub struct ProveCertificateResult {
1722    pub keyring_for_verifier: HashMap<String, String>,
1723    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1724    pub certificate: Option<Certificate>,
1725    #[cfg_attr(
1726        feature = "network",
1727        serde(with = "serde_helpers::option_public_key_hex")
1728    )]
1729    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1730    pub verifier: Option<PublicKey>,
1731}
1732
1733/// Arguments for relinquishing ownership of a certificate.
1734#[derive(Clone, Debug)]
1735#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1736#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1737pub struct RelinquishCertificateArgs {
1738    #[cfg_attr(feature = "network", serde(rename = "type"))]
1739    pub cert_type: CertificateType,
1740    pub serial_number: SerialNumber,
1741    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1742    pub certifier: PublicKey,
1743}
1744
1745/// Result of relinquishing a certificate.
1746#[derive(Clone, Debug)]
1747#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1748#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1749pub struct RelinquishCertificateResult {
1750    pub relinquished: bool,
1751}
1752
1753// ---------------------------------------------------------------------------
1754// Discovery types
1755// ---------------------------------------------------------------------------
1756
1757/// Information about an entity that issues identity certificates.
1758#[derive(Clone, Debug)]
1759#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1760#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1761pub struct IdentityCertifier {
1762    pub name: String,
1763    pub icon_url: String,
1764    pub description: String,
1765    pub trust: u8,
1766}
1767
1768/// An identity certificate with decoded fields and certifier info.
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 IdentityCertificate {
1773    #[cfg_attr(feature = "network", serde(flatten))]
1774    pub certificate: Certificate,
1775    pub certifier_info: IdentityCertifier,
1776    pub publicly_revealed_keyring: HashMap<String, String>,
1777    pub decrypted_fields: HashMap<String, String>,
1778}
1779
1780/// Arguments for discovering certificates by identity key.
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 DiscoverByIdentityKeyArgs {
1785    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1786    pub identity_key: PublicKey,
1787    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1788    pub limit: Option<u32>,
1789    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1790    pub offset: Option<u32>,
1791    pub seek_permission: Option<bool>,
1792}
1793
1794/// Arguments for discovering certificates by attributes.
1795#[derive(Clone, Debug)]
1796#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1797#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1798pub struct DiscoverByAttributesArgs {
1799    pub attributes: HashMap<String, String>,
1800    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1801    pub limit: Option<u32>,
1802    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1803    pub offset: Option<u32>,
1804    pub seek_permission: Option<bool>,
1805}
1806
1807/// Paginated list of identity certificates found during discovery.
1808#[derive(Clone, Debug)]
1809#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1810#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1811pub struct DiscoverCertificatesResult {
1812    pub total_certificates: u32,
1813    pub certificates: Vec<IdentityCertificate>,
1814}
1815
1816// ---------------------------------------------------------------------------
1817// Key linkage types
1818// ---------------------------------------------------------------------------
1819
1820/// Arguments for revealing key linkage between counterparties.
1821#[derive(Clone, Debug)]
1822#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1823#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1824pub struct RevealCounterpartyKeyLinkageArgs {
1825    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1826    pub counterparty: PublicKey,
1827    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1828    pub verifier: PublicKey,
1829    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1830    pub privileged: Option<bool>,
1831    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1832    pub privileged_reason: Option<String>,
1833}
1834
1835/// Result of revealing counterparty key linkage.
1836#[derive(Clone, Debug)]
1837#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1838#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1839pub struct RevealCounterpartyKeyLinkageResult {
1840    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1841    pub prover: PublicKey,
1842    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1843    pub counterparty: PublicKey,
1844    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1845    pub verifier: PublicKey,
1846    pub revelation_time: String,
1847    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1848    pub encrypted_linkage: Vec<u8>,
1849    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1850    pub encrypted_linkage_proof: Vec<u8>,
1851}
1852
1853/// Arguments for revealing specific key linkage.
1854#[derive(Clone, Debug)]
1855#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1856#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1857pub struct RevealSpecificKeyLinkageArgs {
1858    pub counterparty: Counterparty,
1859    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1860    pub verifier: PublicKey,
1861    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1862    pub protocol_id: Protocol,
1863    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1864    pub key_id: String,
1865    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1866    pub privileged: Option<bool>,
1867    #[cfg_attr(feature = "network", serde(skip_serializing_if = "Option::is_none"))]
1868    pub privileged_reason: Option<String>,
1869}
1870
1871/// Result of revealing specific key linkage.
1872#[derive(Clone, Debug)]
1873#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1874#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1875pub struct RevealSpecificKeyLinkageResult {
1876    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1877    pub encrypted_linkage: Vec<u8>,
1878    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_array"))]
1879    pub encrypted_linkage_proof: Vec<u8>,
1880    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1881    pub prover: PublicKey,
1882    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1883    pub verifier: PublicKey,
1884    #[cfg_attr(feature = "network", serde(with = "serde_helpers::public_key_hex"))]
1885    pub counterparty: PublicKey,
1886    #[cfg_attr(feature = "network", serde(rename = "protocolID"))]
1887    pub protocol_id: Protocol,
1888    #[cfg_attr(feature = "network", serde(rename = "keyID"))]
1889    pub key_id: String,
1890    pub proof_type: u8,
1891}
1892
1893// ---------------------------------------------------------------------------
1894// Auth/Info types
1895// ---------------------------------------------------------------------------
1896
1897/// Whether the current session is authenticated.
1898#[derive(Clone, Debug)]
1899#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1900#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1901pub struct AuthenticatedResult {
1902    pub authenticated: bool,
1903}
1904
1905/// Current blockchain height.
1906#[derive(Clone, Debug)]
1907#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1908#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1909pub struct GetHeightResult {
1910    pub height: u32,
1911}
1912
1913/// Arguments for retrieving a blockchain header at a specific height.
1914#[derive(Clone, Debug)]
1915#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1916#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1917pub struct GetHeaderArgs {
1918    pub height: u32,
1919}
1920
1921/// Blockchain header data for the requested height.
1922#[derive(Clone, Debug)]
1923#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1924#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1925pub struct GetHeaderResult {
1926    #[cfg_attr(feature = "network", serde(with = "serde_helpers::bytes_as_hex"))]
1927    pub header: Vec<u8>,
1928}
1929
1930/// Current blockchain network.
1931#[derive(Clone, Debug)]
1932#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1933#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1934pub struct GetNetworkResult {
1935    pub network: Network,
1936}
1937
1938/// Version information about the wallet implementation.
1939#[derive(Clone, Debug)]
1940#[cfg_attr(feature = "network", derive(serde::Serialize, serde::Deserialize))]
1941#[cfg_attr(feature = "network", serde(rename_all = "camelCase"))]
1942pub struct GetVersionResult {
1943    pub version: String,
1944}
1945
1946// ---------------------------------------------------------------------------
1947// WalletInterface trait
1948// ---------------------------------------------------------------------------
1949
1950/// The core wallet interface with all 28 async methods.
1951///
1952/// Uses `#[async_trait]` for object safety -- supports both static dispatch
1953/// (`W: WalletInterface`) and dynamic dispatch (`dyn WalletInterface`).
1954///
1955/// Every method takes `originator: Option<&str>` as the last parameter,
1956/// identifying the calling application domain.
1957#[async_trait]
1958pub trait WalletInterface: Send + Sync {
1959    // -- Action methods --
1960
1961    async fn create_action(
1962        &self,
1963        args: CreateActionArgs,
1964        originator: Option<&str>,
1965    ) -> Result<CreateActionResult, WalletError>;
1966
1967    async fn sign_action(
1968        &self,
1969        args: SignActionArgs,
1970        originator: Option<&str>,
1971    ) -> Result<SignActionResult, WalletError>;
1972
1973    async fn abort_action(
1974        &self,
1975        args: AbortActionArgs,
1976        originator: Option<&str>,
1977    ) -> Result<AbortActionResult, WalletError>;
1978
1979    async fn list_actions(
1980        &self,
1981        args: ListActionsArgs,
1982        originator: Option<&str>,
1983    ) -> Result<ListActionsResult, WalletError>;
1984
1985    async fn internalize_action(
1986        &self,
1987        args: InternalizeActionArgs,
1988        originator: Option<&str>,
1989    ) -> Result<InternalizeActionResult, WalletError>;
1990
1991    // -- Output methods --
1992
1993    async fn list_outputs(
1994        &self,
1995        args: ListOutputsArgs,
1996        originator: Option<&str>,
1997    ) -> Result<ListOutputsResult, WalletError>;
1998
1999    async fn relinquish_output(
2000        &self,
2001        args: RelinquishOutputArgs,
2002        originator: Option<&str>,
2003    ) -> Result<RelinquishOutputResult, WalletError>;
2004
2005    // -- Key/Crypto methods --
2006
2007    async fn get_public_key(
2008        &self,
2009        args: GetPublicKeyArgs,
2010        originator: Option<&str>,
2011    ) -> Result<GetPublicKeyResult, WalletError>;
2012
2013    async fn reveal_counterparty_key_linkage(
2014        &self,
2015        args: RevealCounterpartyKeyLinkageArgs,
2016        originator: Option<&str>,
2017    ) -> Result<RevealCounterpartyKeyLinkageResult, WalletError>;
2018
2019    async fn reveal_specific_key_linkage(
2020        &self,
2021        args: RevealSpecificKeyLinkageArgs,
2022        originator: Option<&str>,
2023    ) -> Result<RevealSpecificKeyLinkageResult, WalletError>;
2024
2025    async fn encrypt(
2026        &self,
2027        args: EncryptArgs,
2028        originator: Option<&str>,
2029    ) -> Result<EncryptResult, WalletError>;
2030
2031    async fn decrypt(
2032        &self,
2033        args: DecryptArgs,
2034        originator: Option<&str>,
2035    ) -> Result<DecryptResult, WalletError>;
2036
2037    async fn create_hmac(
2038        &self,
2039        args: CreateHmacArgs,
2040        originator: Option<&str>,
2041    ) -> Result<CreateHmacResult, WalletError>;
2042
2043    async fn verify_hmac(
2044        &self,
2045        args: VerifyHmacArgs,
2046        originator: Option<&str>,
2047    ) -> Result<VerifyHmacResult, WalletError>;
2048
2049    async fn create_signature(
2050        &self,
2051        args: CreateSignatureArgs,
2052        originator: Option<&str>,
2053    ) -> Result<CreateSignatureResult, WalletError>;
2054
2055    async fn verify_signature(
2056        &self,
2057        args: VerifySignatureArgs,
2058        originator: Option<&str>,
2059    ) -> Result<VerifySignatureResult, WalletError>;
2060
2061    // -- Certificate methods --
2062
2063    async fn acquire_certificate(
2064        &self,
2065        args: AcquireCertificateArgs,
2066        originator: Option<&str>,
2067    ) -> Result<Certificate, WalletError>;
2068
2069    async fn list_certificates(
2070        &self,
2071        args: ListCertificatesArgs,
2072        originator: Option<&str>,
2073    ) -> Result<ListCertificatesResult, WalletError>;
2074
2075    async fn prove_certificate(
2076        &self,
2077        args: ProveCertificateArgs,
2078        originator: Option<&str>,
2079    ) -> Result<ProveCertificateResult, WalletError>;
2080
2081    async fn relinquish_certificate(
2082        &self,
2083        args: RelinquishCertificateArgs,
2084        originator: Option<&str>,
2085    ) -> Result<RelinquishCertificateResult, WalletError>;
2086
2087    // -- Discovery methods --
2088
2089    async fn discover_by_identity_key(
2090        &self,
2091        args: DiscoverByIdentityKeyArgs,
2092        originator: Option<&str>,
2093    ) -> Result<DiscoverCertificatesResult, WalletError>;
2094
2095    async fn discover_by_attributes(
2096        &self,
2097        args: DiscoverByAttributesArgs,
2098        originator: Option<&str>,
2099    ) -> Result<DiscoverCertificatesResult, WalletError>;
2100
2101    // -- Auth/Info methods --
2102
2103    async fn is_authenticated(
2104        &self,
2105        originator: Option<&str>,
2106    ) -> Result<AuthenticatedResult, WalletError>;
2107
2108    async fn wait_for_authentication(
2109        &self,
2110        originator: Option<&str>,
2111    ) -> Result<AuthenticatedResult, WalletError>;
2112
2113    async fn get_height(&self, originator: Option<&str>) -> Result<GetHeightResult, WalletError>;
2114
2115    async fn get_header_for_height(
2116        &self,
2117        args: GetHeaderArgs,
2118        originator: Option<&str>,
2119    ) -> Result<GetHeaderResult, WalletError>;
2120
2121    async fn get_network(&self, originator: Option<&str>) -> Result<GetNetworkResult, WalletError>;
2122
2123    async fn get_version(&self, originator: Option<&str>) -> Result<GetVersionResult, WalletError>;
2124}
2125
2126#[cfg(test)]
2127mod tests {
2128    use super::*;
2129
2130    #[test]
2131    fn test_serial_number_from_string_hex_valid() {
2132        let hex = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2";
2133        let sn = SerialNumber::from_string(hex).unwrap();
2134        assert_eq!(sn.0[0], 0xa1);
2135        assert_eq!(sn.0[31], 0xb2);
2136    }
2137
2138    #[test]
2139    fn test_serial_number_from_string_base64_valid() {
2140        // 32 bytes of zeros -> base64 is "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
2141        let b64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
2142        let sn = SerialNumber::from_string(b64).unwrap();
2143        assert_eq!(sn.0, [0u8; 32]);
2144    }
2145
2146    #[test]
2147    fn test_serial_number_from_string_base64_nonzero() {
2148        // All 0xFF bytes: base64 = "//////////////////////////////////////////8="
2149        let b64 = "//////////////////////////////////////////8=";
2150        let sn = SerialNumber::from_string(b64).unwrap();
2151        assert_eq!(sn.0, [0xffu8; 32]);
2152    }
2153
2154    #[test]
2155    fn test_serial_number_from_string_invalid_length() {
2156        assert!(SerialNumber::from_string("abc").is_err());
2157        assert!(SerialNumber::from_string("").is_err());
2158        assert!(SerialNumber::from_string("a1b2c3").is_err());
2159    }
2160
2161    #[test]
2162    fn test_serial_number_from_string_invalid_chars() {
2163        // 64 chars but not valid hex
2164        let bad_hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
2165        assert!(SerialNumber::from_string(bad_hex).is_err());
2166    }
2167}