1use core::cmp::Ordering;
7use core::fmt;
8use core::str::FromStr;
9
10use sha2::{Digest, Sha256};
11use sha3::Keccak256;
12
13use crate::adaptor;
14use crate::encoding::{self, EncodingError};
15use crate::keys::{CURVE_ORDER_HALF, KeyError, PublicKey, Signature, is_low};
16
17pub const CLAIM_TYPEHASH: [u8; 32] = [
20 0x76, 0x49, 0x65, 0x52, 0xa5, 0xd8, 0x92, 0xc5, 0xce, 0x0b, 0x63, 0xbf, 0x3f, 0x56, 0x93, 0x57, 0xdd, 0x16, 0xc2,
21 0x40, 0x0a, 0xc0, 0x5d, 0x3c, 0x20, 0xbc, 0xab, 0xb4, 0x5b, 0x6a, 0x64, 0xca,
22];
23
24pub const SETTLED_STATE: [u8; 32] = {
26 let mut word = [0u8; 32];
27 word[31] = 1;
28 word
29};
30pub const EMPTY_STATE: [u8; 32] = [0u8; 32];
32
33#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
35#[non_exhaustive]
36pub enum Error {
37 #[error(transparent)]
39 Encoding(#[from] EncodingError),
40 #[error(transparent)]
42 Key(#[from] KeyError),
43 #[error("invalid EVM address")]
45 InvalidAddress,
46 #[error("invalid EVM address checksum")]
48 InvalidChecksum,
49 #[error("invalid uint256")]
51 InvalidUint,
52 #[error("invalid signature recovery value {0}")]
54 InvalidRecoveryValue(u64),
55 #[error("signature s is out of range")]
57 SignatureOutOfRange,
58 #[error("signature is not canonical (high s or v not 27/28)")]
60 MalleableSignature,
61 #[error("signature does not recover to a public key")]
63 InvalidSignature,
64 #[error("unknown adaptor escrow status {0}")]
66 UnknownStatus(u8),
67}
68
69pub fn keccak256(data: &[u8]) -> [u8; 32] {
71 Keccak256::digest(data).into()
72}
73
74pub fn hashlock_of(preimage: &[u8; 32]) -> [u8; 32] {
76 Sha256::digest(preimage).into()
77}
78
79#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
84pub struct EvmAddress(pub [u8; 20]);
85
86impl EvmAddress {
87 pub const ZERO: Self = Self([0u8; 20]);
89
90 pub fn parse(s: &str) -> Result<Self, Error> {
94 let address = Self::parse_any_case(s)?;
95 let digits = s.get(2..).unwrap_or("");
96 let mixed = digits.bytes().any(|b| b.is_ascii_uppercase()) && digits.bytes().any(|b| b.is_ascii_lowercase());
97 if mixed && address.to_checksum() != s {
98 return Err(Error::InvalidChecksum);
99 }
100 Ok(address)
101 }
102
103 pub fn parse_any_case(s: &str) -> Result<Self, Error> {
107 let digits = s.strip_prefix("0x").ok_or(Error::InvalidAddress)?;
108 if digits.len() != 40 || !digits.bytes().all(|b| b.is_ascii_hexdigit()) {
109 return Err(Error::InvalidAddress);
110 }
111 Ok(Self(encoding::decode_hex_array(digits, "address").map_err(|_| Error::InvalidAddress)?))
112 }
113
114 pub fn from_public_key(public_key: &PublicKey) -> Self {
116 let uncompressed = public_key.to_uncompressed();
117 let hash = keccak256(&uncompressed[1..]);
118 let mut out = [0u8; 20];
119 out.copy_from_slice(&hash[12..]);
120 Self(out)
121 }
122
123 pub fn is_zero(&self) -> bool {
125 self.0 == [0u8; 20]
126 }
127
128 pub fn to_hex(&self) -> String {
130 encoding::to_hex_0x(&self.0)
131 }
132
133 pub fn to_checksum(&self) -> String {
135 let lower = encoding::to_hex(&self.0);
136 let hash = keccak256(lower.as_bytes());
137 let mut out = String::with_capacity(42);
138 out.push_str("0x");
139 for (i, c) in lower.chars().enumerate() {
140 let nibble = (hash[i / 2] >> (if i % 2 == 0 { 4 } else { 0 })) & 0x0f;
141 out.push(if c.is_ascii_alphabetic() && nibble >= 8 { c.to_ascii_uppercase() } else { c });
142 }
143 out
144 }
145
146 pub fn to_word(&self) -> [u8; 32] {
148 let mut word = [0u8; 32];
149 word[12..].copy_from_slice(&self.0);
150 word
151 }
152}
153
154impl fmt::Display for EvmAddress {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 f.write_str(&self.to_hex())
157 }
158}
159
160impl fmt::Debug for EvmAddress {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 write!(f, "EvmAddress({})", self.to_hex())
163 }
164}
165
166impl FromStr for EvmAddress {
167 type Err = Error;
168
169 fn from_str(s: &str) -> Result<Self, Self::Err> {
171 Self::parse(s)
172 }
173}
174
175pub fn evm_address_of(public_key: &PublicKey) -> EvmAddress {
177 EvmAddress::from_public_key(public_key)
178}
179
180#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
185pub struct U256([u8; 32]);
186
187impl U256 {
188 pub const ZERO: Self = Self([0u8; 32]);
190
191 pub const fn from_be_bytes(bytes: [u8; 32]) -> Self {
193 Self(bytes)
194 }
195
196 pub const fn to_be_bytes(&self) -> [u8; 32] {
198 self.0
199 }
200
201 pub fn from_u128(n: u128) -> Self {
203 let mut out = [0u8; 32];
204 out[16..].copy_from_slice(&n.to_be_bytes());
205 Self(out)
206 }
207
208 pub fn to_u128(&self) -> Option<u128> {
210 if self.0[..16].iter().any(|&b| b != 0) {
211 return None;
212 }
213 let mut low = [0u8; 16];
214 low.copy_from_slice(&self.0[16..]);
215 Some(u128::from_be_bytes(low))
216 }
217
218 pub fn is_zero(&self) -> bool {
220 self.0 == [0u8; 32]
221 }
222
223 fn limbs(&self) -> [u64; 4] {
224 let mut limbs = [0u64; 4];
225 for (i, limb) in limbs.iter_mut().enumerate() {
226 let mut b = [0u8; 8];
227 b.copy_from_slice(&self.0[24 - 8 * i..32 - 8 * i]);
228 *limb = u64::from_be_bytes(b);
229 }
230 limbs
231 }
232
233 fn from_limbs(limbs: [u64; 4]) -> Self {
234 let mut out = [0u8; 32];
235 for (i, limb) in limbs.iter().enumerate() {
236 out[24 - 8 * i..32 - 8 * i].copy_from_slice(&limb.to_be_bytes());
237 }
238 Self(out)
239 }
240
241 pub fn from_dec_str(s: &str) -> Result<Self, Error> {
243 if s.is_empty() || s.len() > 78 || !s.bytes().all(|b| b.is_ascii_digit()) {
244 return Err(Error::InvalidUint);
245 }
246 let mut limbs = [0u64; 4];
247 for digit in s.bytes() {
248 let mut carry = u128::from(digit - b'0');
249 for limb in &mut limbs {
250 let v = u128::from(*limb) * 10 + carry;
251 *limb = v as u64;
252 carry = v >> 64;
253 }
254 if carry != 0 {
255 return Err(Error::InvalidUint);
256 }
257 }
258 Ok(Self::from_limbs(limbs))
259 }
260
261 pub fn to_dec_string(&self) -> String {
263 const CHUNK: u64 = 10_000_000_000_000_000_000; let mut limbs = self.limbs();
265 let mut chunks = Vec::new();
266 loop {
267 let mut rem: u128 = 0;
268 for limb in limbs.iter_mut().rev() {
269 let cur = (rem << 64) | u128::from(*limb);
270 *limb = (cur / u128::from(CHUNK)) as u64;
271 rem = cur % u128::from(CHUNK);
272 }
273 chunks.push(rem as u64);
274 if limbs.iter().all(|&l| l == 0) {
275 break;
276 }
277 }
278 let mut out = String::new();
279 for (i, chunk) in chunks.iter().rev().enumerate() {
280 if i == 0 {
281 out.push_str(&chunk.to_string());
282 } else {
283 out.push_str(&format!("{chunk:019}"));
284 }
285 }
286 out
287 }
288}
289
290impl From<u128> for U256 {
291 fn from(n: u128) -> Self {
292 Self::from_u128(n)
293 }
294}
295
296impl From<u64> for U256 {
297 fn from(n: u64) -> Self {
298 Self::from_u128(u128::from(n))
299 }
300}
301
302impl PartialOrd for U256 {
303 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
304 Some(self.cmp(other))
305 }
306}
307
308impl Ord for U256 {
309 fn cmp(&self, other: &Self) -> Ordering {
310 self.0.cmp(&other.0)
311 }
312}
313
314impl fmt::Display for U256 {
315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316 f.write_str(&self.to_dec_string())
317 }
318}
319
320impl fmt::Debug for U256 {
321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322 write!(f, "U256({})", self.to_dec_string())
323 }
324}
325
326impl FromStr for U256 {
327 type Err = Error;
328
329 fn from_str(s: &str) -> Result<Self, Self::Err> {
330 Self::from_dec_str(s)
331 }
332}
333
334fn u64_word(n: u64) -> [u8; 32] {
335 let mut word = [0u8; 32];
336 word[24..].copy_from_slice(&n.to_be_bytes());
337 word
338}
339
340#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
345pub struct HtlcSwap {
346 pub hashlock: [u8; 32],
348 pub token: EvmAddress,
350 pub sender: EvmAddress,
352 pub recipient: EvmAddress,
354 pub amount: U256,
356 pub expiry: u64,
358}
359
360impl HtlcSwap {
361 pub fn abi_encode(&self) -> [u8; 192] {
363 let mut out = [0u8; 192];
364 for (i, word) in [
365 self.hashlock,
366 self.token.to_word(),
367 self.sender.to_word(),
368 self.recipient.to_word(),
369 self.amount.to_be_bytes(),
370 u64_word(self.expiry),
371 ]
372 .iter()
373 .enumerate()
374 {
375 out[32 * i..32 * (i + 1)].copy_from_slice(word);
376 }
377 out
378 }
379
380 pub fn commitment(&self) -> [u8; 32] {
382 keccak256(&self.abi_encode())
383 }
384}
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
388pub struct AdaptorSwap {
389 pub token: EvmAddress,
391 pub sender: EvmAddress,
393 pub recipient: EvmAddress,
395 pub signer: EvmAddress,
397 pub amount: U256,
399 pub expiry: u64,
401 pub salt: [u8; 32],
403}
404
405impl AdaptorSwap {
406 pub fn abi_encode(&self) -> [u8; 224] {
408 let mut out = [0u8; 224];
409 for (i, word) in [
410 self.token.to_word(),
411 self.sender.to_word(),
412 self.recipient.to_word(),
413 self.signer.to_word(),
414 self.amount.to_be_bytes(),
415 u64_word(self.expiry),
416 self.salt,
417 ]
418 .iter()
419 .enumerate()
420 {
421 out[32 * i..32 * (i + 1)].copy_from_slice(word);
422 }
423 out
424 }
425
426 pub fn id(&self) -> [u8; 32] {
428 keccak256(&self.abi_encode())
429 }
430}
431
432pub fn adaptor_swap_id(swap: &AdaptorSwap) -> [u8; 32] {
434 swap.id()
435}
436
437pub fn claim_digest(chain_id: u64, contract: &EvmAddress, id: &[u8; 32]) -> [u8; 32] {
440 keccak256(&claim_digest_preimage(chain_id, contract, id))
441}
442
443pub fn claim_digest_preimage(chain_id: u64, contract: &EvmAddress, id: &[u8; 32]) -> [u8; 128] {
445 let mut data = [0u8; 128];
446 data[..32].copy_from_slice(&CLAIM_TYPEHASH);
447 data[32..64].copy_from_slice(&u64_word(chain_id));
448 data[64..96].copy_from_slice(&contract.to_word());
449 data[96..].copy_from_slice(id);
450 data
451}
452
453#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
458pub struct ClaimSignature {
459 pub v: u8,
461 pub r: [u8; 32],
463 pub s: [u8; 32],
465}
466
467impl ClaimSignature {
468 pub fn signature(&self) -> Result<Signature, Error> {
470 Ok(Signature::from_scalars(self.r, self.s)?)
471 }
472}
473
474pub fn normalize_claim_signature(r: [u8; 32], s: [u8; 32], v_or_parity: u64) -> Result<ClaimSignature, Error> {
478 let mut parity = if v_or_parity >= 27 { v_or_parity - 27 } else { v_or_parity };
479 if parity > 1 {
480 return Err(Error::InvalidRecoveryValue(v_or_parity));
481 }
482 let sig_s = Signature::from_scalars([1u8; 32], s).map_err(|_| Error::SignatureOutOfRange)?;
484 let low = sig_s.normalize_s();
485 if low.s() != s {
486 parity ^= 1;
487 }
488 Ok(ClaimSignature { v: 27 + parity as u8, r, s: low.s() })
489}
490
491pub fn claim_signature(signature: &Signature, signer: &PublicKey, digest: &[u8; 32]) -> Result<ClaimSignature, Error> {
493 let sig = signature.normalize_s();
494 let id = adaptor::recovery_id(signer, digest, &sig).map_err(|_| Error::InvalidSignature)?;
495 Ok(ClaimSignature { v: 27 + id, r: sig.r(), s: sig.s() })
496}
497
498pub fn recover_signer(digest: &[u8; 32], sig: &ClaimSignature) -> Result<EvmAddress, Error> {
501 if !is_low(&sig.s) || (sig.v != 27 && sig.v != 28) {
502 return Err(Error::MalleableSignature);
503 }
504 let signature = Signature::from_scalars(sig.r, sig.s).map_err(|_| Error::InvalidSignature)?;
505 let key = adaptor::recover_public_key(digest, &signature, sig.v - 27).map_err(|_| Error::InvalidSignature)?;
506 Ok(EvmAddress::from_public_key(&key))
507}
508
509pub const SECP256K1_N_HALF: [u8; 32] = CURVE_ORDER_HALF;
511
512#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
517pub enum EscrowStatus {
518 Empty,
520 Active,
522 Settled,
524}
525
526pub fn htlc_status_of(raw: &[u8; 32]) -> EscrowStatus {
528 if *raw == EMPTY_STATE {
529 EscrowStatus::Empty
530 } else if *raw == SETTLED_STATE {
531 EscrowStatus::Settled
532 } else {
533 EscrowStatus::Active
534 }
535}
536
537pub fn adaptor_status_of(raw: u8) -> Result<EscrowStatus, Error> {
539 match raw {
540 0 => Ok(EscrowStatus::Empty),
541 1 => Ok(EscrowStatus::Active),
542 2 => Ok(EscrowStatus::Settled),
543 other => Err(Error::UnknownStatus(other)),
544 }
545}
546
547#[cfg(test)]
548mod tests {
549 #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
550 use super::*;
551
552 #[test]
553 fn claim_typehash_is_keccak_of_the_tag() {
554 assert_eq!(keccak256(b"ParalockAdaptorClaim"), CLAIM_TYPEHASH);
555 }
556
557 #[test]
558 fn u256_decimal_round_trips() {
559 for s in ["0", "1", "18446744073709551616", "1000000000000000000000000000000", &"9".repeat(77)] {
560 assert_eq!(U256::from_dec_str(s).unwrap().to_dec_string(), s);
561 }
562 let max = "115792089237316195423570985008687907853269984665640564039457584007913129639935";
563 assert_eq!(U256::from_dec_str(max).unwrap().to_be_bytes(), [0xff; 32]);
564 assert_eq!(
565 U256::from_dec_str("115792089237316195423570985008687907853269984665640564039457584007913129639936"),
566 Err(Error::InvalidUint)
567 );
568 assert!(U256::from_dec_str("").is_err());
569 assert!(U256::from_dec_str("-1").is_err());
570 assert_eq!(U256::from(u128::MAX).to_u128(), Some(u128::MAX));
571 }
572
573 #[test]
574 fn addresses_parse_and_checksum() {
575 for good in [
577 "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
578 "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
579 "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
580 "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
581 ] {
582 let a = EvmAddress::parse(good).unwrap();
583 assert_eq!(a.to_checksum(), good);
584 assert_eq!(good.parse::<EvmAddress>().unwrap(), a);
585 }
586 let typo = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAeD";
588 assert_eq!(EvmAddress::parse(typo), Err(Error::InvalidChecksum));
589 assert_eq!(typo.parse::<EvmAddress>(), Err(Error::InvalidChecksum));
590 assert!(EvmAddress::parse_any_case(typo).is_ok());
591 assert!(EvmAddress::parse("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed").is_ok());
593 assert!(EvmAddress::parse("0x5AAEB6053F3E94C9B9A09F33669435E7EF1BEAED").is_ok());
594 assert!(EvmAddress::parse("5aaeb6053f3e94c9b9a09f33669435e7ef1beaed").is_err());
595 assert!(EvmAddress::parse("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beae").is_err());
596 }
597
598 #[test]
599 fn normalizes_claim_signatures() {
600 let key = crate::keys::KeyPair::random(&mut crate::testutil::TestRng);
601 let digest = keccak256(b"claim");
602 let sig = key.private_key().sign_prehash(&digest).unwrap();
603 let canonical = claim_signature(&sig, &key.public_key(), &digest).unwrap();
604 assert_eq!(recover_signer(&digest, &canonical).unwrap(), EvmAddress::from_public_key(&key.public_key()));
605 let s = crate::keys::scalar_from_bytes(&sig.s()).unwrap();
607 let high_s = crate::keys::scalar_to_bytes(&-s);
608 let flipped = 27 + (1 - (canonical.v - 27)) as u64;
609 assert_eq!(normalize_claim_signature(sig.r(), high_s, flipped).unwrap(), canonical);
610 assert_eq!(normalize_claim_signature(sig.r(), sig.s(), u64::from(canonical.v) - 27).unwrap(), canonical);
611 assert_eq!(recover_signer(&digest, &ClaimSignature { s: high_s, ..canonical }), Err(Error::MalleableSignature));
612 assert_eq!(recover_signer(&digest, &ClaimSignature { v: 29, ..canonical }), Err(Error::MalleableSignature));
613 assert_eq!(normalize_claim_signature(sig.r(), sig.s(), 2), Err(Error::InvalidRecoveryValue(2)));
614 assert_eq!(normalize_claim_signature(sig.r(), [0; 32], 27), Err(Error::SignatureOutOfRange));
615 }
616
617 #[test]
618 fn maps_statuses() {
619 assert_eq!(htlc_status_of(&EMPTY_STATE), EscrowStatus::Empty);
620 assert_eq!(htlc_status_of(&SETTLED_STATE), EscrowStatus::Settled);
621 assert_eq!(htlc_status_of(&[7; 32]), EscrowStatus::Active);
622 assert_eq!(adaptor_status_of(3), Err(Error::UnknownStatus(3)));
623 }
624}