webledgers_teller/error.rs
1//! The one error type every fallible function in this crate returns.
2//!
3//! The teller throws with a sentence a person reads (`'a withdrawal is at
4//! least 546 sat'`) and its tests match on those words. Every refusal here is
5//! a named variant whose `Display` is the teller's sentence, word for word,
6//! so a page can show it and a test can match either the variant or the
7//! text. The variants this port adds (the ledger event, which the teller's
8//! page reads, and the boundaries Rust types make explicit) say so.
9
10/// Everything the teller refuses, in its own words.
11#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
12pub enum Error {
13 /// `accountOf`: not `did:nostr:<x>`, a bare x or a did:nostr Multikey.
14 #[error("an account is a did:nostr identifier (did:nostr:<64 hex>)")]
15 Account,
16 /// `sats`: not a whole number of satoshis between 0 and 21 million coins.
17 #[error("an amount is a whole number of satoshis")]
18 Amount,
19 /// `newLedger`: the name is empty or longer than 80 characters.
20 #[error("a ledger has a name of up to 80 characters")]
21 Name,
22 /// `checkLedger`: not a `WebLedger`, or no genesis.
23 #[error("not a Web Ledger with a genesis")]
24 NotALedger,
25 /// `checkLedger`: the stored hash is not the hash of the genesis.
26 #[error("the ledger's hash is not the hash of its genesis")]
27 HashMismatch,
28 /// `credit`: the outpoint is not `<64 lowercase hex>:<vout>`.
29 #[error("a deposit is an outpoint txid:vout")]
30 Outpoint,
31 /// `transfer`: an amount of zero.
32 #[error("a transfer moves at least one satoshi")]
33 ZeroTransfer,
34 /// `transfer`, `debit`: the account holds less than the amount.
35 #[error("{account}… has {has} sat, not {wanted}")]
36 Insufficient {
37 /// The first 20 characters of the account's did (`did:nostr:` and
38 /// ten hex digits), as the teller writes it.
39 account: String,
40 /// The balance the account holds.
41 has: u64,
42 /// The amount asked for.
43 wanted: u64,
44 },
45 /// `debit`: below [`crate::MIN_PAY`].
46 #[error("a withdrawal is at least 546 sat")]
47 WithdrawalTooSmall,
48 /// `depositAddress`: the ledger hash is not 64 lowercase hex.
49 #[error("ledgerHash is the ledger's 32-byte hash")]
50 DepositLedgerHash,
51 /// `requestTags`: the ledger hash is not 64 lowercase hex.
52 #[error("ledgerHash is the ledger's hash")]
53 RequestLedgerHash,
54 /// `requestTags`: an op other than join, withdraw or transfer.
55 #[error("op is join, withdraw or transfer")]
56 Op,
57 /// `requestTags`, `parseRequest`: a withdrawal or transfer with no `to`.
58 #[error("a withdrawal or transfer names where to")]
59 NoDestination,
60 /// `parseRequest`: not kind 3700.
61 #[error("not a request (kind 3700)")]
62 NotARequest,
63 /// `parseRequest`: the id or the signature does not verify.
64 #[error("the request does not verify")]
65 Unverified,
66 /// `parseRequest`: the `ledger` tag names another ledger.
67 #[error("a request for another ledger")]
68 OtherLedger,
69 /// `parseRequest`: no valid op, or no id of 8 to 64 lowercase hex.
70 #[error("a request has an op and an id")]
71 OpAndId,
72 /// `planPayout`: below [`crate::MIN_PAY`].
73 #[error("a payout is at least 546 sat")]
74 PayoutTooSmall,
75 /// `planPayout`: a fee rate outside 1 to 1000 sat/vB.
76 #[error("rate 1 to 1000 sat/vB")]
77 Rate,
78 /// `planPayout`: every coin together cannot pay the amount and the fee.
79 #[error("the deposits held ({held} sat) do not cover {amount} sat and the fee")]
80 NotCovered {
81 /// The sum of every coin offered.
82 held: u64,
83 /// The amount to pay.
84 amount: u64,
85 },
86 /// `signPayout`: an input failed the chain's script check.
87 #[error("input {input} did not pass the script check ({reason}); nothing was paid")]
88 ScriptCheck {
89 /// The input's index.
90 input: usize,
91 /// The verifier's reason.
92 reason: String,
93 },
94 /// `signPayout`: the signed transaction is bigger than the fee pays for.
95 #[error("the signed payout is {vsize} vB, so its fee of {fee} sat is below {rate} sat/vB; nothing was paid")]
96 FeeBelowRate {
97 /// The signed transaction's virtual size.
98 vsize: u64,
99 /// The plan's fee.
100 fee: u64,
101 /// The plan's rate.
102 rate: u64,
103 },
104 /// A key, point, tweak or scalar refused by the key rule
105 /// (`sidestr/spec siding/lib/keys.mjs`), in its words.
106 #[error("{0}")]
107 Key(&'static str),
108 /// Not a segwit script under that prefix (`address.mjs scriptToAddress`
109 /// returns `null`; this port refuses).
110 #[error("no address for that script under prefix {0:?}")]
111 Address(String),
112 /// A script or txid that is not hex of the right shape (this port: the
113 /// teller writes whatever it is given and the kernel's codec refuses it).
114 #[error("{what}: {reason}")]
115 Encoding {
116 /// Which field.
117 what: &'static str,
118 /// Why it was refused.
119 reason: String,
120 },
121 /// The chain's signature hash could not be computed for an input.
122 #[error("input {input}: {reason}")]
123 Sighash {
124 /// The input's index.
125 input: usize,
126 /// The reason the sighash refused.
127 reason: &'static str,
128 },
129 /// A Nostr event could not be built or signed (`sidestr-nostr`'s reason).
130 #[error("event: {0}")]
131 Event(String),
132 /// This port's reading of a ledger event (kind 30333), which the teller's
133 /// page does inline: not kind 30333.
134 #[error("not a ledger (kind 30333)")]
135 NotALedgerEvent,
136 /// This port: the ledger event's content is not a ledger document.
137 #[error("the ledger event's content is not a ledger: {0}")]
138 LedgerContent(String),
139 /// This port: a ledger event not signed by the ledger's operator, or for
140 /// another ledger (the page drops both: "the operator's own word only").
141 #[error("a ledger event that is not the operator's own copy of this ledger")]
142 NotTheOperators,
143}
144
145/// `Result` with this crate's [`Error`].
146pub type Result<T> = core::result::Result<T, Error>;