Skip to main content

Crate paralock_core

Crate paralock_core 

Source
Expand description

§paralock-core

The protocol core of Paralock: trustless swaps between ZEC and stablecoins on EVM chains. A lock on Zcash and a lock on the EVM chain share one secret, so either both sides settle or both refund.

This crate is the single implementation behind every Paralock SDK. It is pure and synchronous: no I/O, no clocks, no network, no async. Randomness is injected by the caller as a rand_core::CryptoRngCore (an OsRng convenience is available with the default std feature), consensus branch ids and the current time are always inputs, and every parser of untrusted data returns a Result instead of panicking.

It is a byte-for-byte port of the TypeScript reference (@paralock/zcash, @paralock/adaptor, @paralock/evm, @paralock/sdk): scripts, addresses, transactions, signature hashes, txids, escrow ids, claim digests, adaptor pre-signatures and JSON messages are identical, and the test suite checks them against shared cross-language vectors.

§Modules

  • zcash: transparent addresses, keys, scripts, the HTLC and 2-of-2 multisig templates, v5 transactions, ZIP 244 txids and signature hashes, ZIP 317 fees and the transaction builders.
  • adaptor: ECDSA adaptor signatures on secp256k1 with Chaum–Pedersen DLEQ proofs (private mode).
  • evm: ParalockHTLC / ParalockAdaptorEscrow swap ids, claim digests, hashlocks, recovery ids and EVM addresses.
  • protocol: the swap wire messages (serde, identical JSON, decoded through strict validating parsers), the composite safety checks of both parties, the timeout policy, pricing and privacy helpers.
  • keys: secp256k1 private and public keys and 32-byte secrets shared by all of the above.

§Example: an HTLC and its claim

use paralock_core::keys::{KeyPair, Secret};
use paralock_core::zcash::{
    build_htlc_claim, Htlc, HtlcParams, Network, TransparentAddress, TxOptions, Txid, Utxo,
};

let mut rng = paralock_core::OsRng;
let maker = KeyPair::random(&mut rng);
let taker = KeyPair::random(&mut rng);
let secret = Secret::random(&mut rng);

// The taker locks ZEC that the maker can claim with the secret, or the taker can refund at height 1_000.
let htlc = Htlc::new(
    HtlcParams {
        hashlock: secret.hashlock(),
        claim_pubkey: maker.public_key(),
        refund_pubkey: taker.public_key(),
        refund_height: 1_000,
    },
    Network::Regtest,
)?;
assert!(htlc.address().starts_with("t2"));

// Once funded, the maker claims with the secret. The consensus branch id comes from the node.
let utxo = Utxo {
    txid: Txid::from_display_hex(&"11".repeat(32))?,
    vout: 0,
    value: 100_000_000,
    script_pubkey: htlc.script_pubkey().to_vec(),
};
let to = TransparentAddress::p2pkh(&maker.public_key(), Network::Regtest);
let options = TxOptions::new(0x37a5_165b, Network::Regtest);
let claim = build_htlc_claim(&htlc, &utxo, &maker, &secret, &to, &options)?;
assert_eq!(claim.fee, 10_000);

§Safety

The crate forbids unsafe code. Private keys, HTLC preimages and adaptor secrets are zeroized on drop and compared in constant time. Signatures are low-S everywhere, every adaptor pre-signature uses a fresh hedged nonce, and the redeem-script parsers accept only the exact Paralock templates. Values the builders and checks rely on are validated when they are constructed (keys::KeyPair, protocol::TimeoutPolicy, zcash::Htlc, adaptor::VerifiedPreSignature, …), so they can’t be assembled inconsistently.

Use the composite checks of protocol (verify_*, complete_escrow_claim) rather than the building blocks: they check everything a protocol step requires against the stored messages. A claim that reverts still reveals the secret (the preimage or the completed adaptor signature is in its calldata): re-check the lock right before claiming and simulate the claim (eth_call) before broadcasting it.

This code is experimental; it has been through one security review, whose findings are fixed in 0.1.0 (see CHANGELOG.md), but it has not had a formal audit. The Zcash leg in htlc mode is transparent.

Modules§

adaptor
ECDSA adaptor signatures on secp256k1, for scriptless atomic swaps (a port of @paralock/adaptor).
encoding
Byte encodings shared by the whole crate: hex (with or without 0x), Bitcoin-style CompactSize lengths and a bounds-checked little-endian Reader.
evm
The EVM leg: ParalockHTLC and ParalockAdaptorEscrow swap structs, their ids and commitments (keccak256(abi.encode(swap))), the adaptor escrow’s claim digest, hashlocks, canonical (v, r, s) claim signatures and EVM addresses. A port of @paralock/evm’s pure helpers with hand-written ABI encoding (every field is a static 32-byte word).
keys
secp256k1 keys, ECDSA signatures and 32-byte secrets.
protocol
The swap protocol: wire messages, validation, timeouts, pricing and privacy helpers (a port of @paralock/sdk’s types.ts, adaptor-protocol.ts, validation.ts, policy.ts, pricing.ts, privacy.ts, secret.ts and the error codes of errors.ts).
zcash
Zcash: transparent addresses, scripts, the Paralock HTLC and 2-of-2 templates, v5 transactions, ZIP 244 and ZIP 317, and the transaction builders. A port of @paralock/zcash (plus the ZIP 244 shielded digests of @paralock/lightwalletd), byte for byte.

Structs§

OsRng
The operating system’s random number generator (rand_core::OsRng). Requires the getrandom feature (on by default through std). A random number generator that retrieves randomness from the operating system.

Enums§

Error
Any error this crate returns. Each module also has its own precise error type, which converts into this one.

Traits§

CryptoRng
A marker trait used to indicate that an RngCore or BlockRngCore implementation is supposed to be cryptographically secure.
CryptoRngCore
An extension trait that is automatically implemented for any type implementing RngCore and CryptoRng.
RngCore
The core of a random number generator.