sidestr_wallet/error.rs
1//! The one error type every fallible function in this crate returns.
2//!
3//! siding's `spend.mjs` throws `Error` objects whose *messages* are the
4//! contract its callers and tests read (`insufficient: … sats mature, …
5//! needed`, `a peg-out burns at least … sats`, `bad address …`). The
6//! variants here keep that wording, so a message from this crate reads like
7//! one from the reference, while a caller can match on the variant.
8
9/// Everything that can go wrong between "I want to pay" and a transaction
10/// the producer accepts.
11#[derive(Debug, thiserror::Error)]
12#[non_exhaustive]
13pub enum Error {
14 /// The amount is zero (`spend.mjs`: "the amount is a whole number of sats").
15 #[error("the amount is a whole number of sats")]
16 BadAmount,
17 /// The destination is neither a script hex nor a segwit address under
18 /// any prefix (`spend.mjs resolveTo`: "bad address …").
19 #[error("bad address {0}")]
20 BadDestination(String),
21 /// The mature coins do not cover the amount plus the fee bound
22 /// (`spend.mjs`: "insufficient: … sats mature, … needed").
23 #[error("insufficient: {have} sats mature, {need} needed")]
24 Insufficient {
25 /// Sats the picked coins sum to.
26 have: u64,
27 /// Amount plus the fee bound.
28 need: u64,
29 },
30 /// The coins covered the bound but not the fee the size came to
31 /// (`spend.mjs`: "insufficient coins for … plus the …-sat minimum fee").
32 #[error("insufficient coins for {amount} plus the {fee}-sat minimum fee")]
33 InsufficientForFee {
34 /// The amount wanted.
35 amount: u64,
36 /// The fee the sized transaction needs.
37 fee: u64,
38 },
39 /// A burn below the document's `pegoutMin` (SPEC 7; `spend.mjs`: "a
40 /// peg-out burns at least … sats").
41 #[error("a peg-out burns at least {min} sats, not {value}")]
42 BelowPegoutMin {
43 /// The value asked for.
44 value: u64,
45 /// The document's `pegoutMin`.
46 min: u64,
47 },
48 /// An explicit fee under the producer's `minFeeRate` policy, in the
49 /// words `State::submit` would refuse it with.
50 #[error("fee {fee} is below the minimum {min} sats ({vsize} vB at {rate} sat/vB)")]
51 FeeBelowMinimum {
52 /// The fee given.
53 fee: u64,
54 /// The least the producer accepts for this size.
55 min: u64,
56 /// The transaction's virtual size.
57 vsize: u64,
58 /// The document's `minFeeRate`.
59 rate: u64,
60 },
61 /// An output below Bitcoin's dust threshold for its script: the chain
62 /// would accept it and no wallet could economically spend it. The
63 /// reference does not check; this crate does (see the crate docs).
64 #[error("{value} sats to {script} is dust: at least {min} sats")]
65 Dust {
66 /// The value asked for.
67 value: u64,
68 /// The dust threshold for that script.
69 min: u64,
70 /// The script, hex.
71 script: String,
72 },
73 /// A parent address for the wrong network: a peg-in to a `bc1…`
74 /// address beside `tbtc4`, or the reverse (SPEC 6, 3.2).
75 #[error("{address} is not a {network} address: the parent is {parent}")]
76 WrongNetwork {
77 /// The address given.
78 address: String,
79 /// The network the parent alias resolves to.
80 network: String,
81 /// The parent alias from the document.
82 parent: String,
83 },
84 /// A parent-side marker (peg-in, peg-out record) longer than the
85 /// parent's 80-byte `OP_RETURN` relay policy allows, or any marker push
86 /// over the 255 bytes the shared grammar reads back (a 61-byte peg-in
87 /// marker for a 15-byte chain id fits).
88 #[error(
89 "marker of {0} bytes exceeds the parent's 80-byte data limit; the chain id is too long"
90 )]
91 MarkerTooLong(usize),
92 /// The [`SpendPolicy`](crate::policy::SpendPolicy) refused the intent.
93 #[error("policy refused: {0}")]
94 Policy(String),
95 /// The [`SpendSigner`](crate::key::SpendSigner) could not sign.
96 #[error("signer: {0}")]
97 Signer(String),
98 /// An issued-asset build the `assets` view would not read as asked
99 /// (SPEC 12): too little of the asset, a memo shaped like an assets
100 /// record, an input carrying another asset.
101 #[error("asset: {0}")]
102 Asset(String),
103 /// A producer answered `{"error": …}` to a `POST /tx`.
104 #[error("producer refused: {0}")]
105 Refused(String),
106 /// JSON that does not parse or does not fit the shape.
107 #[error("json: {0}")]
108 Json(#[from] serde_json::Error),
109 /// Hex or bytes that do not decode.
110 #[error("encoding: {0}")]
111 Encoding(String),
112 /// An error from `sidestr-core`: a document naming an unknown parent, a
113 /// marker that cannot be built.
114 #[error(transparent)]
115 Core(#[from] sidestr_core::Error),
116 /// A secp256k1 failure: a derived scalar out of range, a bad key.
117 #[error("secp256k1: {0}")]
118 Secp(#[from] bitcoin::secp256k1::Error),
119 /// An HTTP failure talking to a producer (feature `client`).
120 #[cfg(feature = "client")]
121 #[error("http: {0}")]
122 Http(String),
123}
124
125/// `Result` with this crate's [`Error`].
126pub type Result<T> = core::result::Result<T, Error>;