1use bitcoin::hashes::Hash as BitcoinHash;
2use fedimint_core::Amount;
3use fedimint_core::encoding::{Decodable, Encodable};
4use fedimint_core::secp256k1::PublicKey;
5use serde::{Deserialize, Serialize};
67use super::Preimage;
8use crate::LightningInput;
9use crate::contracts::{ContractId, IdentifiableContract};
1011const CANCELLATION_TAG: &str = "outgoing contract cancellation";
1213/// Specialized smart contract for outgoing payments.
14///
15/// A user locks up funds that can be claimed by a lightning gateway if it pays
16/// the invoice and thus receives the preimage to the payment hash and can
17/// thereby prove the payment. If the gateway is not able to do so before the
18/// timelock expires the user can claim back the funds.
19#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
20pub struct OutgoingContract {
21/// Hash that can be used to spend the output before the timelock expires
22pub hash: bitcoin::hashes::sha256::Hash,
23/// Public key of the LN gateway allowed to claim the HTLC before the
24 /// timelock expires
25pub gateway_key: PublicKey,
26/// Block height at which the money will be spendable by the pubkey
27pub timelock: u32,
28/// Public key of the user that can claim the money back after the timelock
29 /// expires
30pub user_key: PublicKey,
31/// Flag that can be set by the gateway and allows the client to claim an
32 /// early refund
33pub cancelled: bool,
34}
3536impl IdentifiableContract for OutgoingContract {
37fn contract_id(&self) -> ContractId {
38let mut engine = ContractId::engine();
39 Encodable::consensus_encode(&self.hash, &mut engine).expect("Hashing never fails");
40 Encodable::consensus_encode(&self.gateway_key, &mut engine).expect("Hashing never fails");
41 Encodable::consensus_encode(&self.timelock, &mut engine).expect("Hashing never fails");
42 Encodable::consensus_encode(&self.user_key, &mut engine).expect("Hashing never fails");
43 ContractId::from_engine(engine)
44 }
45}
4647impl OutgoingContract {
48pub fn cancellation_message(&self) -> bitcoin::hashes::sha256::Hash {
49let mut engine = bitcoin::hashes::sha256::Hash::engine();
50 Encodable::consensus_encode(&CANCELLATION_TAG.as_bytes(), &mut engine)
51 .expect("Hashing never fails");
52 Encodable::consensus_encode(&self.contract_id(), &mut engine).expect("Hashing never fails");
53 bitcoin::hashes::sha256::Hash::from_engine(engine)
54 }
55}
5657#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)]
58pub struct OutgoingContractData {
59pub recovery_key: bitcoin::key::Keypair,
60pub contract_account: OutgoingContractAccount,
61}
6263#[derive(Debug, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Serialize, Deserialize)]
64pub struct OutgoingContractAccount {
65pub amount: Amount,
66pub contract: OutgoingContract,
67}
6869impl OutgoingContractAccount {
70pub fn claim(&self, preimage: Preimage) -> LightningInput {
71 LightningInput::new_v0(self.contract.contract_id(), self.amount, Some(preimage))
72 }
7374pub fn refund(&self) -> LightningInput {
75 LightningInput::new_v0(self.contract.contract_id(), self.amount, None)
76 }
77}