paralock_core/lib.rs
1//! # paralock-core
2//!
3//! The protocol core of [Paralock](https://paralock.xyz): trustless swaps between ZEC and stablecoins on EVM chains.
4//! A lock on Zcash and a lock on the EVM chain share one secret, so either both sides settle or both refund.
5//!
6//! This crate is the single implementation behind every Paralock SDK. It is **pure and synchronous**: no I/O, no
7//! clocks, no network, no async. Randomness is injected by the caller as a [`rand_core::CryptoRngCore`] (an
8//! [`OsRng`] convenience is available with the default `std` feature), consensus branch ids and the current time are
9//! always inputs, and every parser of untrusted data returns a `Result` instead of panicking.
10//!
11//! It is a byte-for-byte port of the TypeScript reference (`@paralock/zcash`, `@paralock/adaptor`,
12//! `@paralock/evm`, `@paralock/sdk`): scripts, addresses, transactions, signature hashes, txids, escrow ids, claim
13//! digests, adaptor pre-signatures and JSON messages are identical, and the test suite checks them against shared
14//! cross-language vectors.
15//!
16//! ## Modules
17//!
18//! - [`zcash`]: transparent addresses, keys, scripts, the HTLC and 2-of-2 multisig templates, v5 transactions,
19//! ZIP 244 txids and signature hashes, ZIP 317 fees and the transaction builders.
20//! - [`adaptor`]: ECDSA adaptor signatures on secp256k1 with Chaum–Pedersen DLEQ proofs (private mode).
21//! - [`evm`]: `ParalockHTLC` / `ParalockAdaptorEscrow` swap ids, claim digests, hashlocks, recovery ids and EVM
22//! addresses.
23//! - [`protocol`]: the swap wire messages (serde, identical JSON, decoded through strict validating parsers), the
24//! composite safety checks of both parties, the timeout policy, pricing and privacy helpers.
25//! - [`keys`]: secp256k1 private and public keys and 32-byte secrets shared by all of the above.
26//!
27//! ## Example: an HTLC and its claim
28//!
29//! ```
30//! use paralock_core::keys::{KeyPair, Secret};
31//! use paralock_core::zcash::{
32//! build_htlc_claim, Htlc, HtlcParams, Network, TransparentAddress, TxOptions, Txid, Utxo,
33//! };
34//!
35//! # fn main() -> Result<(), paralock_core::Error> {
36//! let mut rng = paralock_core::OsRng;
37//! let maker = KeyPair::random(&mut rng);
38//! let taker = KeyPair::random(&mut rng);
39//! let secret = Secret::random(&mut rng);
40//!
41//! // The taker locks ZEC that the maker can claim with the secret, or the taker can refund at height 1_000.
42//! let htlc = Htlc::new(
43//! HtlcParams {
44//! hashlock: secret.hashlock(),
45//! claim_pubkey: maker.public_key(),
46//! refund_pubkey: taker.public_key(),
47//! refund_height: 1_000,
48//! },
49//! Network::Regtest,
50//! )?;
51//! assert!(htlc.address().starts_with("t2"));
52//!
53//! // Once funded, the maker claims with the secret. The consensus branch id comes from the node.
54//! let utxo = Utxo {
55//! txid: Txid::from_display_hex(&"11".repeat(32))?,
56//! vout: 0,
57//! value: 100_000_000,
58//! script_pubkey: htlc.script_pubkey().to_vec(),
59//! };
60//! let to = TransparentAddress::p2pkh(&maker.public_key(), Network::Regtest);
61//! let options = TxOptions::new(0x37a5_165b, Network::Regtest);
62//! let claim = build_htlc_claim(&htlc, &utxo, &maker, &secret, &to, &options)?;
63//! assert_eq!(claim.fee, 10_000);
64//! # Ok(())
65//! # }
66//! ```
67//!
68//! ## Safety
69//!
70//! The crate forbids `unsafe` code. Private keys, HTLC preimages and adaptor secrets are zeroized on drop and compared
71//! in constant time. Signatures are low-S everywhere, every adaptor pre-signature uses a fresh hedged nonce, and the
72//! redeem-script parsers accept only the exact Paralock templates. Values the builders and checks rely on are
73//! validated when they are constructed ([`keys::KeyPair`], [`protocol::TimeoutPolicy`], [`zcash::Htlc`],
74//! [`adaptor::VerifiedPreSignature`], …), so they can't be assembled inconsistently.
75//!
76//! Use the composite checks of [`protocol`] (`verify_*`, `complete_escrow_claim`) rather than the building blocks:
77//! they check everything a protocol step requires against the stored messages. **A claim that reverts still reveals
78//! the secret** (the preimage or the completed adaptor signature is in its calldata): re-check the lock right before
79//! claiming and simulate the claim (`eth_call`) before broadcasting it.
80//!
81//! This code is experimental; it has been through one security review, whose findings are fixed in 0.1.0 (see
82//! `CHANGELOG.md`), but it has not had a formal audit. The Zcash leg in htlc mode is transparent.
83#![forbid(unsafe_code)]
84
85pub mod adaptor;
86pub mod encoding;
87pub mod evm;
88pub mod keys;
89pub mod protocol;
90pub mod zcash;
91
92mod jsnum;
93
94#[cfg(test)]
95mod testutil;
96
97/// The README's examples, compiled and run as doctests (they use `OsRng`).
98#[cfg(all(doctest, feature = "getrandom"))]
99#[doc = include_str!("../README.md")]
100pub struct ReadmeDoctests;
101
102/// Regression tests for the security audit's type-level fixes (see `CHANGELOG.md`). Each `compile_fail` snippet is
103/// paired with a twin that compiles and differs only in the forbidden construct, so none can pass by accident (the
104/// error codes are checked on nightly only).
105///
106/// L3: a key pair can't pair a private key with someone else's public key.
107///
108/// ```
109/// use paralock_core::keys::{KeyPair, PrivateKey};
110/// let key = PrivateKey::from_bytes(&[1; 32]).unwrap();
111/// let pair = KeyPair::from_private(key);
112/// ```
113///
114/// ```compile_fail,E0451
115/// use paralock_core::keys::{KeyPair, PrivateKey};
116/// let key = PrivateKey::from_bytes(&[1; 32]).unwrap();
117/// let other = PrivateKey::from_bytes(&[2; 32]).unwrap();
118/// let pair = KeyPair { private_key: key, public_key: other.public_key() };
119/// ```
120///
121/// L1: a timeout policy is always validated.
122///
123/// ```
124/// use paralock_core::protocol::{TimeoutPolicy, TimeoutPolicyParams};
125/// let params = TimeoutPolicyParams {
126/// zcash_lock_blocks: 144, evm_lock_seconds: 3600, zcash_block_seconds: 75, min_zcash_confirmations: 2,
127/// safety_factor: -1.0,
128/// };
129/// assert!(TimeoutPolicy::new(params).is_err());
130/// ```
131///
132/// ```compile_fail,E0451
133/// use paralock_core::protocol::{TimeoutPolicy, TimeoutPolicyParams};
134/// let policy = TimeoutPolicy {
135/// zcash_lock_blocks: 144, evm_lock_seconds: 3600, zcash_block_seconds: 75, min_zcash_confirmations: 2,
136/// safety_factor: -1.0,
137/// };
138/// ```
139///
140/// M1: only a verified pre-signature can be completed.
141///
142/// ```
143/// use paralock_core::adaptor::{self, AdaptorSecret, PreSignature, VerifiedPreSignature};
144/// fn complete(pre: &VerifiedPreSignature, y: &AdaptorSecret) {
145/// let _ = adaptor::adapt(pre, y);
146/// }
147/// ```
148///
149/// ```compile_fail,E0308
150/// use paralock_core::adaptor::{self, AdaptorSecret, PreSignature, VerifiedPreSignature};
151/// fn complete(pre: &PreSignature, y: &AdaptorSecret) {
152/// let _ = adaptor::adapt(pre, y);
153/// }
154/// ```
155///
156/// ```compile_fail,E0451
157/// use paralock_core::adaptor::{PreSignature, VerifiedPreSignature};
158/// use paralock_core::keys::PublicKey;
159/// fn forge(pre: PreSignature, public_key: PublicKey, adaptor_point: PublicKey) -> VerifiedPreSignature {
160/// VerifiedPreSignature { pre, public_key, digest: [0; 32], adaptor_point }
161/// }
162/// ```
163///
164/// L4: change goes to an address, never a raw script, and lock outputs are paid to addresses.
165///
166/// ```
167/// use paralock_core::keys::KeyPair;
168/// use paralock_core::zcash::{FundingRequest, TransparentAddress};
169/// fn request<'a>(key: &'a KeyPair, change: TransparentAddress) -> FundingRequest<'a> {
170/// FundingRequest { utxos: &[], key, change: Some(change), expiry_height: 3_000_040 }
171/// }
172/// ```
173///
174/// ```compile_fail,E0560
175/// use paralock_core::keys::KeyPair;
176/// use paralock_core::zcash::{FundingRequest, TransparentAddress};
177/// fn request<'a>(key: &'a KeyPair, change: TransparentAddress) -> FundingRequest<'a> {
178/// FundingRequest { utxos: &[], key, change_script_pubkey: Some(&[]), expiry_height: 3_000_040 }
179/// }
180/// ```
181///
182/// ```compile_fail,E0432
183/// use paralock_core::zcash::Payee;
184/// ```
185///
186/// A 2-of-2 spend's sighash is always the one of its transaction (the maker's refund signature signs nothing else).
187///
188/// ```
189/// use paralock_core::zcash::MultisigSpend;
190/// fn digest(spend: &MultisigSpend) -> [u8; 32] {
191/// *spend.sighash()
192/// }
193/// ```
194///
195/// ```compile_fail,E0616
196/// use paralock_core::zcash::MultisigSpend;
197/// fn forge(spend: &mut MultisigSpend) {
198/// spend.sighash = [0; 32];
199/// }
200/// ```
201#[cfg(doctest)]
202pub struct AuditCompileFail;
203
204/// The operating system's random number generator (`rand_core::OsRng`). Requires the `getrandom` feature (on by
205/// default through `std`).
206#[cfg(feature = "getrandom")]
207pub use rand_core::OsRng;
208pub use rand_core::{CryptoRng, CryptoRngCore, RngCore};
209
210/// Any error this crate returns. Each module also has its own precise error type, which converts into this one.
211#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
212#[non_exhaustive]
213pub enum Error {
214 /// Malformed hex or a value of the wrong length.
215 #[error(transparent)]
216 Encoding(#[from] encoding::EncodingError),
217 /// An invalid key, point or secret.
218 #[error(transparent)]
219 Key(#[from] keys::KeyError),
220 /// A Zcash address, script, transaction or builder error.
221 #[error(transparent)]
222 Zcash(#[from] zcash::Error),
223 /// An adaptor-signature error.
224 #[error(transparent)]
225 Adaptor(#[from] adaptor::Error),
226 /// An EVM encoding or signature error.
227 #[error(transparent)]
228 Evm(#[from] evm::Error),
229 /// A protocol (validation, policy or pricing) error, with its wire error code.
230 #[error(transparent)]
231 Protocol(#[from] protocol::ParalockError),
232}