fedimint_ln_common/contracts/
mod.rs

1pub mod incoming;
2pub mod outgoing;
3
4use std::fmt::Display;
5use std::io::Error;
6
7use bitcoin::hashes::sha256::Hash as Sha256;
8use bitcoin::hashes::{Hash as BitcoinHash, hash_newtype};
9use fedimint_core::encoding::{Decodable, DecodeError, Encodable};
10use fedimint_core::hex::ToHex;
11use fedimint_core::module::registry::ModuleDecoderRegistry;
12use fedimint_core::{OutPoint, secp256k1};
13use serde::{Deserialize, Serialize};
14
15/// Anything representing a contract which thus has an associated [`ContractId`]
16pub trait IdentifiableContract: Encodable {
17    fn contract_id(&self) -> ContractId;
18}
19
20hash_newtype!(
21    /// The hash of a LN incoming contract
22    pub struct ContractId(Sha256);
23);
24
25/// A contract before execution as found in transaction outputs
26// TODO: investigate if this is actually a problem
27#[allow(clippy::large_enum_variant)]
28#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
29pub enum Contract {
30    Incoming(incoming::IncomingContract),
31    Outgoing(outgoing::OutgoingContract),
32}
33
34/// A contract after execution as saved in the database
35#[allow(clippy::large_enum_variant)]
36#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)]
37pub enum FundedContract {
38    Incoming(incoming::FundedIncomingContract),
39    Outgoing(outgoing::OutgoingContract),
40}
41
42/// Outcome of a contract. Only incoming contracts currently need to communicate
43/// anything back to the user (the decrypted preimage).
44#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
45pub enum ContractOutcome {
46    Incoming(DecryptedPreimage),
47    Outgoing(OutgoingContractOutcome),
48}
49
50impl ContractOutcome {
51    pub fn is_permanent(&self) -> bool {
52        match self {
53            ContractOutcome::Incoming(o) => o.is_permanent(),
54            ContractOutcome::Outgoing(_) => true,
55        }
56    }
57}
58
59#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
60pub struct OutgoingContractOutcome {}
61
62impl IdentifiableContract for Contract {
63    fn contract_id(&self) -> ContractId {
64        match self {
65            Contract::Incoming(c) => c.contract_id(),
66            Contract::Outgoing(c) => c.contract_id(),
67        }
68    }
69}
70
71impl IdentifiableContract for FundedContract {
72    fn contract_id(&self) -> ContractId {
73        match self {
74            FundedContract::Incoming(c) => c.contract.contract_id(),
75            FundedContract::Outgoing(c) => c.contract_id(),
76        }
77    }
78}
79
80impl Contract {
81    /// Creates the initial contract outcome that is created on transaction
82    /// acceptance. Depending on the contract type it is not yet final.
83    pub fn to_outcome(&self) -> ContractOutcome {
84        match self {
85            Contract::Incoming(_) => ContractOutcome::Incoming(DecryptedPreimage::Pending),
86            Contract::Outgoing(_) => ContractOutcome::Outgoing(OutgoingContractOutcome {}),
87        }
88    }
89
90    /// Converts a contract to its executed version.
91    pub fn to_funded(self, out_point: OutPoint) -> FundedContract {
92        match self {
93            Contract::Incoming(incoming) => {
94                FundedContract::Incoming(incoming::FundedIncomingContract {
95                    contract: incoming,
96                    out_point,
97                })
98            }
99            Contract::Outgoing(outgoing) => FundedContract::Outgoing(outgoing),
100        }
101    }
102}
103
104impl Encodable for ContractId {
105    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
106        self.to_byte_array().consensus_encode(writer)
107    }
108}
109
110impl Decodable for ContractId {
111    fn consensus_decode_partial<D: std::io::Read>(
112        d: &mut D,
113        modules: &ModuleDecoderRegistry,
114    ) -> Result<Self, DecodeError> {
115        Ok(ContractId::from_byte_array(
116            Decodable::consensus_decode_partial(d, modules)?,
117        ))
118    }
119}
120
121#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
122pub struct Preimage(pub [u8; 32]);
123
124impl Display for Preimage {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        write!(f, "{}", self.0.encode_hex::<String>())
127    }
128}
129
130#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
131pub struct PreimageKey(#[serde(with = "serde_big_array::BigArray")] pub [u8; 33]);
132
133impl PreimageKey {
134    /// Create a Schnorr public key
135    ///
136    /// # Errors
137    ///
138    /// Returns [`secp256k1::Error::InvalidPublicKey`] if the Preimage does not
139    /// represent a valid Secp256k1 point x coordinate.
140    pub fn to_public_key(&self) -> Result<secp256k1::PublicKey, secp256k1::Error> {
141        secp256k1::PublicKey::from_slice(&self.0)
142    }
143}
144
145/// Current status of preimage decryption
146#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
147pub enum DecryptedPreimageStatus {
148    /// There aren't enough decryption shares yet
149    Pending,
150    /// The decrypted preimage was valid
151    Some(Preimage),
152    /// The decrypted preimage was invalid
153    Invalid,
154}
155
156/// Possible outcomes of preimage decryption
157#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
158pub enum DecryptedPreimage {
159    /// There aren't enough decryption shares yet
160    Pending,
161    /// The decrypted preimage was valid
162    Some(PreimageKey),
163    /// The decrypted preimage was invalid
164    Invalid,
165}
166
167impl DecryptedPreimage {
168    pub fn is_permanent(&self) -> bool {
169        match self {
170            DecryptedPreimage::Pending => false,
171            DecryptedPreimage::Some(_) | DecryptedPreimage::Invalid => true,
172        }
173    }
174}
175/// Threshold-encrypted [`Preimage`]
176#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable, Deserialize, Serialize)]
177pub struct EncryptedPreimage(pub threshold_crypto::Ciphertext);
178
179/// Share to decrypt an [`EncryptedPreimage`]
180#[derive(Debug, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Serialize, Deserialize)]
181pub struct PreimageDecryptionShare(pub threshold_crypto::DecryptionShare);
182
183impl EncryptedPreimage {
184    pub fn new(preimage_key: &PreimageKey, key: &threshold_crypto::PublicKey) -> EncryptedPreimage {
185        EncryptedPreimage(key.encrypt(preimage_key.0))
186    }
187}