├── rust-toolchain ├── .gitignore ├── spec.pdf ├── src ├── bitcoin │ ├── keygen.rs │ ├── wallet_outputs.rs │ ├── keys.rs │ ├── offer.rs │ ├── event.rs │ ├── client.rs │ ├── alice.rs │ ├── node.rs │ ├── transaction.rs │ ├── bob.rs │ ├── mod.rs │ ├── sign.rs │ ├── action.rs │ └── wallet.rs ├── execute.rs ├── look_for.rs ├── grin │ ├── keygen.rs │ ├── offer.rs │ ├── event.rs │ ├── keys.rs │ ├── special_outputs.rs │ ├── alice.rs │ ├── bob.rs │ ├── bulletproof.rs │ ├── wallet.rs │ ├── sign.rs │ ├── mod.rs │ └── action.rs ├── lib.rs ├── messages.rs ├── commit.rs ├── dleq.rs ├── bin │ ├── refund.rs │ ├── grin_bitcoin_happy.rs │ └── bitcoin_grin_happy.rs ├── keypair.rs ├── bob.rs ├── ecdsa.rs ├── alice.rs └── schnorr.rs ├── README.md ├── rustfmt.toml ├── Cargo.toml ├── tests └── schnorr_ecdsa.rs └── LICENSE /rust-toolchain: -------------------------------------------------------------------------------- 1 | 1.41.0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /spec.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/comit-network/grin-btc-poc/HEAD/spec.pdf -------------------------------------------------------------------------------- /src/bitcoin/keygen.rs: -------------------------------------------------------------------------------- 1 | use crate::{bitcoin::SKs, KeyPair}; 2 | 3 | pub fn keygen() -> SKs { 4 | let x = KeyPair::new_random(); 5 | 6 | SKs { x } 7 | } 8 | -------------------------------------------------------------------------------- /src/execute.rs: -------------------------------------------------------------------------------- 1 | pub trait Execute { 2 | type Wallet; 3 | type Return; 4 | 5 | fn execute(self, wallet: &Self::Wallet) -> anyhow::Result; 6 | } 7 | -------------------------------------------------------------------------------- /src/look_for.rs: -------------------------------------------------------------------------------- 1 | pub trait LookFor { 2 | type Event; 3 | type Extract; 4 | 5 | fn look_for(&self, event: Self::Event) -> anyhow::Result; 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Implementation of a Grin-Bitcoin atomic swap using one-time verifiably encrypted signatures. For more information, check out the [protocol specification](https://github.com/comit-network/grin-btc-poc/blob/master/spec.pdf). 2 | -------------------------------------------------------------------------------- /src/bitcoin/wallet_outputs.rs: -------------------------------------------------------------------------------- 1 | use crate::bitcoin::{wallet, Address}; 2 | 3 | #[derive(Clone)] 4 | pub struct WalletOutputs { 5 | pub fund_input: wallet::Output, 6 | pub fund_change_address: Address, 7 | pub redeem_address: Address, 8 | pub refund_address: Address, 9 | } 10 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" 2 | condense_wildcard_suffixes = true 3 | format_macro_matchers = true 4 | merge_imports = true 5 | use_field_init_shorthand = true 6 | format_code_in_doc_comments = true 7 | normalize_comments = true 8 | wrap_comments = true 9 | overflow_delimited_expr = true 10 | -------------------------------------------------------------------------------- /src/grin/keygen.rs: -------------------------------------------------------------------------------- 1 | use crate::{grin::SKs, KeyPair}; 2 | 3 | pub fn keygen() -> SKs { 4 | let x = KeyPair::new_random(); 5 | 6 | let r_fund = KeyPair::new_random(); 7 | let r_redeem = KeyPair::new_random(); 8 | let r_refund = KeyPair::new_random(); 9 | 10 | SKs { 11 | x, 12 | r_fund, 13 | r_redeem, 14 | r_refund, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | pub mod alice; 4 | pub mod bitcoin; 5 | pub mod bob; 6 | pub mod commit; 7 | pub mod dleq; 8 | pub mod ecdsa; 9 | pub mod execute; 10 | pub mod grin; 11 | pub mod keypair; 12 | pub mod look_for; 13 | pub mod messages; 14 | pub mod schnorr; 15 | 16 | pub use execute::Execute; 17 | pub use keypair::{KeyPair, PublicKey, SecretKey}; 18 | pub use look_for::LookFor; 19 | 20 | pub trait Hash { 21 | fn hash(&self) -> [u8; 64]; 22 | } 23 | -------------------------------------------------------------------------------- /src/grin/offer.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub struct Offer { 3 | pub asset: u64, 4 | pub fee: u64, // for simplicity we don't model separate fee values for different transactions 5 | pub expiry: u64, // block height 6 | } 7 | 8 | impl Offer { 9 | pub fn fund_input_amount(&self) -> u64 { 10 | self.asset + (2 * self.fee) 11 | } 12 | 13 | pub fn fund_output_amount(&self) -> u64 { 14 | self.asset + self.fee 15 | } 16 | 17 | pub fn redeem_output_amount(&self) -> u64 { 18 | self.asset 19 | } 20 | 21 | pub fn refund_output_amount(&self) -> u64 { 22 | self.asset 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/bitcoin/keys.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | keypair::{KeyPair, PublicKey}, 3 | Hash, 4 | }; 5 | use blake2::{Blake2b, Digest}; 6 | 7 | #[derive(Clone)] 8 | pub struct SKs { 9 | pub x: KeyPair, 10 | } 11 | 12 | impl Into for SKs { 13 | fn into(self) -> PKs { 14 | PKs { 15 | X: self.x.public_key, 16 | } 17 | } 18 | } 19 | 20 | #[derive(Clone)] 21 | pub struct PKs { 22 | pub X: PublicKey, 23 | } 24 | 25 | impl Hash for PKs { 26 | fn hash(&self) -> [u8; 64] { 27 | let mut hasher = Blake2b::new(); 28 | 29 | hasher.input(self.X.0); 30 | 31 | let mut hash = [0u8; 64]; 32 | hash.copy_from_slice(&hasher.result()); 33 | hash 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/messages.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | commit::{Commitment, Opening}, 3 | grin, 4 | }; 5 | 6 | // Sent by Alice 7 | pub struct Message0 { 8 | pub commitment: Commitment, 9 | pub bulletproof_round_1_alice: grin::bulletproof::Round1, 10 | } 11 | 12 | // Sent by Bob 13 | pub struct Message1 { 14 | pub PKs_alpha: A, 15 | pub PKs_beta: B, 16 | pub bulletproof_round_1_bob: grin::bulletproof::Round1, 17 | } 18 | 19 | // Sent by Alice 20 | pub struct Message2 { 21 | pub opening: Opening, 22 | pub beta_redeemer_sigs: B, 23 | } 24 | 25 | // Sent by Bob 26 | pub struct Message3 { 27 | pub alpha_redeemer_sigs: A, 28 | pub beta_redeem_encsig: B, 29 | } 30 | 31 | // Sent by Alice 32 | pub struct Message4 { 33 | pub alpha_redeem_encsig: A, 34 | } 35 | -------------------------------------------------------------------------------- /src/bitcoin/offer.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub struct Offer { 3 | pub asset: u64, 4 | pub fee: u64, 5 | pub expiry: u32, // absolute timestamp 6 | } 7 | 8 | impl Offer { 9 | // funder pays for fee of redeem/refund tx 10 | pub fn fund_output_amount(&self) -> u64 { 11 | self.asset + self.fee 12 | } 13 | 14 | pub fn change_output_amount(&self, input: u64) -> anyhow::Result { 15 | input 16 | .checked_sub(self.asset + (2 * self.fee)) 17 | .ok_or_else(|| { 18 | anyhow::anyhow!("Bitcoin input amount does not cover fund output amount plus fees") 19 | }) 20 | } 21 | 22 | pub fn redeem_output_amount(&self) -> u64 { 23 | self.asset 24 | } 25 | 26 | pub fn refund_output_amount(&self) -> u64 { 27 | self.asset 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/grin/event.rs: -------------------------------------------------------------------------------- 1 | use crate::grin::{ 2 | compute_excess_pk, compute_offset, public_key_to_pedersen_commitment, PKs, SpecialOutputs, 3 | }; 4 | use secp256k1zkp::pedersen::Commitment; 5 | 6 | pub struct Redeem { 7 | pub excess: Commitment, 8 | } 9 | 10 | impl Redeem { 11 | pub fn new( 12 | special_outputs: &SpecialOutputs, 13 | redeemer_PKs: &PKs, 14 | funder_PKs: &PKs, 15 | ) -> anyhow::Result { 16 | let offset = compute_offset(&funder_PKs.R_redeem, &redeemer_PKs.R_redeem)?; 17 | 18 | let excess_pk = compute_excess_pk( 19 | vec![&redeemer_PKs.X, &funder_PKs.X], 20 | vec![&special_outputs.redeem_output_key], 21 | Some(&offset), 22 | )?; 23 | 24 | Ok(Self { 25 | excess: public_key_to_pedersen_commitment(&excess_pk), 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grin_btc_poc" 3 | version = "0.1.0" 4 | authors = ["CoBloX developers ", "Lloyd Fournier "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | anyhow = "1" 9 | bitcoin = { version = "0.19", git = "https://github.com/jaspervdm/rust-bitcoin", branch = "zkp", features = ["bitcoinconsensus"] } 10 | bitcoin_hashes = "0.7" 11 | blake2 = "0.8" 12 | generic-array = "0.12" 13 | grin_chain = "3" 14 | grin_core = "3" 15 | grin_keychain = "3" 16 | grin_secp256k1zkp = "0.7" 17 | grin_util = "3" 18 | grin_wallet_controller = "3" 19 | grin_wallet_impls = "3" 20 | grin_wallet_libwallet = "3" 21 | grin_wallet_util = "3" 22 | hex = "0.4" 23 | lazy_static = "1.4" 24 | purerust_secp256k1 = { package = "libsecp256k1", version = "0.3" } 25 | rand = "0.7" 26 | sha2 = "0.8" 27 | thiserror = "1" 28 | ureq = { version = "0.11", default-features = false, features = ["json"]} 29 | testcontainers = "0.8" 30 | 31 | [patch.crates-io] 32 | grin_secp256k1zkp = { git = "https://github.com/jaspervdm/rust-secp256k1-zkp", branch = "master" } 33 | -------------------------------------------------------------------------------- /src/grin/keys.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | keypair::{KeyPair, PublicKey}, 3 | Hash, 4 | }; 5 | use blake2::{Blake2b, Digest}; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct SKs { 9 | pub x: KeyPair, 10 | pub r_fund: KeyPair, 11 | pub r_redeem: KeyPair, 12 | pub r_refund: KeyPair, 13 | } 14 | 15 | impl Into for SKs { 16 | fn into(self) -> PKs { 17 | PKs { 18 | X: self.x.public_key, 19 | R_fund: self.r_fund.public_key, 20 | R_redeem: self.r_redeem.public_key, 21 | R_refund: self.r_refund.public_key, 22 | } 23 | } 24 | } 25 | 26 | #[derive(Debug, Clone)] 27 | pub struct PKs { 28 | pub X: PublicKey, 29 | pub R_fund: PublicKey, 30 | pub R_redeem: PublicKey, 31 | pub R_refund: PublicKey, 32 | } 33 | 34 | impl Hash for PKs { 35 | fn hash(&self) -> [u8; 64] { 36 | let mut hasher = Blake2b::new(); 37 | 38 | hasher.input(self.X.0); 39 | hasher.input(self.R_fund.0); 40 | hasher.input(self.R_redeem.0); 41 | hasher.input(self.R_refund.0); 42 | 43 | let mut hash = [0u8; 64]; 44 | hash.copy_from_slice(&hasher.result()); 45 | hash 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/grin/special_outputs.rs: -------------------------------------------------------------------------------- 1 | use crate::{KeyPair, PublicKey}; 2 | 3 | /// Special outputs allow the signing phase to occur without knowledge of the 4 | /// actual wallet outputs of either party. They can be generated locally and 5 | /// will be erased via cut-through even before a transaction involving them is 6 | /// published to the blockchain. 7 | 8 | #[derive(Clone)] 9 | pub struct SpecialOutputs { 10 | pub fund_input_key: PublicKey, 11 | pub redeem_output_key: PublicKey, 12 | pub refund_output_key: PublicKey, 13 | } 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct SpecialOutputKeyPairsFunder { 17 | pub fund_input_key: KeyPair, 18 | pub refund_output_key: KeyPair, 19 | } 20 | 21 | impl SpecialOutputKeyPairsFunder { 22 | pub fn new_random() -> Self { 23 | Self { 24 | fund_input_key: KeyPair::new_random(), 25 | refund_output_key: KeyPair::new_random(), 26 | } 27 | } 28 | } 29 | 30 | #[derive(Debug, Clone)] 31 | pub struct SpecialOutputKeyPairsRedeemer { 32 | pub redeem_output_key: KeyPair, 33 | } 34 | 35 | impl SpecialOutputKeyPairsRedeemer { 36 | pub fn new_random() -> Self { 37 | Self { 38 | redeem_output_key: KeyPair::new_random(), 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/bitcoin/event.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitcoin::{ 3 | transaction::{fund_transaction, redeem_transaction}, 4 | wallet_outputs::WalletOutputs, 5 | Offer, PKs, 6 | }, 7 | keypair::PublicKey, 8 | }; 9 | use ::bitcoin::{ 10 | hashes::{sha256d, Hash}, 11 | util::bip143::SighashComponents, 12 | }; 13 | use secp256k1zkp::Message; 14 | 15 | pub struct Redeem { 16 | // To identify the redeem transaction on Bitcoin 17 | pub txid: sha256d::Hash, 18 | // To extract the correct signature from the witness stack 19 | pub funder_pk: PublicKey, 20 | pub message_hash: Message, 21 | } 22 | 23 | impl Redeem { 24 | pub fn new( 25 | offer: &Offer, 26 | wallet_outputs: &WalletOutputs, 27 | redeemer_PKs: &PKs, 28 | funder_PKs: &PKs, 29 | ) -> anyhow::Result { 30 | let (fund_transaction, fund_output_script) = 31 | fund_transaction(&offer, &wallet_outputs, &redeemer_PKs.X, &funder_PKs.X)?; 32 | let redeem_transaction = 33 | redeem_transaction(&offer, &wallet_outputs, fund_transaction.txid()); 34 | 35 | let redeem_digest = SighashComponents::new(&redeem_transaction).sighash_all( 36 | &redeem_transaction.input[0], 37 | &fund_output_script, 38 | fund_transaction.output[0].value, 39 | ); 40 | let message_hash = Message::from_slice(&redeem_digest.into_inner())?; 41 | 42 | Ok(Self { 43 | txid: redeem_transaction.txid(), 44 | funder_pk: funder_PKs.X, 45 | message_hash, 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/commit.rs: -------------------------------------------------------------------------------- 1 | use crate::PublicKey; 2 | use blake2::{Blake2b, Digest}; 3 | 4 | pub struct Commitment([u8; 64]); 5 | 6 | pub struct Opening { 7 | PKs_alpha: Vec, 8 | PKs_beta: Vec, 9 | Y: PublicKey, 10 | } 11 | 12 | pub type CoinTossingKeys = Vec; 13 | 14 | impl Commitment { 15 | pub fn commit( 16 | PKs_alpha: CoinTossingKeys, 17 | PKs_beta: CoinTossingKeys, 18 | Y: &PublicKey, 19 | ) -> Commitment { 20 | let mut hasher = Blake2b::new(); 21 | 22 | for pk in PKs_alpha.iter() { 23 | hasher.input(pk.0); 24 | } 25 | 26 | for pk in PKs_beta.iter() { 27 | hasher.input(pk.0); 28 | } 29 | 30 | hasher.input(Y.0); 31 | 32 | let mut commitment = [0u8; 64]; 33 | commitment.copy_from_slice(&hasher.result()); 34 | 35 | Commitment(commitment) 36 | } 37 | } 38 | 39 | impl Opening { 40 | pub fn new(PKs_alpha: CoinTossingKeys, PKs_beta: CoinTossingKeys, Y: PublicKey) -> Self { 41 | Opening { 42 | PKs_alpha, 43 | PKs_beta, 44 | Y, 45 | } 46 | } 47 | 48 | pub fn open( 49 | self, 50 | commitment: Commitment, 51 | ) -> anyhow::Result<(CoinTossingKeys, CoinTossingKeys, PublicKey)> { 52 | let self_commitment = 53 | Commitment::commit(self.PKs_alpha.clone(), self.PKs_beta.clone(), &self.Y); 54 | 55 | if commitment.0[..] == self_commitment.0[..] { 56 | Ok((self.PKs_alpha, self.PKs_beta, self.Y)) 57 | } else { 58 | Err(anyhow::anyhow!("Opening does not match commitment")) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/bitcoin/client.rs: -------------------------------------------------------------------------------- 1 | use crate::bitcoin::Transaction; 2 | use bitcoin::{consensus::encode::Encodable, hashes::sha256d, util::psbt::serialize::Deserialize}; 3 | 4 | pub trait Client { 5 | fn node_url(&self) -> String; 6 | fn generate_blocks(&self, n: u32) -> anyhow::Result<()> { 7 | ureq::post(&self.node_url()) 8 | .send_json(ureq::json!({"jsonrpc": "1.0", "method": "generate", "params": [n] })) 9 | .into_json() 10 | .map(|_| ()) 11 | .map_err(|e| anyhow::anyhow!("failed to generate blocks: {}", e)) 12 | } 13 | 14 | fn get_rawtransaction(&self, txid: &sha256d::Hash) -> anyhow::Result { 15 | let res = ureq::post(&Client::node_url(self)) 16 | .send_json(ureq::json!({"jsonrpc": "1.0", "method": "getrawtransaction", "params": [format!("{}", txid), 1] })); 17 | 18 | if res.ok() { 19 | let json = res.into_json()?; 20 | let hex_tx = json 21 | .as_object() 22 | .expect("response is object") 23 | .get("result") 24 | .expect("field exists") 25 | .get("hex") 26 | .expect("field exists") 27 | .as_str() 28 | .expect("value is string"); 29 | 30 | Ok(Transaction::deserialize(&hex::decode(hex_tx)?)?) 31 | } else { 32 | Err(anyhow::anyhow!("failed to get raw transaction")) 33 | } 34 | } 35 | 36 | fn send_rawtransaction(&self, transaction: &Transaction) -> anyhow::Result<()> { 37 | let mut raw_tx = vec![]; 38 | transaction 39 | .consensus_encode(&mut raw_tx) 40 | .expect("valid transaction"); 41 | 42 | let res = ureq::post(&Client::node_url(self)) 43 | .send_json(ureq::json!({"jsonrpc": "1.0", "method": "sendrawtransaction", "params": [hex::encode(raw_tx)] })); 44 | 45 | if res.ok() { 46 | self.generate_blocks(1)?; 47 | 48 | Ok(()) 49 | } else { 50 | Err(anyhow::anyhow!("failed to send raw transaction")) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/bitcoin/alice.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitcoin::{ 3 | action, sign::FunderActions, wallet_outputs::WalletOutputs, EncryptedSignature, Funder0, 4 | Funder1, Offer, PKs, Redeemer0, Redeemer1, Signature, 5 | }, 6 | commit::CoinTossingKeys, 7 | KeyPair, 8 | }; 9 | use std::convert::TryInto; 10 | 11 | #[derive(Clone)] 12 | pub struct AliceFunder0(pub Funder0); 13 | 14 | impl AliceFunder0 { 15 | pub fn new(offer: Offer, wallet_outputs: WalletOutputs) -> Self { 16 | Self(Funder0::new(offer, wallet_outputs)) 17 | } 18 | 19 | pub fn transition(self, PKs_other: PKs) -> AliceFunder1 { 20 | AliceFunder1(self.0.transition(PKs_other)) 21 | } 22 | } 23 | 24 | pub struct AliceFunder1(pub Funder1); 25 | 26 | impl AliceFunder1 { 27 | pub fn transition( 28 | self, 29 | redeemer_refund_sig: Signature, 30 | y: &KeyPair, 31 | ) -> anyhow::Result<(AliceFunder2, EncryptedSignature)> { 32 | let (FunderActions { fund, refund }, redeem_encsig) = 33 | self.0.sign(&y.public_key, redeemer_refund_sig)?; 34 | 35 | Ok(( 36 | AliceFunder2 { 37 | fund_action: fund, 38 | refund_action: refund, 39 | }, 40 | redeem_encsig, 41 | )) 42 | } 43 | } 44 | 45 | pub struct AliceFunder2 { 46 | pub fund_action: action::Fund, 47 | pub refund_action: action::Refund, 48 | } 49 | 50 | #[derive(Clone)] 51 | pub struct AliceRedeemer0(pub Redeemer0); 52 | 53 | impl AliceRedeemer0 { 54 | pub fn new(offer: Offer, wallet_outputs: WalletOutputs) -> Self { 55 | Self(Redeemer0::new(offer, wallet_outputs)) 56 | } 57 | 58 | pub fn transition(self, PKs_other: PKs) -> anyhow::Result<(AliceRedeemer1, Signature)> { 59 | let (state, redeemer_refund_sig) = self.0.transition(PKs_other)?; 60 | 61 | Ok((AliceRedeemer1(state), redeemer_refund_sig)) 62 | } 63 | } 64 | 65 | #[derive(Clone)] 66 | pub struct AliceRedeemer1(pub Redeemer1); 67 | 68 | impl AliceRedeemer1 { 69 | pub fn transition( 70 | self, 71 | redeem_encsig: EncryptedSignature, 72 | y: &KeyPair, 73 | ) -> anyhow::Result { 74 | let encrypted_redeem_action = action::EncryptedRedeem::new( 75 | &self.0.offer, 76 | &self.0.wallet_outputs, 77 | &self.0.SKs_self, 78 | &self.0.PKs_other, 79 | redeem_encsig, 80 | )?; 81 | let redeem_action = encrypted_redeem_action.decrypt(&y); 82 | 83 | Ok(AliceRedeemer2 { redeem_action }) 84 | } 85 | } 86 | 87 | pub struct AliceRedeemer2 { 88 | pub redeem_action: action::Redeem, 89 | } 90 | 91 | impl Into for AliceFunder0 { 92 | fn into(self) -> CoinTossingKeys { 93 | let PKs: PKs = self.0.SKs_self.into(); 94 | vec![PKs.X] 95 | } 96 | } 97 | 98 | impl Into for AliceRedeemer0 { 99 | fn into(self) -> CoinTossingKeys { 100 | let PKs: PKs = self.0.SKs_self.into(); 101 | vec![PKs.X] 102 | } 103 | } 104 | 105 | impl TryInto for CoinTossingKeys { 106 | type Error = anyhow::Error; 107 | fn try_into(self) -> anyhow::Result { 108 | Ok(PKs { X: self[0] }) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/bitcoin/node.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitcoin::{ 3 | wallet::{find_output, FunderWallet, Output, RedeemerWallet}, 4 | Address, Client, OutPoint, 5 | }, 6 | keypair::KeyPair, 7 | }; 8 | use bitcoin_hashes::sha256d; 9 | use std::{ 10 | process::{Child, Command, Stdio}, 11 | str::FromStr, 12 | }; 13 | 14 | pub struct Node { 15 | url: String, 16 | process: Child, 17 | } 18 | 19 | impl Node { 20 | pub fn start() -> anyhow::Result<(Node, Wallets)> { 21 | let process = Command::new("bitcoind") 22 | .args(&[ 23 | "-regtest", 24 | "-server", 25 | "-rpcuser=user", 26 | "-rpcpassword=password", 27 | ]) 28 | .stdout(Stdio::null()) 29 | .stderr(Stdio::null()) 30 | .spawn()?; 31 | std::thread::sleep(std::time::Duration::from_millis(2000)); 32 | 33 | let url = "http://user:password@localhost:18443"; 34 | let node = Node { 35 | process, 36 | url: url.into(), 37 | }; 38 | 39 | node.generate_blocks(100)?; 40 | 41 | let fund_input = node.mint(3)?; 42 | 43 | let funder_wallet = FunderWallet::new(url.into(), fund_input)?; 44 | let redeemer_wallet = RedeemerWallet::new(url.into()); 45 | 46 | Ok((node, Wallets { 47 | funder_wallet, 48 | redeemer_wallet, 49 | })) 50 | } 51 | 52 | pub fn kill(&mut self) -> anyhow::Result<()> { 53 | self.process.kill().map_err(|e| { 54 | anyhow::anyhow!( 55 | "could not kill bitcoind process {}: {}", 56 | self.process.id(), 57 | e 58 | ) 59 | }) 60 | } 61 | 62 | pub fn mint(&self, amount: u8) -> anyhow::Result { 63 | let keypair = KeyPair::new_random(); 64 | 65 | let address = keypair.to_bitcoin_address(); 66 | let txid = self.send_to_address(&address, amount)?; 67 | let transaction = self.get_rawtransaction(&txid)?; 68 | let (vout, txout) = find_output(&transaction, &address).ok_or_else(|| { 69 | anyhow::anyhow!( 70 | "failed to find output for address {} in transaction {:?}", 71 | &address, 72 | &transaction 73 | ) 74 | })?; 75 | 76 | Ok(Output::new(keypair, OutPoint { txid, vout }, txout.clone())) 77 | } 78 | 79 | fn send_to_address(&self, address: &Address, amount: u8) -> anyhow::Result { 80 | let res = ureq::post(&self.url) 81 | .send_json(ureq::json!({"jsonrpc": "1.0", "method": "sendtoaddress", "params": [format!("{}", address), amount] })); 82 | 83 | if res.ok() { 84 | let json = &res.into_json()?; 85 | let string = json["result"].as_str().expect("value is string"); 86 | Ok(sha256d::Hash::from_str(&string).unwrap()) 87 | } else { 88 | Err(anyhow::anyhow!("failed to send to address")) 89 | } 90 | } 91 | } 92 | 93 | impl Client for Node { 94 | fn node_url(&self) -> String { 95 | self.url.clone() 96 | } 97 | } 98 | 99 | pub struct Wallets { 100 | pub funder_wallet: FunderWallet, 101 | pub redeemer_wallet: RedeemerWallet, 102 | } 103 | -------------------------------------------------------------------------------- /src/bitcoin/transaction.rs: -------------------------------------------------------------------------------- 1 | use crate::bitcoin::{Offer, WalletOutputs}; 2 | use ::bitcoin::{ 3 | blockdata::{opcodes, script}, 4 | hashes::sha256d::Hash, 5 | network::constants::Network, 6 | Address, OutPoint, Script, Transaction, TxIn, TxOut, 7 | }; 8 | use secp256k1zkp::key::PublicKey; 9 | 10 | pub fn fund_transaction( 11 | offer: &Offer, 12 | wallet_outputs: &WalletOutputs, 13 | redeemer_key: &PublicKey, 14 | funder_key: &PublicKey, 15 | ) -> anyhow::Result<(Transaction, Script)> { 16 | let fund_output_script = script::Builder::new() 17 | .push_int(2) 18 | .push_key(&::bitcoin::util::key::PublicKey { 19 | key: *redeemer_key, 20 | compressed: true, 21 | }) 22 | .push_key(&::bitcoin::util::key::PublicKey { 23 | key: *funder_key, 24 | compressed: true, 25 | }) 26 | .push_int(2) 27 | .push_opcode(opcodes::all::OP_CHECKMULTISIG) 28 | .into_script(); 29 | 30 | let fund_output_addr = Address::p2wsh(&fund_output_script, Network::Regtest); 31 | let transaction = Transaction { 32 | input: vec![TxIn { 33 | previous_output: wallet_outputs.fund_input.outpoint, 34 | sequence: 0xffff_ffff, 35 | witness: Vec::new(), 36 | script_sig: Script::new(), 37 | }], 38 | output: vec![ 39 | TxOut { 40 | script_pubkey: fund_output_addr.script_pubkey(), 41 | value: offer.fund_output_amount(), 42 | }, 43 | TxOut { 44 | script_pubkey: wallet_outputs.fund_change_address.script_pubkey(), 45 | value: offer.change_output_amount(wallet_outputs.fund_input.txout.value)?, 46 | }, 47 | ], 48 | lock_time: 0, 49 | version: 2, 50 | }; 51 | 52 | Ok((transaction, fund_output_script)) 53 | } 54 | 55 | pub fn refund_transaction( 56 | offer: &Offer, 57 | wallet_outputs: &WalletOutputs, 58 | fund_transaction_id: Hash, 59 | ) -> Transaction { 60 | Transaction { 61 | input: vec![TxIn { 62 | previous_output: OutPoint { 63 | txid: fund_transaction_id, 64 | vout: 0, 65 | }, 66 | sequence: 0xffff_ffff, 67 | witness: Vec::new(), 68 | script_sig: Script::new(), 69 | }], 70 | output: vec![TxOut { 71 | script_pubkey: wallet_outputs.refund_address.script_pubkey(), 72 | value: offer.refund_output_amount(), 73 | }], 74 | lock_time: offer.expiry, 75 | version: 2, 76 | } 77 | } 78 | 79 | pub fn redeem_transaction( 80 | offer: &Offer, 81 | wallet_outputs: &WalletOutputs, 82 | fund_transaction_id: Hash, 83 | ) -> Transaction { 84 | Transaction { 85 | input: vec![TxIn { 86 | previous_output: OutPoint { 87 | txid: fund_transaction_id, 88 | vout: 0, 89 | }, 90 | sequence: 0xffff_ffff, 91 | witness: Vec::new(), 92 | script_sig: Script::new(), 93 | }], 94 | output: vec![TxOut { 95 | script_pubkey: wallet_outputs.redeem_address.script_pubkey(), 96 | value: offer.redeem_output_amount(), 97 | }], 98 | lock_time: 0, 99 | version: 2, 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/bitcoin/bob.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitcoin::{ 3 | action, event, sign::FunderActions, wallet_outputs::WalletOutputs, EncryptedSignature, 4 | Funder0, Funder1, Offer, PKs, PublicKey, Redeemer0, Redeemer1, Signature, 5 | }, 6 | ecdsa::{self, RecoveryKey}, 7 | }; 8 | 9 | #[derive(Clone)] 10 | pub struct BobFunder0(pub Funder0); 11 | 12 | impl BobFunder0 { 13 | pub fn new(offer: Offer, wallet_outputs: WalletOutputs) -> Self { 14 | Self(Funder0::new(offer, wallet_outputs)) 15 | } 16 | 17 | pub fn transition( 18 | self, 19 | PKs_other: PKs, 20 | redeemer_refund_sig: Signature, 21 | Y: &PublicKey, 22 | ) -> anyhow::Result<(BobFunder1, EncryptedSignature)> { 23 | let state = self.0.transition(PKs_other); 24 | 25 | let (FunderActions { fund, refund }, redeem_encsig) = 26 | state.clone().sign(Y, redeemer_refund_sig)?; 27 | let recovery_key = ecdsa::reckey(&Y, &redeem_encsig); 28 | 29 | Ok(( 30 | BobFunder1 { 31 | common: state, 32 | fund_action: fund, 33 | refund_action: refund, 34 | recovery_key, 35 | }, 36 | redeem_encsig, 37 | )) 38 | } 39 | } 40 | 41 | pub struct BobFunder1 { 42 | common: Funder1, 43 | fund_action: action::Fund, 44 | refund_action: action::Refund, 45 | recovery_key: RecoveryKey, 46 | } 47 | 48 | impl BobFunder1 { 49 | pub fn transition(self) -> anyhow::Result { 50 | let redeem_event = event::Redeem::new( 51 | &self.common.offer, 52 | &self.common.wallet_outputs, 53 | &self.common.PKs_other, 54 | &self.common.SKs_self.into(), 55 | )?; 56 | 57 | Ok(BobFunder2 { 58 | fund_action: self.fund_action, 59 | refund_action: self.refund_action, 60 | recovery_key: self.recovery_key, 61 | redeem_event, 62 | }) 63 | } 64 | } 65 | 66 | pub struct BobFunder2 { 67 | pub fund_action: action::Fund, 68 | pub refund_action: action::Refund, 69 | pub recovery_key: RecoveryKey, 70 | pub redeem_event: event::Redeem, 71 | } 72 | 73 | #[derive(Clone)] 74 | pub struct BobRedeemer0(pub Redeemer0); 75 | 76 | impl BobRedeemer0 { 77 | pub fn new(offer: Offer, wallet_outputs: WalletOutputs) -> Self { 78 | Self(Redeemer0::new(offer, wallet_outputs)) 79 | } 80 | 81 | pub fn transition(self, PKs_other: PKs) -> anyhow::Result<(BobRedeemer1, Signature)> { 82 | let (state, redeemer_refund_sig) = self.0.transition(PKs_other)?; 83 | 84 | Ok((BobRedeemer1(state), redeemer_refund_sig)) 85 | } 86 | } 87 | 88 | pub struct BobRedeemer1(pub Redeemer1); 89 | 90 | impl BobRedeemer1 { 91 | pub fn transition(self, redeem_encsig: EncryptedSignature) -> anyhow::Result { 92 | let encrypted_redeem_action = action::EncryptedRedeem::new( 93 | &self.0.offer, 94 | &self.0.wallet_outputs, 95 | &self.0.SKs_self, 96 | &self.0.PKs_other, 97 | redeem_encsig, 98 | )?; 99 | 100 | Ok(BobRedeemer2 { 101 | encrypted_redeem_action, 102 | }) 103 | } 104 | } 105 | 106 | pub struct BobRedeemer2 { 107 | pub encrypted_redeem_action: action::EncryptedRedeem, 108 | } 109 | 110 | impl Into for BobFunder0 { 111 | fn into(self) -> PKs { 112 | self.0.SKs_self.into() 113 | } 114 | } 115 | 116 | impl Into for BobRedeemer0 { 117 | fn into(self) -> PKs { 118 | self.0.SKs_self.into() 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/dleq.rs: -------------------------------------------------------------------------------- 1 | use crate::keypair::{KeyPair, PublicKey, SecretKey, SECP}; 2 | use sha2::{Digest, Sha256}; 3 | 4 | #[derive(Debug)] 5 | pub struct Proof { 6 | s: SecretKey, 7 | c: SecretKey, 8 | } 9 | 10 | pub fn prove(G: &PublicKey, Gx: &PublicKey, H: &PublicKey, Hx: &PublicKey, x: &SecretKey) -> Proof { 11 | // NOTE: using thread_rng for PoC and even early stage production but there 12 | // are more robust ways of doing this which include hashing secret 13 | // information along with randomness (see https://github.com/bitcoin/bips/pull/893/). 14 | let r = KeyPair::new_random(); 15 | 16 | // Gr 17 | let mut Gr = *G; 18 | Gr.mul_assign(&*SECP, &r.secret_key).unwrap(); 19 | 20 | // Hr 21 | let mut Hr = *H; 22 | Hr.mul_assign(&*SECP, &r.secret_key).unwrap(); 23 | 24 | // c = H(G | Gx | H | Hx | Gr | Hr) 25 | let mut hasher = Sha256::default(); 26 | hasher.input(&G.serialize_vec(&*SECP, true)); 27 | hasher.input(&Gx.serialize_vec(&*SECP, true)); 28 | hasher.input(&H.serialize_vec(&*SECP, true)); 29 | hasher.input(&Hx.serialize_vec(&*SECP, true)); 30 | hasher.input(&Gr.serialize_vec(&*SECP, true)); 31 | hasher.input(&Hr.serialize_vec(&*SECP, true)); 32 | let c = SecretKey::from_slice(&*SECP, &hasher.result()[..]).unwrap(); 33 | 34 | // s = r + cx 35 | let mut s = c.clone(); 36 | s.mul_assign(&*SECP, &x).unwrap(); 37 | s.add_assign(&*SECP, &r.secret_key).unwrap(); 38 | 39 | Proof { s, c } 40 | } 41 | 42 | pub fn verify( 43 | G: &PublicKey, 44 | Gx: &PublicKey, 45 | H: &PublicKey, 46 | Hx: &PublicKey, 47 | proof: &Proof, // (s = r + cx, c) 48 | ) -> bool { 49 | let mut c_neg = proof.c.clone(); 50 | c_neg.neg_assign(&*SECP).unwrap(); 51 | 52 | // Gr = Gs + (Gx * -c) = Gr + Gcx - Gcx 53 | let Gr = { 54 | let mut Gxc_neg = *Gx; 55 | // TODO: Don't panic on things controlled by adversary 56 | Gxc_neg.mul_assign(&*SECP, &c_neg).unwrap(); 57 | 58 | let mut Gs = *G; 59 | Gs.mul_assign(&*SECP, &proof.s).unwrap(); 60 | PublicKey::from_combination(&*SECP, vec![&Gxc_neg, &Gs]).unwrap() 61 | }; 62 | 63 | // Hr = Hs + (Hx * -c) = Hr + Hcx - Hcx 64 | let Hr = { 65 | let mut Hxc_neg = *Hx; 66 | Hxc_neg.mul_assign(&*SECP, &c_neg).unwrap(); 67 | 68 | let mut Hs = *H; 69 | Hs.mul_assign(&*SECP, &proof.s).unwrap(); 70 | PublicKey::from_combination(&*SECP, vec![&Hxc_neg, &Hs]).unwrap() 71 | }; 72 | 73 | // c = H(G | Gx | H | Hx | Gr | Hr) 74 | let mut hasher = Sha256::default(); 75 | hasher.input(&G.serialize_vec(&*SECP, true)); 76 | hasher.input(&Gx.serialize_vec(&*SECP, true)); 77 | hasher.input(&H.serialize_vec(&*SECP, true)); 78 | hasher.input(&Hx.serialize_vec(&*SECP, true)); 79 | hasher.input(&Gr.serialize_vec(&*SECP, true)); 80 | hasher.input(&Hr.serialize_vec(&*SECP, true)); 81 | let c = SecretKey::from_slice(&*SECP, &hasher.result()[..]).unwrap(); 82 | 83 | // c == c' 84 | proof.c == c 85 | } 86 | 87 | #[cfg(test)] 88 | mod test { 89 | use super::*; 90 | use crate::keypair::{random_secret_key, G}; 91 | 92 | #[test] 93 | fn prove_and_verify() { 94 | let x = random_secret_key(); 95 | let mut Gx = *G; 96 | Gx.mul_assign(&*SECP, &x).unwrap(); 97 | 98 | let mut H = *G; 99 | H.mul_assign(&*SECP, &random_secret_key()).unwrap(); 100 | 101 | let mut Hx = H; 102 | Hx.mul_assign(&*SECP, &x).unwrap(); 103 | 104 | let proof = crate::dleq::prove(&*G, &Gx, &H, &Hx, &x); 105 | 106 | assert!(crate::dleq::verify(&*G, &Gx, &H, &Hx, &proof)) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /tests/schnorr_ecdsa.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use grin_btc_poc::{ 4 | ecdsa, 5 | keypair::{KeyPair, PublicKey, SECP}, 6 | schnorr, 7 | }; 8 | use secp256k1zkp::{aggsig, Message}; 9 | use std::convert::TryFrom; 10 | 11 | #[test] 12 | fn recover_from_ecdsa_to_decrypt_schnorr() -> anyhow::Result<()> { 13 | let x0 = KeyPair::new_random(); 14 | let x1 = KeyPair::new_random(); 15 | let r0 = KeyPair::new_random(); 16 | let r1 = KeyPair::new_random(); 17 | 18 | let y = KeyPair::new_random(); 19 | 20 | let (r0, r1, y) = schnorr::normalize_keypairs(r0, r1, y)?; 21 | 22 | let message = b"tttttttttttttttttttttttttttttttt"; 23 | 24 | let schnorr_partial_encsig = schnorr::encsign_2p_0( 25 | &x0, 26 | &r0, 27 | &x1.public_key, 28 | &r1.public_key, 29 | &y.public_key, 30 | &Message::from_slice(message)?, 31 | )?; 32 | 33 | let schnorr_encsig = schnorr::encsign_2p_1( 34 | &x1, 35 | &r1, 36 | &x0.public_key, 37 | &r0.public_key, 38 | &y.public_key, 39 | &Message::from_slice(message)?, 40 | &schnorr_partial_encsig, 41 | )?; 42 | 43 | let x = KeyPair::new_random(); 44 | 45 | let ecdsa_encsig = ecdsa::encsign(&x, &y.public_key, message); 46 | 47 | let ecdsa_sig = ecdsa::decsig(&y, &ecdsa_encsig); 48 | 49 | let rec_key = ecdsa::reckey(&y.public_key, &ecdsa_encsig); 50 | let y_tag = ecdsa::recover(&ecdsa_sig, &rec_key).unwrap(); 51 | 52 | let R_hat = PublicKey::from_combination(&*SECP, vec![&r0.public_key, &r1.public_key])?; 53 | let schnorr_sig = schnorr::decsig(&y_tag, &schnorr_encsig, &R_hat)?; 54 | 55 | let X = PublicKey::from_combination(&*SECP, vec![&x0.public_key, &x1.public_key])?; 56 | assert!(aggsig::verify_single( 57 | &*SECP, 58 | &schnorr_sig, 59 | &Message::from_slice(message).unwrap(), 60 | None, 61 | &X, 62 | Some(&X), 63 | None, 64 | false 65 | )); 66 | Ok(()) 67 | } 68 | 69 | #[test] 70 | fn recover_from_schnorr_to_decrypt_ecdsa() -> anyhow::Result<()> { 71 | let x0 = KeyPair::new_random(); 72 | let x1 = KeyPair::new_random(); 73 | let r0 = KeyPair::new_random(); 74 | let r1 = KeyPair::new_random(); 75 | 76 | let y = KeyPair::new_random(); 77 | 78 | let (r0, r1, y) = schnorr::normalize_keypairs(r0, r1, y)?; 79 | 80 | let message = b"tttttttttttttttttttttttttttttttt"; 81 | 82 | let schnorr_partial_encsig = schnorr::encsign_2p_0( 83 | &x0, 84 | &r0, 85 | &x1.public_key, 86 | &r1.public_key, 87 | &y.public_key, 88 | &Message::from_slice(message)?, 89 | )?; 90 | 91 | let schnorr_encsig = schnorr::encsign_2p_1( 92 | &x1, 93 | &r1, 94 | &x0.public_key, 95 | &r0.public_key, 96 | &y.public_key, 97 | &Message::from_slice(message)?, 98 | &schnorr_partial_encsig, 99 | )?; 100 | 101 | let x = KeyPair::new_random(); 102 | 103 | let ecdsa_encsig = ecdsa::encsign(&x, &y.public_key, message); 104 | 105 | let R_hat = PublicKey::from_combination(&*SECP, vec![&r0.public_key, &r1.public_key]).unwrap(); 106 | let schnorr_sig = schnorr::decsig(&y, &schnorr_encsig, &R_hat)?; 107 | 108 | let rec_key = schnorr::RecoveryKey::try_from(schnorr_encsig)?; 109 | let y_tag = schnorr::recover(&schnorr_sig, &rec_key)?; 110 | 111 | let ecdsa_sig = ecdsa::decsig(&y_tag, &ecdsa_encsig); 112 | 113 | assert!(SECP 114 | .verify( 115 | &Message::from_slice(message).unwrap(), 116 | &ecdsa_sig.into(), 117 | &x.public_key 118 | ) 119 | .is_ok()); 120 | Ok(()) 121 | } 122 | -------------------------------------------------------------------------------- /src/bitcoin/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::bitcoin::sign::FunderActions; 2 | 3 | pub mod action; 4 | pub mod alice; 5 | pub mod bob; 6 | pub mod client; 7 | pub mod event; 8 | pub mod keygen; 9 | pub mod keys; 10 | pub mod node; 11 | pub mod offer; 12 | pub mod sign; 13 | pub mod transaction; 14 | pub mod wallet; 15 | pub mod wallet_outputs; 16 | 17 | pub use crate::{ 18 | bitcoin::{ 19 | alice::*, 20 | bob::*, 21 | keygen::keygen, 22 | keys::{PKs, SKs}, 23 | offer::Offer, 24 | wallet_outputs::WalletOutputs, 25 | }, 26 | ecdsa::EncryptedSignature, 27 | PublicKey, 28 | }; 29 | pub use ::bitcoin::{ 30 | blockdata::transaction::TxOut, network::constants::Network, 31 | util::key::PublicKey as BitcoinPublicKey, Address, OutPoint, Script, Transaction, 32 | }; 33 | pub use client::Client; 34 | pub use node::{Node, Wallets}; 35 | pub use secp256k1zkp::Signature; 36 | 37 | #[derive(Clone)] 38 | pub struct Funder0 { 39 | pub offer: Offer, 40 | pub wallet_outputs: WalletOutputs, 41 | pub SKs_self: SKs, 42 | } 43 | 44 | impl Funder0 { 45 | pub fn new(offer: Offer, wallet_outputs: WalletOutputs) -> Self { 46 | let SKs_self = keygen(); 47 | 48 | Self { 49 | offer, 50 | wallet_outputs, 51 | SKs_self, 52 | } 53 | } 54 | 55 | pub fn transition(self, PKs_other: PKs) -> Funder1 { 56 | Funder1 { 57 | offer: self.offer, 58 | wallet_outputs: self.wallet_outputs, 59 | SKs_self: self.SKs_self, 60 | PKs_other, 61 | } 62 | } 63 | } 64 | 65 | #[derive(Clone)] 66 | pub struct Funder1 { 67 | pub offer: Offer, 68 | pub wallet_outputs: WalletOutputs, 69 | pub SKs_self: SKs, 70 | pub PKs_other: PKs, 71 | } 72 | 73 | impl Funder1 { 74 | pub fn sign( 75 | self, 76 | Y: &PublicKey, 77 | redeemer_refund_sig: Signature, 78 | ) -> anyhow::Result<(FunderActions, EncryptedSignature)> { 79 | let (funder_actions, redeem_encsig) = sign::funder( 80 | &self.offer, 81 | &self.wallet_outputs, 82 | &self.SKs_self, 83 | &self.PKs_other, 84 | &Y, 85 | &redeemer_refund_sig, 86 | )?; 87 | 88 | Ok((funder_actions, redeem_encsig)) 89 | } 90 | } 91 | 92 | pub struct Funder2 { 93 | pub fund_action: action::Fund, 94 | pub refund_action: action::Refund, 95 | } 96 | 97 | #[derive(Clone)] 98 | pub struct Redeemer0 { 99 | pub offer: Offer, 100 | pub wallet_outputs: WalletOutputs, 101 | pub SKs_self: SKs, 102 | } 103 | 104 | impl Redeemer0 { 105 | pub fn new(offer: Offer, wallet_outputs: WalletOutputs) -> Self { 106 | let SKs_self = keygen(); 107 | 108 | Self { 109 | offer, 110 | wallet_outputs, 111 | SKs_self, 112 | } 113 | } 114 | 115 | pub fn transition(self, PKs_other: PKs) -> anyhow::Result<(Redeemer1, Signature)> { 116 | let redeemer_refund_sig = sign::redeemer( 117 | &self.offer, 118 | &self.wallet_outputs, 119 | &self.SKs_self, 120 | &PKs_other, 121 | )?; 122 | 123 | let state = Redeemer1 { 124 | offer: self.offer, 125 | wallet_outputs: self.wallet_outputs, 126 | SKs_self: self.SKs_self, 127 | PKs_other, 128 | }; 129 | 130 | Ok((state, redeemer_refund_sig)) 131 | } 132 | } 133 | 134 | #[derive(Clone)] 135 | pub struct Redeemer1 { 136 | pub offer: Offer, 137 | pub wallet_outputs: WalletOutputs, 138 | pub SKs_self: SKs, 139 | pub PKs_other: PKs, 140 | } 141 | 142 | pub struct Redeemer2 { 143 | pub encrypted_redeem_action: action::EncryptedRedeem, 144 | } 145 | -------------------------------------------------------------------------------- /src/bitcoin/sign.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitcoin::{ 3 | action, 4 | transaction::{fund_transaction, redeem_transaction, refund_transaction}, 5 | Offer, PKs, SKs, WalletOutputs, 6 | }, 7 | ecdsa, 8 | keypair::{self, PublicKey}, 9 | }; 10 | use ::bitcoin::{hashes::Hash, util::bip143::SighashComponents}; 11 | use secp256k1zkp::{self, Message}; 12 | 13 | // TODO: Remove Y from spec version 14 | // TODO: Remove redeem signature from output in spec 15 | pub fn redeemer( 16 | offer: &Offer, 17 | wallet_outputs: &WalletOutputs, 18 | redeemer_SKs: &SKs, 19 | funder_PKs: &PKs, 20 | ) -> anyhow::Result { 21 | let (fund_transaction, fund_output_script) = fund_transaction( 22 | &offer, 23 | &wallet_outputs, 24 | &redeemer_SKs.x.public_key, 25 | &funder_PKs.X, 26 | )?; 27 | 28 | let refund_transaction = refund_transaction(&offer, &wallet_outputs, fund_transaction.txid()); 29 | 30 | let refund_digest = SighashComponents::new(&refund_transaction).sighash_all( 31 | &refund_transaction.input[0], 32 | &fund_output_script, 33 | fund_transaction.output[0].value, 34 | ); 35 | let refund_digest = Message::from_slice(&refund_digest.into_inner()) 36 | .expect("should not fail because it is a hash"); 37 | 38 | Ok(redeemer_SKs.x.sign_ecdsa(&refund_digest)) 39 | } 40 | 41 | pub struct FunderActions { 42 | pub fund: action::Fund, 43 | pub refund: action::Refund, 44 | } 45 | 46 | // TODO: Modify the spec to not pass redeemer's redeem signature to funder 47 | pub fn funder( 48 | offer: &Offer, 49 | wallet_outputs: &WalletOutputs, 50 | funder_SKs: &SKs, 51 | redeemer_PKs: &PKs, 52 | Y: &PublicKey, 53 | redeemer_refund_signature: &secp256k1zkp::Signature, 54 | ) -> anyhow::Result<(FunderActions, ecdsa::EncryptedSignature)> { 55 | let (fund_transaction, fund_output_script) = fund_transaction( 56 | &offer, 57 | &wallet_outputs, 58 | &redeemer_PKs.X, 59 | &funder_SKs.x.public_key, 60 | )?; 61 | 62 | let fund = action::Fund { 63 | transaction: fund_transaction.clone(), 64 | }; 65 | 66 | let refund = { 67 | let refund_transaction = 68 | refund_transaction(&offer, &wallet_outputs, fund_transaction.txid()); 69 | 70 | let refund_digest = SighashComponents::new(&refund_transaction).sighash_all( 71 | &refund_transaction.input[0], 72 | &fund_output_script, 73 | fund_transaction.output[0].value, 74 | ); 75 | let refund_digest = Message::from_slice(&refund_digest.into_inner()) 76 | .expect("Should not fail because it is a hash"); 77 | 78 | if !keypair::verify_ecdsa(&refund_digest, &redeemer_refund_signature, &redeemer_PKs.X) { 79 | return Err(anyhow::anyhow!( 80 | "failed to verify redeemer's Bitcoin refund signature" 81 | )); 82 | } 83 | 84 | let funder_refund_signature = funder_SKs.x.sign_ecdsa(&refund_digest); 85 | 86 | action::Refund::new( 87 | refund_transaction, 88 | *redeemer_refund_signature, 89 | funder_refund_signature, 90 | fund_output_script.clone(), 91 | ) 92 | }; 93 | 94 | let encrypted_redeem_signature = { 95 | let redeem_transaction = 96 | redeem_transaction(&offer, &wallet_outputs, fund_transaction.txid()); 97 | let redeem_digest = SighashComponents::new(&redeem_transaction).sighash_all( 98 | &redeem_transaction.input[0], 99 | &fund_output_script, 100 | fund_transaction.output[0].value, 101 | ); 102 | 103 | ecdsa::encsign(&funder_SKs.x, &Y, &redeem_digest) 104 | }; 105 | 106 | Ok((FunderActions { fund, refund }, encrypted_redeem_signature)) 107 | } 108 | -------------------------------------------------------------------------------- /src/bin/refund.rs: -------------------------------------------------------------------------------- 1 | use grin_btc_poc::{alice::Alice0, bitcoin, bob::Bob0, grin, Execute}; 2 | 3 | fn main() -> anyhow::Result<()> { 4 | // Set up Bitcoin wallets 5 | let ( 6 | mut bitcoin_node, 7 | bitcoin::Wallets { 8 | funder_wallet: alice_alpha_wallet, 9 | redeemer_wallet: bob_alpha_wallet, 10 | }, 11 | ) = bitcoin::Node::start()?; 12 | 13 | // Set up Grin wallets 14 | let ( 15 | grin_node, 16 | grin::Wallets { 17 | funder_wallet: bob_beta_wallet, 18 | .. 19 | }, 20 | ) = grin::Node::start()?; 21 | 22 | let bob_beta_starting_balance = bob_beta_wallet.get_balance()?; 23 | 24 | // Base parameters of the swap, including the offer negotiated prior to 25 | // executing this protocol, and a set of outputs per party to know where the 26 | // assets come from and go to during the execution phase of the protocol 27 | 28 | let offer_bitcoin = bitcoin::Offer { 29 | asset: 100_000_000, 30 | fee: 1_000, 31 | expiry: 0, 32 | }; 33 | let outputs_bitcoin = bitcoin::WalletOutputs { 34 | fund_input: alice_alpha_wallet.fund_input(), 35 | fund_change_address: alice_alpha_wallet.change_output_address(), 36 | redeem_address: bob_alpha_wallet.redeem_output_address(), 37 | refund_address: alice_alpha_wallet.refund_output_address(), 38 | }; 39 | 40 | let offer_grin = grin::Offer { 41 | asset: 10_000_000_000, 42 | fee: 5_000_000, 43 | expiry: 0, 44 | }; 45 | let output_keypairs_grin_funder = grin::SpecialOutputKeyPairsFunder::new_random(); 46 | let output_keypairs_grin_redeemer = grin::SpecialOutputKeyPairsRedeemer::new_random(); 47 | let outputs_grin = grin::SpecialOutputs { 48 | fund_input_key: output_keypairs_grin_funder.fund_input_key.public_key, 49 | redeem_output_key: output_keypairs_grin_redeemer.redeem_output_key.public_key, 50 | refund_output_key: output_keypairs_grin_funder.refund_output_key.public_key, 51 | }; 52 | 53 | // Key generation and signing 54 | 55 | let (alice0, message0) = Alice0::::new( 56 | offer_bitcoin.clone(), 57 | outputs_bitcoin.clone(), 58 | offer_grin.clone(), 59 | outputs_grin.clone(), 60 | output_keypairs_grin_redeemer, 61 | )?; 62 | 63 | let (bob0, message1) = Bob0::::new( 64 | offer_bitcoin.clone(), 65 | outputs_bitcoin, 66 | offer_grin, 67 | outputs_grin, 68 | output_keypairs_grin_funder, 69 | message0, 70 | )?; 71 | 72 | let (alice1, message2) = alice0.receive(message1)?; 73 | 74 | let (bob1, message3) = bob0.receive(message2)?; 75 | 76 | let (alice2, message4) = alice1.receive(message3)?; 77 | 78 | let bob2 = bob1.receive(message4)?; 79 | 80 | // Execution 81 | 82 | alice2 83 | .alpha_state 84 | .fund_action 85 | .execute(&alice_alpha_wallet)?; 86 | 87 | let fund_fee = bob2.beta_state.fund_action.execute(&bob_beta_wallet)?; 88 | 89 | alice2 90 | .alpha_state 91 | .refund_action 92 | .clone() 93 | .execute(&alice_alpha_wallet)?; 94 | 95 | let refund_fee = bob2.beta_state.refund_action.execute(&bob_beta_wallet)?; 96 | 97 | // Verify that alice gets her bitcoin back 98 | assert!(alice_alpha_wallet.verify_payment_to_address( 99 | alice2.alpha_state.refund_action.transaction.txid(), 100 | offer_bitcoin.asset 101 | )?); 102 | 103 | // Verify that bob has the same amount of grin as before (minus fees) 104 | assert_eq!( 105 | bob_beta_wallet.get_balance()?, 106 | bob_beta_starting_balance - fund_fee - refund_fee 107 | ); 108 | 109 | // Clean-up 110 | 111 | bitcoin_node.kill()?; 112 | grin_node.kill(); 113 | Ok(()) 114 | } 115 | -------------------------------------------------------------------------------- /src/keypair.rs: -------------------------------------------------------------------------------- 1 | use crate::bitcoin::{Address, BitcoinPublicKey, Network}; 2 | use rand::Rng; 3 | use secp256k1zkp::{ContextFlag, Message, Secp256k1, Signature}; 4 | 5 | pub use secp256k1zkp::key::{PublicKey, SecretKey, ZERO_KEY}; 6 | 7 | lazy_static::lazy_static! { 8 | pub static ref SECP: Secp256k1 = Secp256k1::with_caps(ContextFlag::Commit); 9 | pub static ref G: PublicKey = { 10 | let mut vec = vec![4u8]; 11 | vec.extend(&secp256k1zkp::constants::GENERATOR_G[..]); 12 | PublicKey::from_slice(&*SECP, &vec).unwrap() 13 | }; 14 | pub static ref H: PublicKey = { 15 | let mut vec = vec![4u8]; 16 | vec.extend(&secp256k1zkp::constants::GENERATOR_H[..]); 17 | PublicKey::from_slice(&*SECP, &vec).unwrap() 18 | }; 19 | pub static ref MINUS_ONE: SecretKey = { 20 | let mut one = secp256k1zkp::key::ONE_KEY; 21 | one.neg_assign(&*SECP).unwrap(); 22 | one 23 | }; 24 | } 25 | 26 | #[derive(Debug, Clone)] 27 | pub struct KeyPair { 28 | pub secret_key: SecretKey, 29 | pub public_key: PublicKey, 30 | } 31 | 32 | impl KeyPair { 33 | pub fn new(secret_key: SecretKey) -> Self { 34 | let public_key = PublicKey::from_secret_key(&*SECP, &secret_key) 35 | .expect("could not derive public key from secret key"); 36 | 37 | KeyPair { 38 | secret_key, 39 | public_key, 40 | } 41 | } 42 | 43 | pub fn from_slice(data: &[u8; 32]) -> KeyPair { 44 | let secret_key = 45 | SecretKey::from_slice(&*SECP, data).expect("could not derive secret key from slice"); 46 | 47 | KeyPair::new(secret_key) 48 | } 49 | 50 | pub fn new_random() -> Self { 51 | KeyPair::new(random_secret_key()) 52 | } 53 | 54 | pub fn sign_ecdsa(&self, message: &Message) -> Signature { 55 | SECP.sign(message, &self.secret_key).expect("cannot fail") 56 | } 57 | 58 | pub fn to_bitcoin_address(&self) -> Address { 59 | Address::p2wpkh( 60 | &BitcoinPublicKey { 61 | key: self.public_key, 62 | compressed: true, 63 | }, 64 | Network::Regtest, 65 | ) 66 | } 67 | } 68 | 69 | pub fn verify_ecdsa(msg: &Message, sig: &Signature, pk: &PublicKey) -> bool { 70 | SECP.verify(msg, sig, pk).is_ok() 71 | } 72 | 73 | pub trait XCoor { 74 | fn x_coor(&self) -> [u8; 32]; 75 | } 76 | 77 | impl XCoor for PublicKey { 78 | fn x_coor(&self) -> [u8; 32] { 79 | let serialized_pk = self.serialize_vec(&*SECP, true); 80 | 81 | let mut x_coor = [0u8; 32]; 82 | // there's a random byte at the front of the uncompressed serialized pk 83 | x_coor.copy_from_slice(&serialized_pk[1..33]); 84 | x_coor 85 | } 86 | } 87 | 88 | pub trait YCoor { 89 | fn y_coor(&self) -> [u8; 32]; 90 | } 91 | 92 | impl YCoor for PublicKey { 93 | fn y_coor(&self) -> [u8; 32] { 94 | let serialized_pk = self.serialize_vec(&*SECP, false); 95 | 96 | let mut y_coor = [0u8; 32]; 97 | y_coor.copy_from_slice(&serialized_pk[33..65]); 98 | y_coor 99 | } 100 | } 101 | 102 | pub trait Negate { 103 | fn negate(&self) -> Self; 104 | } 105 | 106 | impl Negate for PublicKey { 107 | fn negate(&self) -> Self { 108 | let mut negated = *self; 109 | negated.mul_assign(&*SECP, &*MINUS_ONE).unwrap(); 110 | negated 111 | } 112 | } 113 | 114 | impl Negate for SecretKey { 115 | fn negate(&self) -> Self { 116 | let mut negated = self.clone(); 117 | negated.neg_assign(&*SECP).unwrap(); 118 | negated 119 | } 120 | } 121 | 122 | impl Negate for KeyPair { 123 | fn negate(&self) -> Self { 124 | KeyPair { 125 | secret_key: self.secret_key.negate(), 126 | public_key: self.public_key.negate(), 127 | } 128 | } 129 | } 130 | 131 | pub fn random_secret_key() -> SecretKey { 132 | SecretKey::from_slice(&*SECP, &rand::thread_rng().gen::<[u8; 32]>()).unwrap() 133 | } 134 | -------------------------------------------------------------------------------- /src/bin/grin_bitcoin_happy.rs: -------------------------------------------------------------------------------- 1 | use grin_btc_poc::{alice::Alice0, bitcoin, bob::Bob0, ecdsa, grin, Execute, LookFor}; 2 | 3 | fn main() -> anyhow::Result<()> { 4 | // Set up Grin wallets 5 | let ( 6 | grin_node, 7 | grin::Wallets { 8 | funder_wallet: alice_alpha_wallet, 9 | redeemer_wallet: bob_alpha_wallet, 10 | }, 11 | ) = grin::Node::start()?; 12 | 13 | let bob_alpha_starting_balance = bob_alpha_wallet.get_balance()?; 14 | 15 | // Set up Bitcoin wallets 16 | let ( 17 | mut bitcoin_node, 18 | bitcoin::Wallets { 19 | funder_wallet: bob_beta_wallet, 20 | redeemer_wallet: alice_beta_wallet, 21 | }, 22 | ) = bitcoin::Node::start()?; 23 | 24 | // Base parameters of the swap, including the offer negotiated prior to 25 | // executing this protocol, and a set of outputs per party to know where the 26 | // assets come from and go to during the execution phase of the protocol 27 | 28 | let offer_grin = grin::Offer { 29 | asset: 10_000_000_000, 30 | fee: 5_000_000, 31 | expiry: 0, 32 | }; 33 | let output_keypairs_grin_funder = grin::SpecialOutputKeyPairsFunder::new_random(); 34 | let output_keypairs_grin_redeemer = grin::SpecialOutputKeyPairsRedeemer::new_random(); 35 | let outputs_grin = grin::SpecialOutputs { 36 | fund_input_key: output_keypairs_grin_funder.fund_input_key.public_key, 37 | redeem_output_key: output_keypairs_grin_redeemer.redeem_output_key.public_key, 38 | refund_output_key: output_keypairs_grin_funder.refund_output_key.public_key, 39 | }; 40 | 41 | let offer_bitcoin = bitcoin::Offer { 42 | asset: 100_000_000, 43 | fee: 1_000, 44 | expiry: 0, 45 | }; 46 | let outputs_bitcoin = bitcoin::WalletOutputs { 47 | fund_input: bob_beta_wallet.fund_input(), 48 | fund_change_address: bob_beta_wallet.change_output_address(), 49 | redeem_address: alice_beta_wallet.redeem_output_address(), 50 | refund_address: bob_beta_wallet.refund_output_address(), 51 | }; 52 | 53 | // Key generation and signing 54 | 55 | let (alice0, message0) = Alice0::::new( 56 | offer_grin.clone(), 57 | outputs_grin.clone(), 58 | output_keypairs_grin_funder, 59 | offer_bitcoin.clone(), 60 | outputs_bitcoin.clone(), 61 | )?; 62 | 63 | let (bob0, message1) = Bob0::::new( 64 | offer_grin.clone(), 65 | outputs_grin, 66 | output_keypairs_grin_redeemer, 67 | offer_bitcoin.clone(), 68 | outputs_bitcoin, 69 | message0, 70 | )?; 71 | 72 | let (alice1, message2) = alice0.receive(message1)?; 73 | 74 | let (bob1, message3) = bob0.receive(message2)?; 75 | 76 | let (alice2, message4) = alice1.receive(message3)?; 77 | 78 | let bob2 = bob1.receive(message4)?; 79 | 80 | // Execution 81 | 82 | alice2 83 | .alpha_state 84 | .0 85 | .fund_action 86 | .execute(&alice_alpha_wallet)?; 87 | 88 | bob2.beta_state.fund_action.execute(&bob_beta_wallet)?; 89 | 90 | alice2 91 | .beta_state 92 | .redeem_action 93 | .clone() 94 | .execute(&alice_beta_wallet)?; 95 | 96 | let beta_decrypted_redeem_sig = bob_beta_wallet.look_for(bob2.beta_state.redeem_event)?; 97 | let y = ecdsa::recover(&beta_decrypted_redeem_sig, &bob2.beta_state.recovery_key)?; 98 | let alpha_redeem_action = bob2.alpha_state.encrypted_redeem_action.decrypt(&y)?; 99 | 100 | alpha_redeem_action.execute(&bob_alpha_wallet)?; 101 | 102 | // Verify that alice gets the agreed upon bitcoin 103 | assert!(alice_beta_wallet.verify_payment_to_address( 104 | alice2.beta_state.redeem_action.transaction.txid(), 105 | offer_bitcoin.asset 106 | )?); 107 | 108 | // Verify that bob gets the agreed upon grin 109 | assert_eq!( 110 | bob_alpha_wallet.get_balance()?, 111 | bob_alpha_starting_balance + offer_grin.asset 112 | ); 113 | 114 | // Clean-up 115 | 116 | grin_node.kill(); 117 | bitcoin_node.kill()?; 118 | Ok(()) 119 | } 120 | -------------------------------------------------------------------------------- /src/bin/bitcoin_grin_happy.rs: -------------------------------------------------------------------------------- 1 | use grin_btc_poc::{alice::Alice0, bitcoin, bob::Bob0, grin, schnorr, Execute, LookFor}; 2 | 3 | fn main() -> anyhow::Result<()> { 4 | // Set up Bitcoin wallets 5 | let ( 6 | mut bitcoin_node, 7 | bitcoin::Wallets { 8 | funder_wallet: alice_alpha_wallet, 9 | redeemer_wallet: bob_alpha_wallet, 10 | }, 11 | ) = bitcoin::Node::start()?; 12 | 13 | // Set up Grin wallets 14 | let ( 15 | grin_node, 16 | grin::Wallets { 17 | funder_wallet: bob_beta_wallet, 18 | redeemer_wallet: alice_beta_wallet, 19 | }, 20 | ) = grin::Node::start()?; 21 | 22 | let alice_beta_starting_balance = alice_beta_wallet.get_balance()?; 23 | 24 | // Base parameters of the swap, including the offer negotiated prior to 25 | // executing this protocol, and a set of outputs per party to know where the 26 | // assets come from and go to during the execution phase of the protocol 27 | 28 | let offer_bitcoin = bitcoin::Offer { 29 | asset: 100_000_000, 30 | fee: 1_000, 31 | expiry: 0, 32 | }; 33 | let outputs_bitcoin = bitcoin::WalletOutputs { 34 | fund_input: alice_alpha_wallet.fund_input(), 35 | fund_change_address: alice_alpha_wallet.change_output_address(), 36 | redeem_address: bob_alpha_wallet.redeem_output_address(), 37 | refund_address: alice_alpha_wallet.refund_output_address(), 38 | }; 39 | 40 | let offer_grin = grin::Offer { 41 | asset: 10_000_000_000, 42 | fee: 5_000_000, 43 | expiry: 0, 44 | }; 45 | let output_keypairs_grin_funder = grin::SpecialOutputKeyPairsFunder::new_random(); 46 | let output_keypairs_grin_redeemer = grin::SpecialOutputKeyPairsRedeemer::new_random(); 47 | let outputs_grin = grin::SpecialOutputs { 48 | fund_input_key: output_keypairs_grin_funder.fund_input_key.public_key, 49 | redeem_output_key: output_keypairs_grin_redeemer.redeem_output_key.public_key, 50 | refund_output_key: output_keypairs_grin_funder.refund_output_key.public_key, 51 | }; 52 | 53 | // Key generation and signing 54 | 55 | let (alice0, message0) = Alice0::::new( 56 | offer_bitcoin.clone(), 57 | outputs_bitcoin.clone(), 58 | offer_grin.clone(), 59 | outputs_grin.clone(), 60 | output_keypairs_grin_redeemer, 61 | )?; 62 | 63 | let (bob0, message1) = Bob0::::new( 64 | offer_bitcoin.clone(), 65 | outputs_bitcoin, 66 | offer_grin.clone(), 67 | outputs_grin, 68 | output_keypairs_grin_funder, 69 | message0, 70 | )?; 71 | 72 | let (alice1, message2) = alice0.receive(message1)?; 73 | 74 | let (bob1, message3) = bob0.receive(message2)?; 75 | 76 | let (alice2, message4) = alice1.receive(message3)?; 77 | 78 | let bob2 = bob1.receive(message4)?; 79 | 80 | // Execution 81 | 82 | alice2 83 | .alpha_state 84 | .fund_action 85 | .clone() 86 | .execute(&alice_alpha_wallet)?; 87 | 88 | bob2.beta_state.fund_action.execute(&bob_beta_wallet)?; 89 | 90 | // Verify that alice funds the bitcoin 91 | assert!(alice_alpha_wallet.verify_payment_to_address( 92 | alice2.alpha_state.fund_action.transaction.txid(), 93 | offer_bitcoin.fund_output_amount() 94 | )?); 95 | 96 | alice2 97 | .beta_state 98 | .redeem_action 99 | .execute(&alice_beta_wallet)?; 100 | 101 | let beta_decrypted_redeem_sig = bob_beta_wallet.look_for(bob2.beta_state.redeem_event)?; 102 | let y = schnorr::recover(&beta_decrypted_redeem_sig, &bob2.beta_state.recovery_key)?; 103 | let alpha_redeem_action = bob2.alpha_state.encrypted_redeem_action.decrypt(&y); 104 | let alpha_redeem_txid = alpha_redeem_action.transaction.txid(); 105 | 106 | alpha_redeem_action.execute(&bob_alpha_wallet)?; 107 | 108 | // Verify that alice gets the agreed upon grin 109 | assert_eq!( 110 | alice_beta_wallet.get_balance()?, 111 | alice_beta_starting_balance + offer_grin.asset 112 | ); 113 | 114 | // Verify that bob gets the agreed upon bitcoin 115 | assert!(bob_alpha_wallet.verify_payment_to_address(alpha_redeem_txid, offer_bitcoin.asset)?); 116 | 117 | // Clean-up 118 | 119 | grin_node.kill(); 120 | bitcoin_node.kill()?; 121 | Ok(()) 122 | } 123 | -------------------------------------------------------------------------------- /src/bitcoin/action.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitcoin::{ 3 | transaction::{fund_transaction, redeem_transaction}, 4 | wallet::{signature_into_witness, FunderWallet, RedeemerWallet}, 5 | wallet_outputs::WalletOutputs, 6 | Client, Offer, PKs, SKs, Signature, Transaction, 7 | }, 8 | ecdsa, 9 | keypair::KeyPair, 10 | Execute, 11 | }; 12 | use ::bitcoin::{hashes::Hash, util::bip143::SighashComponents, Script}; 13 | use anyhow::Context; 14 | use secp256k1zkp::Message; 15 | 16 | #[derive(Clone)] 17 | pub struct Fund { 18 | pub transaction: Transaction, 19 | } 20 | 21 | #[derive(Clone)] 22 | pub struct Refund { 23 | pub transaction: Transaction, 24 | } 25 | 26 | impl Refund { 27 | pub fn new( 28 | transaction: Transaction, 29 | redeemer_sig: Signature, 30 | funder_sig: Signature, 31 | fund_output_script: Script, 32 | ) -> Self { 33 | let mut completed_transaction = transaction; 34 | let funder_witness = signature_into_witness(funder_sig); 35 | let redeemer_witness = signature_into_witness(redeemer_sig); 36 | 37 | completed_transaction.input[0].witness = vec![ 38 | vec![], /* You have to put some extra shit on the stack because OP_CHECKMULTISIG is 39 | * buggy */ 40 | redeemer_witness, 41 | funder_witness, 42 | fund_output_script.to_bytes(), 43 | ]; 44 | 45 | Refund { 46 | transaction: completed_transaction, 47 | } 48 | } 49 | } 50 | 51 | pub struct EncryptedRedeem { 52 | pub transaction: Transaction, 53 | pub redeemer_sig: Signature, 54 | pub funder_encsig: ecdsa::EncryptedSignature, 55 | pub fund_output_script: Script, 56 | } 57 | 58 | impl EncryptedRedeem { 59 | pub fn new( 60 | offer: &Offer, 61 | wallet_outputs: &WalletOutputs, 62 | redeemer_SKs: &SKs, 63 | funder_PKs: &PKs, 64 | funder_encsig: ecdsa::EncryptedSignature, 65 | ) -> anyhow::Result { 66 | let (fund_transaction, fund_output_script) = fund_transaction( 67 | &offer, 68 | &wallet_outputs, 69 | &redeemer_SKs.x.public_key, 70 | &funder_PKs.X, 71 | )?; 72 | 73 | let redeem_transaction = 74 | redeem_transaction(&offer, &wallet_outputs, fund_transaction.txid()); 75 | 76 | let redeemer_sig = { 77 | let redeem_digest = SighashComponents::new(&redeem_transaction).sighash_all( 78 | &redeem_transaction.input[0], 79 | &fund_output_script, 80 | fund_transaction.output[0].value, 81 | ); 82 | let redeem_digest = Message::from_slice(&redeem_digest.into_inner()) 83 | .expect("should not fail because it is a hash"); 84 | 85 | redeemer_SKs.x.sign_ecdsa(&redeem_digest) 86 | }; 87 | 88 | Ok(Self { 89 | transaction: redeem_transaction, 90 | redeemer_sig, 91 | funder_encsig, 92 | fund_output_script, 93 | }) 94 | } 95 | 96 | pub fn decrypt(self, y: &KeyPair) -> Redeem { 97 | let funder_sig = ecdsa::decsig(&y, &self.funder_encsig).into(); 98 | 99 | let mut completed_transaction = self.transaction; 100 | let funder_witness = signature_into_witness(funder_sig); 101 | let redeemer_witness = signature_into_witness(self.redeemer_sig); 102 | 103 | completed_transaction.input[0].witness = vec![ 104 | vec![], /* You have to put some extra shit on the stack because OP_CHECKMULTISIG is 105 | * buggy */ 106 | redeemer_witness, 107 | funder_witness, 108 | self.fund_output_script.to_bytes(), 109 | ]; 110 | 111 | Redeem { 112 | transaction: completed_transaction, 113 | } 114 | } 115 | } 116 | 117 | #[derive(Clone)] 118 | pub struct Redeem { 119 | pub transaction: Transaction, 120 | } 121 | 122 | impl Execute for Fund { 123 | type Wallet = FunderWallet; 124 | type Return = (); 125 | fn execute(self, wallet: &Self::Wallet) -> anyhow::Result { 126 | let transaction = wallet.sign_input(self.transaction)?; 127 | 128 | wallet.send_rawtransaction(&transaction).context("fund") 129 | } 130 | } 131 | 132 | impl Execute for Redeem { 133 | type Wallet = RedeemerWallet; 134 | type Return = (); 135 | fn execute(self, wallet: &Self::Wallet) -> anyhow::Result { 136 | wallet 137 | .send_rawtransaction(&self.transaction) 138 | .context("redeem") 139 | } 140 | } 141 | 142 | impl Execute for Refund { 143 | type Wallet = FunderWallet; 144 | type Return = (); 145 | fn execute(self, wallet: &Self::Wallet) -> anyhow::Result { 146 | wallet 147 | .send_rawtransaction(&self.transaction) 148 | .context("refund") 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/grin/alice.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | commit::CoinTossingKeys, 3 | grin::{ 4 | action, bulletproof, normalize_redeem_keys_alice, EncryptedSignature, Funder0, Funder1, 5 | Funder2, KeyPair, Offer, PKs, Redeemer0, Redeemer1, Redeemer2, RedeemerSigs, 6 | SpecialOutputKeyPairsFunder, SpecialOutputKeyPairsRedeemer, SpecialOutputs, 7 | }, 8 | }; 9 | use std::convert::TryInto; 10 | 11 | #[derive(Clone)] 12 | pub struct AliceFunder0 { 13 | pub common: Funder0, 14 | pub bulletproof_common_nonce: bulletproof::CommonNonce, 15 | pub bulletproof_round_1_self: bulletproof::Round1, 16 | } 17 | 18 | impl AliceFunder0 { 19 | pub fn new( 20 | offer: Offer, 21 | special_outputs: SpecialOutputs, 22 | special_output_keypairs_funder: SpecialOutputKeyPairsFunder, 23 | ) -> anyhow::Result { 24 | let common = Funder0::new(offer, special_outputs, special_output_keypairs_funder); 25 | 26 | let bulletproof_common_nonce = 27 | bulletproof::CommonNonce::derive(&common.SKs_self.x.public_key)?; 28 | let bulletproof_round_1_self = bulletproof::Round1::new(&common.SKs_self.x.secret_key)?; 29 | 30 | Ok(Self { 31 | common, 32 | bulletproof_common_nonce, 33 | bulletproof_round_1_self, 34 | }) 35 | } 36 | 37 | pub fn transition( 38 | self, 39 | PKs_other: PKs, 40 | bulletproof_round_1_other: bulletproof::Round1, 41 | ) -> anyhow::Result { 42 | Ok(AliceFunder1(Funder1 { 43 | offer: self.common.offer, 44 | special_outputs: self.common.special_outputs, 45 | special_output_keypairs_funder: self.common.special_output_keypairs_funder, 46 | SKs_self: self.common.SKs_self, 47 | PKs_other, 48 | bulletproof_common_nonce: self.bulletproof_common_nonce, 49 | bulletproof_round_1_self: self.bulletproof_round_1_self, 50 | bulletproof_round_1_other, 51 | })) 52 | } 53 | } 54 | 55 | pub struct AliceFunder1(pub Funder1); 56 | 57 | impl AliceFunder1 { 58 | pub fn transition( 59 | self, 60 | redeemer_sigs: RedeemerSigs, 61 | y: &KeyPair, 62 | bulletproof_round_2_other: bulletproof::Round2, 63 | ) -> anyhow::Result<(AliceFunder2, EncryptedSignature)> { 64 | let (state, redeem_encsig) = 65 | self.0 66 | .transition(redeemer_sigs, &y.public_key, bulletproof_round_2_other)?; 67 | 68 | Ok((AliceFunder2(state), redeem_encsig)) 69 | } 70 | } 71 | 72 | pub struct AliceFunder2(pub Funder2); 73 | 74 | #[derive(Clone)] 75 | pub struct AliceRedeemer0 { 76 | pub common: Redeemer0, 77 | pub bulletproof_common_nonce: bulletproof::CommonNonce, 78 | pub bulletproof_round_1_self: bulletproof::Round1, 79 | } 80 | 81 | impl AliceRedeemer0 { 82 | pub fn new( 83 | offer: Offer, 84 | special_outputs: SpecialOutputs, 85 | special_output_keypairs_redeemer: SpecialOutputKeyPairsRedeemer, 86 | ) -> anyhow::Result { 87 | let common = Redeemer0::new(offer, special_outputs, special_output_keypairs_redeemer); 88 | 89 | let bulletproof_common_nonce = 90 | bulletproof::CommonNonce::derive(&common.SKs_self.x.public_key)?; 91 | let bulletproof_round_1_self = bulletproof::Round1::new(&common.SKs_self.x.secret_key)?; 92 | 93 | Ok(Self { 94 | common, 95 | bulletproof_common_nonce, 96 | bulletproof_round_1_self, 97 | }) 98 | } 99 | 100 | pub fn transition( 101 | mut self, 102 | mut PKs_other: PKs, 103 | mut y: &mut KeyPair, 104 | bulletproof_round_1_other: bulletproof::Round1, 105 | ) -> anyhow::Result<(AliceRedeemer1, RedeemerSigs, bulletproof::Round2)> { 106 | normalize_redeem_keys_alice( 107 | &mut self.common.SKs_self.r_redeem, 108 | &mut PKs_other.R_redeem, 109 | &mut y, 110 | )?; 111 | 112 | let (state, redeemer_sigs, bulletproof_round_2_self) = self.common.transition( 113 | self.bulletproof_common_nonce, 114 | self.bulletproof_round_1_self.clone(), 115 | bulletproof_round_1_other.clone(), 116 | PKs_other, 117 | y.public_key, 118 | )?; 119 | 120 | Ok(( 121 | AliceRedeemer1 { 122 | common: state, 123 | bulletproof_round_1_self: self.bulletproof_round_1_self, 124 | bulletproof_round_1_other, 125 | }, 126 | redeemer_sigs, 127 | bulletproof_round_2_self, 128 | )) 129 | } 130 | } 131 | 132 | pub struct AliceRedeemer1 { 133 | pub common: Redeemer1, 134 | pub bulletproof_round_1_self: bulletproof::Round1, 135 | pub bulletproof_round_1_other: bulletproof::Round1, 136 | } 137 | 138 | impl AliceRedeemer1 { 139 | pub fn transition( 140 | self, 141 | y: KeyPair, 142 | redeem_encsig: EncryptedSignature, 143 | ) -> anyhow::Result { 144 | let Redeemer2 { 145 | encrypted_redeem_action, 146 | } = self.common.transition(y.public_key, redeem_encsig)?; 147 | 148 | let redeem_action = encrypted_redeem_action.decrypt(&y)?; 149 | 150 | Ok(AliceRedeemer2 { redeem_action }) 151 | } 152 | } 153 | 154 | pub struct AliceRedeemer2 { 155 | pub redeem_action: action::Redeem, 156 | } 157 | 158 | impl Into for AliceFunder0 { 159 | fn into(self) -> CoinTossingKeys { 160 | let PKs: PKs = self.common.SKs_self.into(); 161 | vec![PKs.X, PKs.R_fund, PKs.R_redeem, PKs.R_refund] 162 | } 163 | } 164 | 165 | impl Into for AliceRedeemer0 { 166 | fn into(self) -> CoinTossingKeys { 167 | let PKs: PKs = self.common.SKs_self.into(); 168 | vec![PKs.X, PKs.R_fund, PKs.R_redeem, PKs.R_refund] 169 | } 170 | } 171 | 172 | impl TryInto for CoinTossingKeys { 173 | type Error = anyhow::Error; 174 | fn try_into(self) -> anyhow::Result { 175 | Ok(PKs { 176 | X: self[0], 177 | R_fund: self[1], 178 | R_redeem: self[2], 179 | R_refund: self[3], 180 | }) 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/grin/bob.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | grin::{ 3 | action, bulletproof, event, normalize_redeem_keys_bob, EncryptedSignature, Funder0, 4 | Funder1, Offer, PKs, PublicKey, Redeemer0, Redeemer1, Redeemer2, RedeemerSigs, SKs, 5 | SpecialOutputKeyPairsFunder, SpecialOutputKeyPairsRedeemer, SpecialOutputs, 6 | }, 7 | schnorr::RecoveryKey, 8 | }; 9 | use std::convert::TryFrom; 10 | 11 | #[derive(Clone)] 12 | pub struct BobFunder0 { 13 | pub common: Funder0, 14 | pub bulletproof_round_1_self: bulletproof::Round1, 15 | pub bulletproof_round_1_other: bulletproof::Round1, 16 | } 17 | 18 | impl BobFunder0 { 19 | pub fn new( 20 | offer: Offer, 21 | special_outputs: SpecialOutputs, 22 | special_output_keypairs_funder: SpecialOutputKeyPairsFunder, 23 | bulletproof_round_1_other: bulletproof::Round1, 24 | ) -> anyhow::Result { 25 | let common = Funder0::new(offer, special_outputs, special_output_keypairs_funder); 26 | let bulletproof_round_1_self = bulletproof::Round1::new(&common.SKs_self.x.secret_key)?; 27 | 28 | Ok(Self { 29 | common, 30 | bulletproof_round_1_self, 31 | bulletproof_round_1_other, 32 | }) 33 | } 34 | 35 | pub fn transition( 36 | self, 37 | PKs_other: PKs, 38 | redeemer_sigs: RedeemerSigs, 39 | Y: &PublicKey, 40 | bulletproof_round_2_other: bulletproof::Round2, 41 | ) -> anyhow::Result<(BobFunder1, EncryptedSignature)> { 42 | let state = Funder1 { 43 | offer: self.common.offer.clone(), 44 | special_outputs: self.common.special_outputs.clone(), 45 | special_output_keypairs_funder: self.common.special_output_keypairs_funder, 46 | SKs_self: self.common.SKs_self.clone(), 47 | PKs_other: PKs_other.clone(), 48 | bulletproof_common_nonce: bulletproof::CommonNonce::derive(&PKs_other.X)?, 49 | bulletproof_round_1_self: self.bulletproof_round_1_self, 50 | bulletproof_round_1_other: self.bulletproof_round_1_other, 51 | }; 52 | 53 | let (state, redeem_encsig) = 54 | state.transition(redeemer_sigs, &Y, bulletproof_round_2_other)?; 55 | 56 | let recovery_key = RecoveryKey::try_from(redeem_encsig)?; 57 | 58 | Ok(( 59 | BobFunder1 { 60 | special_outputs: self.common.special_outputs, 61 | SKs_self: self.common.SKs_self, 62 | PKs_other, 63 | fund_action: state.fund_action, 64 | refund_action: state.refund_action, 65 | recovery_key, 66 | }, 67 | redeem_encsig, 68 | )) 69 | } 70 | } 71 | 72 | pub struct BobFunder1 { 73 | special_outputs: SpecialOutputs, 74 | SKs_self: SKs, 75 | PKs_other: PKs, 76 | fund_action: action::Fund, 77 | refund_action: action::Refund, 78 | recovery_key: RecoveryKey, 79 | } 80 | 81 | impl BobFunder1 { 82 | pub fn transition(self) -> anyhow::Result { 83 | let redeem_event = event::Redeem::new( 84 | &self.special_outputs, 85 | &self.PKs_other, 86 | &self.SKs_self.into(), 87 | )?; 88 | 89 | Ok(BobFunder2 { 90 | fund_action: self.fund_action, 91 | refund_action: self.refund_action, 92 | recovery_key: self.recovery_key, 93 | redeem_event, 94 | }) 95 | } 96 | } 97 | 98 | pub struct BobFunder2 { 99 | pub fund_action: action::Fund, 100 | pub refund_action: action::Refund, 101 | pub recovery_key: RecoveryKey, 102 | pub redeem_event: event::Redeem, 103 | } 104 | 105 | #[derive(Clone)] 106 | pub struct BobRedeemer0 { 107 | pub common: Redeemer0, 108 | pub bulletproof_round_1_self: bulletproof::Round1, 109 | pub bulletproof_round_1_other: bulletproof::Round1, 110 | } 111 | 112 | impl BobRedeemer0 { 113 | pub fn new( 114 | offer: Offer, 115 | special_outputs: SpecialOutputs, 116 | special_output_keypairs_redeemer: SpecialOutputKeyPairsRedeemer, 117 | bulletproof_round_1_other: bulletproof::Round1, 118 | ) -> anyhow::Result { 119 | let common = Redeemer0::new(offer, special_outputs, special_output_keypairs_redeemer); 120 | let bulletproof_round_1_self = bulletproof::Round1::new(&common.SKs_self.x.secret_key)?; 121 | 122 | Ok(Self { 123 | common, 124 | bulletproof_round_1_self, 125 | bulletproof_round_1_other, 126 | }) 127 | } 128 | 129 | pub fn transition( 130 | mut self, 131 | mut PKs_other: PKs, 132 | mut Y: &mut PublicKey, 133 | ) -> anyhow::Result<(BobRedeemer1, RedeemerSigs, bulletproof::Round2)> { 134 | normalize_redeem_keys_bob( 135 | &mut PKs_other.R_redeem, 136 | &mut self.common.SKs_self.r_redeem, 137 | &mut Y, 138 | )?; 139 | 140 | let bulletproof_common_nonce = bulletproof::CommonNonce::derive(&PKs_other.X)?; 141 | let (state, redeemer_sigs, bulletproof_round_2_self) = self.common.transition( 142 | bulletproof_common_nonce, 143 | self.bulletproof_round_1_self, 144 | self.bulletproof_round_1_other, 145 | PKs_other, 146 | Y.clone(), 147 | )?; 148 | 149 | Ok((BobRedeemer1(state), redeemer_sigs, bulletproof_round_2_self)) 150 | } 151 | } 152 | 153 | pub struct BobRedeemer1(pub Redeemer1); 154 | 155 | impl BobRedeemer1 { 156 | pub fn transition( 157 | self, 158 | Y: PublicKey, 159 | redeem_encsig: EncryptedSignature, 160 | ) -> anyhow::Result { 161 | let Redeemer2 { 162 | encrypted_redeem_action, 163 | } = self.0.transition(Y, redeem_encsig)?; 164 | 165 | Ok(BobRedeemer2 { 166 | encrypted_redeem_action, 167 | }) 168 | } 169 | } 170 | 171 | pub struct BobRedeemer2 { 172 | pub encrypted_redeem_action: action::EncryptedRedeem, 173 | } 174 | 175 | impl Into for BobFunder0 { 176 | fn into(self) -> PKs { 177 | self.common.SKs_self.into() 178 | } 179 | } 180 | 181 | impl Into for BobRedeemer0 { 182 | fn into(self) -> PKs { 183 | self.common.SKs_self.into() 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/bob.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitcoin, 3 | commit::Commitment, 4 | grin::{self, bulletproof}, 5 | keypair, 6 | messages::{Message0, Message1, Message2, Message3, Message4}, 7 | }; 8 | use std::convert::TryInto; 9 | 10 | pub struct Bob0 { 11 | alpha_state: AL, 12 | beta_state: BL, 13 | alice_commitment: Commitment, 14 | } 15 | 16 | impl Bob0 { 17 | pub fn new( 18 | offer_grin: grin::Offer, 19 | outputs_grin: grin::SpecialOutputs, 20 | output_keypairs_grin_redeemer: grin::SpecialOutputKeyPairsRedeemer, 21 | offer_bitcoin: bitcoin::Offer, 22 | outputs_bitcoin: bitcoin::WalletOutputs, 23 | message: Message0, 24 | ) -> anyhow::Result<(Self, Message1)> { 25 | let grin_state = grin::bob::BobRedeemer0::new( 26 | offer_grin, 27 | outputs_grin, 28 | output_keypairs_grin_redeemer, 29 | message.bulletproof_round_1_alice, 30 | )?; 31 | let bitcoin_state = bitcoin::bob::BobFunder0::new(offer_bitcoin, outputs_bitcoin); 32 | 33 | Ok(( 34 | Bob0 { 35 | alpha_state: grin_state.clone(), 36 | beta_state: bitcoin_state.clone(), 37 | alice_commitment: message.commitment, 38 | }, 39 | Message1 { 40 | PKs_alpha: grin_state.clone().into(), 41 | PKs_beta: bitcoin_state.into(), 42 | bulletproof_round_1_bob: grin_state.bulletproof_round_1_self, 43 | }, 44 | )) 45 | } 46 | 47 | #[allow(clippy::type_complexity)] 48 | pub fn receive( 49 | self, 50 | Message2 { 51 | opening, 52 | beta_redeemer_sigs: alice_bitcoin_refund_signature, 53 | .. 54 | }: Message2, 55 | ) -> anyhow::Result<( 56 | Bob1, 57 | Message3<(grin::RedeemerSigs, grin::bulletproof::Round2), bitcoin::EncryptedSignature>, 58 | )> { 59 | let (alice_PKs_grin, alice_PKs_bitcoin, mut Y) = opening.open(self.alice_commitment)?; 60 | 61 | let (grin_state, grin_redeemer_sigs, bulletproof_round_2_self) = self 62 | .alpha_state 63 | .transition(alice_PKs_grin.try_into()?, &mut Y)?; 64 | let (bitcoin_state, bitcoin_redeem_encsig) = self.beta_state.transition( 65 | alice_PKs_bitcoin.try_into()?, 66 | alice_bitcoin_refund_signature, 67 | &Y, 68 | )?; 69 | 70 | let state = Bob1 { 71 | alpha_state: grin_state, 72 | beta_state: bitcoin_state, 73 | Y, 74 | }; 75 | 76 | let message = Message3 { 77 | alpha_redeemer_sigs: (grin_redeemer_sigs, bulletproof_round_2_self), 78 | beta_redeem_encsig: bitcoin_redeem_encsig, 79 | }; 80 | 81 | Ok((state, message)) 82 | } 83 | } 84 | 85 | impl Bob0 { 86 | pub fn new( 87 | offer_bitcoin: bitcoin::Offer, 88 | outputs_bitcoin: bitcoin::WalletOutputs, 89 | offer_grin: grin::Offer, 90 | outputs_grin: grin::SpecialOutputs, 91 | output_keypairs_grin_funder: grin::SpecialOutputKeyPairsFunder, 92 | message: Message0, 93 | ) -> anyhow::Result<(Self, Message1)> { 94 | let bitcoin_state = bitcoin::bob::BobRedeemer0::new(offer_bitcoin, outputs_bitcoin); 95 | let grin_state = grin::bob::BobFunder0::new( 96 | offer_grin, 97 | outputs_grin, 98 | output_keypairs_grin_funder, 99 | message.bulletproof_round_1_alice, 100 | )?; 101 | 102 | Ok(( 103 | Bob0 { 104 | alpha_state: bitcoin_state.clone(), 105 | beta_state: grin_state.clone(), 106 | alice_commitment: message.commitment, 107 | }, 108 | Message1 { 109 | PKs_alpha: bitcoin_state.into(), 110 | PKs_beta: grin_state.clone().into(), 111 | bulletproof_round_1_bob: grin_state.bulletproof_round_1_self, 112 | }, 113 | )) 114 | } 115 | 116 | pub fn receive( 117 | self, 118 | Message2 { 119 | opening, 120 | beta_redeemer_sigs: alice_grin_redeemer_sigs, 121 | }: Message2<(grin::RedeemerSigs, bulletproof::Round2)>, 122 | ) -> anyhow::Result<( 123 | Bob1, 124 | Message3, 125 | )> { 126 | let (alice_PKs_bitcoin, alice_PKs_grin, Y) = opening.open(self.alice_commitment)?; 127 | 128 | let (bitcoin_state, bitcoin_redeemer_refund_sig) = 129 | self.alpha_state.transition(alice_PKs_bitcoin.try_into()?)?; 130 | let (grin_state, grin_redeem_encsig) = self.beta_state.transition( 131 | alice_PKs_grin.try_into()?, 132 | alice_grin_redeemer_sigs.0, 133 | &Y, 134 | alice_grin_redeemer_sigs.1, 135 | )?; 136 | 137 | let state = Bob1 { 138 | alpha_state: bitcoin_state, 139 | beta_state: grin_state, 140 | Y, 141 | }; 142 | 143 | let message = Message3 { 144 | alpha_redeemer_sigs: bitcoin_redeemer_refund_sig, 145 | beta_redeem_encsig: grin_redeem_encsig, 146 | }; 147 | 148 | Ok((state, message)) 149 | } 150 | } 151 | 152 | pub struct Bob1 { 153 | alpha_state: AL, 154 | beta_state: BL, 155 | Y: keypair::PublicKey, 156 | } 157 | 158 | impl Bob1 { 159 | pub fn receive( 160 | self, 161 | message: Message4, 162 | ) -> anyhow::Result> { 163 | let grin_state = self 164 | .alpha_state 165 | .transition(self.Y, message.alpha_redeem_encsig)?; 166 | let bitcoin_state = self.beta_state.transition()?; 167 | 168 | Ok(Bob2 { 169 | alpha_state: grin_state, 170 | beta_state: bitcoin_state, 171 | }) 172 | } 173 | } 174 | 175 | impl Bob1 { 176 | pub fn receive( 177 | self, 178 | message: Message4, 179 | ) -> anyhow::Result> { 180 | let bitcoin_state = self.alpha_state.transition(message.alpha_redeem_encsig)?; 181 | let grin_state = self.beta_state.transition()?; 182 | 183 | Ok(Bob2 { 184 | alpha_state: bitcoin_state, 185 | beta_state: grin_state, 186 | }) 187 | } 188 | } 189 | 190 | pub struct Bob2 { 191 | pub alpha_state: AL, 192 | pub beta_state: BL, 193 | } 194 | -------------------------------------------------------------------------------- /src/grin/bulletproof.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | grin::public_key_to_pedersen_commitment, 3 | keypair::{random_secret_key, PublicKey, SecretKey, SECP}, 4 | }; 5 | use secp256k1zkp::pedersen::RangeProof; 6 | use sha2::{Digest, Sha256}; 7 | 8 | #[derive(Clone, Debug)] 9 | pub struct CommonNonce(SecretKey); 10 | 11 | impl CommonNonce { 12 | pub fn derive(pk: &PublicKey) -> anyhow::Result { 13 | let mut hasher = Sha256::new(); 14 | hasher.input(pk.0); 15 | let sk = SecretKey::from_slice(&*SECP, &hasher.result())?; 16 | Ok(Self(sk)) 17 | } 18 | } 19 | 20 | #[derive(Debug, Clone)] 21 | pub struct Round1 { 22 | pub T_1: PublicKey, 23 | pub T_2: PublicKey, 24 | } 25 | 26 | impl Round1 { 27 | /// To generate T_1 and T_2 for each party we hash their x_fund 28 | pub fn new(private_nonce: &SecretKey) -> anyhow::Result { 29 | // --- BEGIN INIT UNUSED VALUES --- 30 | let value = 0; 31 | let blind = random_secret_key(); 32 | let common_nonce = random_secret_key(); 33 | let commit = SECP.commit_value(0)?; 34 | // --- END INIT UNUSED VALUES --- 35 | 36 | let mut T_1 = PublicKey::new(); 37 | let mut T_2 = PublicKey::new(); 38 | let _ = SECP.bullet_proof_multisig( 39 | value, 40 | blind, 41 | common_nonce, 42 | None, 43 | None, 44 | None, 45 | Some(&mut T_1), 46 | Some(&mut T_2), 47 | vec![commit], 48 | Some(&private_nonce), 49 | 1, 50 | ); 51 | 52 | Ok(Round1 { T_1, T_2 }) 53 | } 54 | } 55 | 56 | pub struct Round2 { 57 | tau_x: SecretKey, 58 | } 59 | 60 | impl Round2 { 61 | pub fn new( 62 | private_nonce: &SecretKey, 63 | x_j: &SecretKey, 64 | X: &PublicKey, 65 | value: u64, 66 | common_nonce: &CommonNonce, 67 | Round1 { 68 | T_1: T_1_R, 69 | T_2: T_2_R, 70 | }: &Round1, 71 | Round1 { 72 | T_1: T_1_F, 73 | T_2: T_2_F, 74 | }: &Round1, 75 | ) -> anyhow::Result { 76 | let mut T_one = PublicKey::from_combination(&*SECP, vec![&T_1_R, &T_1_F])?; 77 | let mut T_two = PublicKey::from_combination(&*SECP, vec![&T_2_R, &T_2_F])?; 78 | 79 | let commit = { 80 | let commit_blind = public_key_to_pedersen_commitment(&X); 81 | let commit_value = SECP.commit_value(value)?; 82 | SECP.commit_sum(vec![commit_blind, commit_value], Vec::new())? 83 | }; 84 | 85 | let mut tau_x = SecretKey([0; 32]); 86 | SECP.bullet_proof_multisig( 87 | value, 88 | x_j.clone(), 89 | common_nonce.0.clone(), 90 | None, 91 | None, 92 | Some(&mut tau_x), 93 | Some(&mut T_one), 94 | Some(&mut T_two), 95 | vec![commit], 96 | Some(&private_nonce), 97 | 2, 98 | ); 99 | 100 | Ok(Round2 { tau_x }) 101 | } 102 | } 103 | 104 | pub struct Round3 { 105 | pub bulletproof: RangeProof, 106 | } 107 | 108 | impl Round3 { 109 | #[allow(clippy::too_many_arguments)] 110 | pub fn new( 111 | private_nonce: &SecretKey, 112 | x_j: &SecretKey, 113 | X: &PublicKey, 114 | value: u64, 115 | common_nonce: &CommonNonce, 116 | Round1 { 117 | T_1: T_1_R, 118 | T_2: T_2_R, 119 | }: &Round1, 120 | Round1 { 121 | T_1: T_1_F, 122 | T_2: T_2_F, 123 | }: &Round1, 124 | Round2 { tau_x: tau_x_R }: &Round2, 125 | Round2 { tau_x: tau_x_F }: &Round2, 126 | ) -> anyhow::Result { 127 | let mut T_one = PublicKey::from_combination(&*SECP, vec![&T_1_R, &T_1_F])?; 128 | let mut T_two = PublicKey::from_combination(&*SECP, vec![&T_2_R, &T_2_F])?; 129 | 130 | let commit = { 131 | let commit_blind = public_key_to_pedersen_commitment(&X); 132 | let commit_value = SECP.commit_value(value)?; 133 | SECP.commit_sum(vec![commit_blind, commit_value], Vec::new())? 134 | }; 135 | 136 | let mut tau_x = { 137 | let mut tau_x = tau_x_R.clone(); 138 | tau_x.add_assign(&*SECP, &tau_x_F)?; 139 | tau_x 140 | }; 141 | 142 | let bulletproof = SECP 143 | .bullet_proof_multisig( 144 | value, 145 | x_j.clone(), 146 | common_nonce.0.clone(), 147 | None, 148 | None, 149 | Some(&mut tau_x), 150 | Some(&mut T_one), 151 | Some(&mut T_two), 152 | vec![commit], 153 | Some(&private_nonce), 154 | 0, 155 | ) 156 | .ok_or_else(|| anyhow::anyhow!("failed to generate bulletproof"))?; 157 | 158 | SECP.verify_bullet_proof(commit, bulletproof, None)?; 159 | 160 | Ok(Round3 { bulletproof }) 161 | } 162 | } 163 | 164 | #[cfg(test)] 165 | mod test { 166 | use super::*; 167 | use crate::keypair::{KeyPair, Negate}; 168 | 169 | #[test] 170 | fn can_generate_multiparty_bulletproof() -> anyhow::Result<()> { 171 | let value = 1_000_000; 172 | 173 | let x_input = KeyPair::new_random(); 174 | 175 | let x_alice = KeyPair::new_random(); 176 | let common_nonce = CommonNonce::derive(&x_alice.public_key)?; 177 | 178 | let x_bob = KeyPair::new_random(); 179 | 180 | let X = PublicKey::from_combination(&*SECP, vec![ 181 | &x_alice.public_key, 182 | &x_bob.public_key, 183 | &x_input.public_key.negate(), 184 | ])?; 185 | 186 | let mut x_bob_prime = x_bob.secret_key; 187 | x_bob_prime.add_assign(&*SECP, &x_input.secret_key.negate())?; 188 | 189 | let round1_alice = Round1::new(&x_alice.secret_key)?; 190 | let round1_bob = Round1::new(&x_bob_prime)?; 191 | 192 | let round2_alice = Round2::new( 193 | &x_alice.secret_key, 194 | &x_alice.secret_key, 195 | &X, 196 | value, 197 | &common_nonce, 198 | &round1_alice, 199 | &round1_bob, 200 | )?; 201 | 202 | let round2_bob = Round2::new( 203 | &x_bob_prime, 204 | &x_bob_prime, 205 | &X, 206 | value, 207 | &common_nonce, 208 | &round1_alice, 209 | &round1_bob, 210 | )?; 211 | 212 | assert!(Round3::new( 213 | &x_alice.secret_key, 214 | &x_alice.secret_key, 215 | &X, 216 | value, 217 | &common_nonce, 218 | &round1_alice, 219 | &round1_bob, 220 | &round2_alice, 221 | &round2_bob, 222 | ) 223 | .is_ok()); 224 | Ok(()) 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/ecdsa.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | dleq, 3 | keypair::{random_secret_key, KeyPair, Negate, PublicKey, SecretKey, XCoor, G, SECP}, 4 | }; 5 | 6 | #[derive(Debug, PartialEq, Clone)] 7 | pub struct Signature { 8 | s: SecretKey, 9 | R_x: SecretKey, 10 | } 11 | 12 | #[derive(Debug)] 13 | pub struct EncryptedSignature { 14 | R: PublicKey, 15 | R_hat: PublicKey, 16 | s_hat: SecretKey, 17 | proof: dleq::Proof, 18 | } 19 | 20 | pub fn encsign(x: &KeyPair, Y: &PublicKey, message_hash: &[u8]) -> EncryptedSignature { 21 | let r = random_secret_key(); 22 | let mut R_hat = *G; 23 | R_hat.mul_assign(&*SECP, &r).unwrap(); 24 | 25 | let mut R = *Y; 26 | R.mul_assign(&*SECP, &r).unwrap(); 27 | 28 | let proof = dleq::prove(&G, &R_hat, &Y, &R, &r); 29 | 30 | let s_hat = { 31 | let R_x = SecretKey::from_slice(&*SECP, &R.x_coor()).unwrap(); 32 | 33 | let mut s_hat = R_x; 34 | s_hat.mul_assign(&*SECP, &x.secret_key).unwrap(); 35 | s_hat 36 | .add_assign( 37 | &*SECP, 38 | &SecretKey::from_slice(&*SECP, &message_hash).unwrap(), 39 | ) 40 | .unwrap(); 41 | 42 | let mut r_inv = r; 43 | r_inv.inv_assign(&*SECP).unwrap(); 44 | 45 | s_hat.mul_assign(&*SECP, &r_inv).unwrap(); 46 | 47 | s_hat 48 | }; 49 | 50 | EncryptedSignature { 51 | R, 52 | R_hat, 53 | s_hat, 54 | proof, 55 | } 56 | } 57 | 58 | #[derive(Debug, Clone)] 59 | pub enum EncVerifyError { 60 | InvalidProof, 61 | Invalid, 62 | } 63 | 64 | pub fn encverify( 65 | X: &PublicKey, 66 | Y: &PublicKey, 67 | message_hash: &[u8], 68 | EncryptedSignature { 69 | R, 70 | R_hat, 71 | s_hat, 72 | proof, 73 | }: &EncryptedSignature, 74 | ) -> Result<(), EncVerifyError> { 75 | //TODO: check that s_hat is not 0 -- it will cause a panic 76 | if !dleq::verify(&*G, R_hat, Y, R, proof) { 77 | return Err(EncVerifyError::InvalidProof); 78 | } 79 | 80 | //TODO: Don't panic on something that can be provided by a malicious party 81 | // ::from_slice(0) panics 82 | let R_x = SecretKey::from_slice(&*SECP, &R.x_coor()).unwrap(); 83 | 84 | let message_hash = SecretKey::from_slice(&*SECP, message_hash).unwrap(); 85 | 86 | let mut s_hat_inv = s_hat.clone(); 87 | s_hat_inv.inv_assign(&*SECP).unwrap(); 88 | 89 | let U0 = { 90 | let mut u0 = message_hash; 91 | u0.mul_assign(&*SECP, &s_hat_inv).unwrap(); 92 | let mut U0 = *G; 93 | U0.mul_assign(&*SECP, &u0).unwrap(); 94 | U0 95 | }; 96 | 97 | let U1 = { 98 | let mut u1 = R_x; 99 | u1.mul_assign(&*SECP, &s_hat_inv).unwrap(); 100 | let mut U1 = *X; 101 | U1.mul_assign(&*SECP, &u1).unwrap(); 102 | U1 103 | }; 104 | 105 | let R_hat_candidate = PublicKey::from_combination(&*SECP, vec![&U0, &U1]).unwrap(); 106 | 107 | if &R_hat_candidate == R_hat { 108 | Ok(()) 109 | } else { 110 | Err(EncVerifyError::Invalid) 111 | } 112 | } 113 | 114 | pub fn decsig(y: &KeyPair, EncryptedSignature { R, s_hat, .. }: &EncryptedSignature) -> Signature { 115 | let s = { 116 | let mut y_inv = y.secret_key.clone(); 117 | y_inv.inv_assign(&*SECP).unwrap(); 118 | 119 | let mut s = s_hat.clone(); 120 | s.mul_assign(&*SECP, &y_inv).unwrap(); 121 | s 122 | }; 123 | 124 | let R_x = R.x_coor(); 125 | 126 | Signature { 127 | s, 128 | R_x: SecretKey::from_slice(&*SECP, &R_x).unwrap(), 129 | } 130 | } 131 | 132 | #[derive(Debug, Clone)] 133 | pub struct RecoveryKey { 134 | Y: PublicKey, 135 | s_hat: SecretKey, 136 | } 137 | 138 | pub fn reckey( 139 | &Y: &PublicKey, 140 | EncryptedSignature { s_hat, .. }: &EncryptedSignature, 141 | ) -> RecoveryKey { 142 | RecoveryKey { 143 | Y, 144 | s_hat: s_hat.clone(), 145 | } 146 | } 147 | 148 | pub fn recover( 149 | Signature { s, .. }: &Signature, 150 | RecoveryKey { Y, s_hat }: &RecoveryKey, 151 | ) -> anyhow::Result { 152 | let y_macron = { 153 | let mut s_inv = s.clone(); 154 | s_inv.inv_assign(&*SECP)?; 155 | 156 | let mut y_macron = s_hat.clone(); 157 | y_macron.mul_assign(&*SECP, &s_inv)?; 158 | y_macron 159 | }; 160 | 161 | let mut Gy_macron = *G; 162 | Gy_macron.mul_assign(&*SECP, &y_macron)?; 163 | 164 | if Gy_macron == *Y { 165 | Ok(KeyPair::new(y_macron)) 166 | } else if Gy_macron == Y.negate() { 167 | Ok(KeyPair::new(y_macron.negate())) 168 | } else { 169 | Err(anyhow::anyhow!("recovery key does not match signature")) 170 | } 171 | } 172 | 173 | impl From for secp256k1zkp::Signature { 174 | fn from(from: Signature) -> Self { 175 | let mut buffer = [0u8; 64]; 176 | 177 | buffer[0..32].copy_from_slice(&from.R_x[..]); 178 | buffer[32..64].copy_from_slice(&from.s[..]); 179 | 180 | let mut sig = secp256k1zkp::Signature::from_compact(&*SECP, &buffer).unwrap(); 181 | sig.normalize_s(&*SECP); 182 | sig 183 | } 184 | } 185 | 186 | impl From for Signature { 187 | fn from(from: secp256k1zkp::Signature) -> Self { 188 | let mut sig = from; 189 | sig.normalize_s(&*SECP); 190 | let bytes = sig.serialize_compact(&*SECP); 191 | let mut R_x = [0u8; 32]; 192 | let mut s = [0u8; 32]; 193 | R_x.copy_from_slice(&bytes[0..32]); 194 | s.copy_from_slice(&bytes[32..64]); 195 | 196 | Self { 197 | R_x: SecretKey::from_slice(&*SECP, &R_x).unwrap(), 198 | s: SecretKey::from_slice(&*SECP, &s).unwrap(), 199 | } 200 | } 201 | } 202 | 203 | #[cfg(test)] 204 | mod test { 205 | use super::*; 206 | use secp256k1zkp::Message; 207 | 208 | #[test] 209 | fn encsign_and_encverify() { 210 | let x = KeyPair::new_random(); 211 | let y = KeyPair::new_random(); 212 | let message_hash = b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; 213 | 214 | let enc_signature = encsign(&x, &y.public_key, message_hash); 215 | 216 | encverify(&x.public_key, &y.public_key, message_hash, &enc_signature).unwrap(); 217 | } 218 | 219 | #[test] 220 | fn ecdsa_encsign_and_decsig() { 221 | let x = KeyPair::new_random(); 222 | let y = KeyPair::new_random(); 223 | 224 | let message_hash = b"mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"; 225 | let _message_hash = &Message::from_slice(message_hash).expect("message hash"); 226 | 227 | let encsig = encsign(&x, &y.public_key, message_hash); 228 | 229 | let sig = decsig(&y, &encsig); 230 | 231 | assert!(SECP 232 | .verify(_message_hash, &sig.into(), &x.public_key) 233 | .is_ok()) 234 | } 235 | 236 | #[test] 237 | fn recover_key_from_decrypted_signature() { 238 | let x = KeyPair::new_random(); 239 | let y = KeyPair::new_random(); 240 | 241 | let message_hash = b"mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"; 242 | 243 | let encsig = encsign(&x, &y.public_key, message_hash); 244 | let sig = decsig(&y, &encsig); 245 | 246 | let rec_key = reckey(&y.public_key, &encsig); 247 | let y_tag = recover(&sig, &rec_key).unwrap(); 248 | 249 | assert_eq!(y.secret_key, y_tag.secret_key); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/bitcoin/wallet.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitcoin::{event, Address, Client, OutPoint, Script, Signature, Transaction, TxOut}, 3 | keypair::{verify_ecdsa, KeyPair, PublicKey, SECP}, 4 | LookFor, 5 | }; 6 | use bitcoin::{ 7 | hashes::{hash160, Hash}, 8 | util::bip143::SighashComponents, 9 | SigHashType, 10 | }; 11 | use bitcoin_hashes::sha256d; 12 | use secp256k1zkp::Message; 13 | 14 | pub struct FunderWallet { 15 | url: String, 16 | fund_input: Output, 17 | change_output_keypair: KeyPair, 18 | refund_output_keypair: KeyPair, 19 | } 20 | 21 | impl FunderWallet { 22 | pub fn new(url: String, fund_input: Output) -> anyhow::Result { 23 | Ok(Self { 24 | url, 25 | fund_input, 26 | change_output_keypair: KeyPair::new_random(), 27 | refund_output_keypair: KeyPair::new_random(), 28 | }) 29 | } 30 | 31 | pub fn change_output_address(&self) -> Address { 32 | self.change_output_keypair.to_bitcoin_address() 33 | } 34 | 35 | pub fn refund_output_address(&self) -> Address { 36 | self.refund_output_keypair.to_bitcoin_address() 37 | } 38 | 39 | pub fn fund_input(&self) -> Output { 40 | self.fund_input.clone() 41 | } 42 | 43 | pub fn sign_input(&self, transaction: Transaction) -> anyhow::Result { 44 | let mut completed_tx = transaction; 45 | let sighash_components = SighashComponents::new(&completed_tx); 46 | 47 | #[allow(clippy::toplevel_ref_arg)] 48 | for ref mut input in &mut completed_tx.input { 49 | let wallet_input = self.fund_input(); 50 | 51 | let owned_output = if input.previous_output == wallet_input.outpoint { 52 | &wallet_input 53 | } else { 54 | return Err(anyhow::anyhow!( 55 | "transaction input {:?} not owned by wallet", 56 | input 57 | )); 58 | }; 59 | 60 | let fund_digest = { 61 | let digest = sighash_components.sighash_all( 62 | &input, 63 | &generate_prev_script_p2wpkh(&owned_output.keypair.public_key), 64 | owned_output.txout.value, 65 | ); 66 | 67 | Message::from_slice(&digest.into_inner()).expect("always correct length") 68 | }; 69 | 70 | let signature_element = { 71 | let signature = owned_output.keypair.sign_ecdsa(&fund_digest); 72 | signature_into_witness(signature) 73 | }; 74 | 75 | input.witness = vec![ 76 | signature_element, 77 | owned_output 78 | .keypair 79 | .public_key 80 | .serialize_vec(&*SECP, true) 81 | .to_vec(), 82 | ] 83 | } 84 | 85 | Ok(completed_tx) 86 | } 87 | 88 | pub fn verify_payment_to_address( 89 | &self, 90 | txid: sha256d::Hash, 91 | amount: u64, 92 | ) -> anyhow::Result { 93 | let transaction = self.get_rawtransaction(&txid)?; 94 | 95 | Ok(transaction.output[0].value == amount) 96 | } 97 | } 98 | 99 | pub struct RedeemerWallet { 100 | url: String, 101 | redeem_output_keypair: KeyPair, 102 | } 103 | 104 | impl RedeemerWallet { 105 | pub fn new(url: String) -> Self { 106 | Self { 107 | url, 108 | redeem_output_keypair: KeyPair::new_random(), 109 | } 110 | } 111 | 112 | pub fn redeem_output_address(&self) -> Address { 113 | self.redeem_output_keypair.to_bitcoin_address() 114 | } 115 | 116 | pub fn verify_payment_to_address( 117 | &self, 118 | txid: sha256d::Hash, 119 | amount: u64, 120 | ) -> anyhow::Result { 121 | let transaction = self.get_rawtransaction(&txid)?; 122 | 123 | Ok(transaction.output[0].value == amount) 124 | } 125 | } 126 | 127 | impl Client for FunderWallet { 128 | fn node_url(&self) -> String { 129 | self.url.clone() 130 | } 131 | } 132 | 133 | impl Client for RedeemerWallet { 134 | fn node_url(&self) -> String { 135 | self.url.clone() 136 | } 137 | } 138 | 139 | impl LookFor for FunderWallet { 140 | type Event = event::Redeem; 141 | type Extract = crate::ecdsa::Signature; 142 | 143 | fn look_for(&self, event: Self::Event) -> anyhow::Result { 144 | let transaction = self.get_rawtransaction(&event.txid)?; 145 | 146 | // the redeem transaction contains 1 input 147 | transaction.input[0] 148 | // OP_CHECKMULTISIG doesn't enforce order so we go through all 149 | // of them. 150 | .witness 151 | .iter() 152 | .find_map(|witness| { 153 | if witness.is_empty() { 154 | return None; 155 | } 156 | 157 | // remove last byte which is SIGHASH flag 158 | let sig_bytes = &witness[..witness.len() - 1]; 159 | 160 | match Signature::from_der(&*SECP, sig_bytes) { 161 | Ok(sig) if verify_ecdsa(&event.message_hash, &sig, &event.funder_pk) => { 162 | Some(sig.into()) 163 | } 164 | _ => None, 165 | } 166 | }) 167 | .ok_or_else(|| { 168 | anyhow::anyhow!("failed to find signature corresponding to redeemer's public key") 169 | }) 170 | } 171 | } 172 | 173 | #[derive(Clone, Debug)] 174 | pub struct Output { 175 | pub keypair: KeyPair, 176 | pub outpoint: OutPoint, 177 | pub txout: TxOut, 178 | } 179 | 180 | impl Output { 181 | pub fn new(keypair: KeyPair, outpoint: OutPoint, txout: TxOut) -> Self { 182 | Self { 183 | keypair, 184 | outpoint, 185 | txout, 186 | } 187 | } 188 | 189 | pub fn address(&self) -> Address { 190 | self.keypair.to_bitcoin_address() 191 | } 192 | } 193 | 194 | pub fn find_output<'a>( 195 | transaction: &'a Transaction, 196 | to_address: &Address, 197 | ) -> Option<(u32, &'a TxOut)> { 198 | let to_address_script_pubkey = to_address.script_pubkey(); 199 | 200 | transaction 201 | .output 202 | .iter() 203 | .enumerate() 204 | .map(|(index, txout)| { 205 | // Casting a usize to u32 can lead to truncation on 64bit platforms 206 | // However, bitcoin limits the number of inputs to u32 anyway, so this 207 | // is not a problem for us. 208 | #[allow(clippy::cast_possible_truncation)] 209 | (index as u32, txout) 210 | }) 211 | .find(|(_, txout)| txout.script_pubkey == to_address_script_pubkey) 212 | } 213 | 214 | pub fn signature_into_witness(sig: Signature) -> Vec { 215 | let mut serialized_signature = sig.serialize_der(&*SECP).to_vec(); 216 | serialized_signature.push(SigHashType::All as u8); 217 | serialized_signature 218 | } 219 | 220 | fn generate_prev_script_p2wpkh(public_key: &PublicKey) -> Script { 221 | let public_key_hash = 222 | hash160::Hash::hash(public_key.serialize_vec(&*SECP, true).to_vec().as_ref()); 223 | 224 | let mut prev_script = vec![0x76, 0xa9, 0x14]; 225 | 226 | prev_script.append(&mut public_key_hash[..].to_vec()); 227 | prev_script.push(0x88); 228 | prev_script.push(0xac); 229 | 230 | Script::from(prev_script) 231 | } 232 | -------------------------------------------------------------------------------- /src/alice.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitcoin, 3 | commit::{CoinTossingKeys, Commitment, Opening}, 4 | grin, 5 | messages::{Message0, Message1, Message2, Message3, Message4}, 6 | KeyPair, 7 | }; 8 | use grin::bulletproof; 9 | 10 | pub struct Alice0 { 11 | alpha_state: AL, 12 | beta_state: BL, 13 | y: KeyPair, 14 | } 15 | 16 | impl Alice0 { 17 | pub fn new( 18 | offer_grin: grin::Offer, 19 | outputs_grin: grin::SpecialOutputs, 20 | output_keypairs_grin_funder: grin::SpecialOutputKeyPairsFunder, 21 | offer_bitcoin: bitcoin::Offer, 22 | outputs_bitcoin: bitcoin::WalletOutputs, 23 | ) -> anyhow::Result<(Self, Message0)> { 24 | let grin_state = 25 | grin::alice::AliceFunder0::new(offer_grin, outputs_grin, output_keypairs_grin_funder)?; 26 | let bitcoin_state = bitcoin::alice::AliceRedeemer0::new(offer_bitcoin, outputs_bitcoin); 27 | 28 | Ok(Alice0::state_and_message( 29 | grin_state.clone(), 30 | bitcoin_state, 31 | grin_state.bulletproof_round_1_self, 32 | )) 33 | } 34 | 35 | pub fn receive( 36 | self, 37 | message: Message1, 38 | ) -> anyhow::Result<( 39 | Alice1, 40 | Message2, 41 | )> { 42 | let opening = self.opening(); 43 | 44 | let grin_state = self 45 | .alpha_state 46 | .transition(message.PKs_alpha, message.bulletproof_round_1_bob)?; 47 | let (bitcoin_state, bitcoin_redeemer_refund_sig) = 48 | self.beta_state.transition(message.PKs_beta)?; 49 | 50 | Ok(( 51 | Alice1 { 52 | y: self.y, 53 | alpha_state: grin_state, 54 | beta_state: bitcoin_state, 55 | }, 56 | Message2 { 57 | opening, 58 | beta_redeemer_sigs: bitcoin_redeemer_refund_sig, 59 | }, 60 | )) 61 | } 62 | } 63 | 64 | impl Alice0 { 65 | pub fn new( 66 | offer_bitcoin: bitcoin::Offer, 67 | outputs_bitcoin: bitcoin::WalletOutputs, 68 | offer_grin: grin::Offer, 69 | outputs_grin: grin::SpecialOutputs, 70 | output_keypairs_grin_redeemer: grin::SpecialOutputKeyPairsRedeemer, 71 | ) -> anyhow::Result<(Self, Message0)> { 72 | let bitcoin_state = bitcoin::alice::AliceFunder0::new(offer_bitcoin, outputs_bitcoin); 73 | let grin_state = grin::alice::AliceRedeemer0::new( 74 | offer_grin, 75 | outputs_grin, 76 | output_keypairs_grin_redeemer, 77 | )?; 78 | 79 | Ok(Alice0::state_and_message( 80 | bitcoin_state, 81 | grin_state.clone(), 82 | grin_state.bulletproof_round_1_self, 83 | )) 84 | } 85 | 86 | #[allow(clippy::type_complexity)] 87 | pub fn receive( 88 | mut self, 89 | message: Message1, 90 | ) -> anyhow::Result<( 91 | Alice1, 92 | Message2<(grin::RedeemerSigs, grin::bulletproof::Round2)>, 93 | )> { 94 | // Building the opening must happen now, because some keys may change when 95 | // transitioning Grin's state and Alice has already committed to the original 96 | // ones 97 | let opening = self.opening(); 98 | 99 | let bitcoin_state = self.alpha_state.transition(message.PKs_alpha); 100 | let (grin_state, grin_redeemer_sigs, bulletproof_round_2_alice) = 101 | self.beta_state.transition( 102 | message.PKs_beta, 103 | &mut self.y, 104 | message.bulletproof_round_1_bob, 105 | )?; 106 | 107 | Ok(( 108 | Alice1 { 109 | y: self.y, 110 | alpha_state: bitcoin_state, 111 | beta_state: grin_state, 112 | }, 113 | Message2 { 114 | opening, 115 | beta_redeemer_sigs: (grin_redeemer_sigs, bulletproof_round_2_alice), 116 | }, 117 | )) 118 | } 119 | } 120 | 121 | impl Alice0 122 | where 123 | A: Into + Clone, 124 | B: Into + Clone, 125 | { 126 | pub fn state_and_message( 127 | alpha_state: A, 128 | beta_state: B, 129 | bulletproof_round_1_alice: grin::bulletproof::Round1, 130 | ) -> (Self, Message0) { 131 | let y = KeyPair::new_random(); 132 | 133 | let commitment = Commitment::commit( 134 | alpha_state.clone().into(), 135 | beta_state.clone().into(), 136 | &y.public_key, 137 | ); 138 | 139 | let state = Alice0 { 140 | alpha_state, 141 | beta_state, 142 | y, 143 | }; 144 | 145 | let message = Message0 { 146 | commitment, 147 | bulletproof_round_1_alice, 148 | }; 149 | 150 | (state, message) 151 | } 152 | 153 | pub fn opening(&self) -> Opening { 154 | Opening::new( 155 | self.alpha_state.clone().into(), 156 | self.beta_state.clone().into(), 157 | self.y.public_key, 158 | ) 159 | } 160 | } 161 | 162 | pub struct Alice1 { 163 | alpha_state: AL, 164 | beta_state: BL, 165 | y: KeyPair, 166 | } 167 | 168 | impl Alice1 { 169 | pub fn receive( 170 | self, 171 | message: Message3<(grin::RedeemerSigs, bulletproof::Round2), bitcoin::EncryptedSignature>, 172 | ) -> anyhow::Result<( 173 | Alice2, 174 | Message4, 175 | )> { 176 | let (grin_state, grin_redeem_encsig) = self.alpha_state.transition( 177 | message.alpha_redeemer_sigs.0, 178 | &self.y, 179 | message.alpha_redeemer_sigs.1, 180 | )?; 181 | let bitcoin_state = self 182 | .beta_state 183 | .transition(message.beta_redeem_encsig, &self.y)?; 184 | 185 | let state = Alice2 { 186 | alpha_state: grin_state, 187 | beta_state: bitcoin_state, 188 | }; 189 | 190 | let message = Message4 { 191 | alpha_redeem_encsig: grin_redeem_encsig, 192 | }; 193 | 194 | Ok((state, message)) 195 | } 196 | } 197 | 198 | impl Alice1 { 199 | pub fn receive( 200 | self, 201 | Message3 { 202 | alpha_redeemer_sigs: bob_bitcoin_refund_signature, 203 | beta_redeem_encsig: grin_redeem_encsig, 204 | .. 205 | }: Message3, 206 | ) -> anyhow::Result<( 207 | Alice2, 208 | Message4, 209 | )> { 210 | let (bitcoin_state, bitcoin_redeem_encsig) = self 211 | .alpha_state 212 | .transition(bob_bitcoin_refund_signature, &self.y)?; 213 | let grin_state = self.beta_state.transition(self.y, grin_redeem_encsig)?; 214 | 215 | let state = Alice2 { 216 | alpha_state: bitcoin_state, 217 | beta_state: grin_state, 218 | }; 219 | 220 | let message = Message4 { 221 | alpha_redeem_encsig: bitcoin_redeem_encsig, 222 | }; 223 | 224 | Ok((state, message)) 225 | } 226 | } 227 | 228 | pub struct Alice2 { 229 | pub alpha_state: AL, 230 | pub beta_state: BL, 231 | } 232 | -------------------------------------------------------------------------------- /src/grin/wallet.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | grin::{event, Signature}, 3 | keypair::{random_secret_key, SECP}, 4 | look_for::LookFor, 5 | }; 6 | use grin_chain::Chain; 7 | use grin_core::core::{Input, Output, OutputFeatures, Transaction, TxKernel}; 8 | use grin_util::ZeroingString; 9 | use grin_wallet_impls::{ 10 | test_framework::{ 11 | award_block_to_wallet, award_blocks_to_wallet, wallet_info, LocalWalletClient, WalletProxy, 12 | }, 13 | DefaultLCProvider, DefaultWalletImpl, 14 | }; 15 | use grin_wallet_libwallet::{InitTxArgs, IssueInvoiceTxArgs, NodeClient, Slate, WalletInst}; 16 | use grin_wallet_util::{grin_keychain::ExtKeychain, grin_util::Mutex}; 17 | use secp256k1zkp::{pedersen::Commitment, SecretKey}; 18 | use std::{sync::Arc, thread}; 19 | 20 | pub struct Node { 21 | chain_dir: String, 22 | wallet: Wallet, 23 | } 24 | 25 | impl Node { 26 | pub fn start() -> anyhow::Result<(Self, Wallets)> { 27 | let chain_dir = "target/test_output/"; 28 | 29 | let _ = std::fs::remove_dir_all(chain_dir); 30 | let mut wallet_proxy: WalletProxy< 31 | DefaultLCProvider, 32 | LocalWalletClient, 33 | ExtKeychain, 34 | > = WalletProxy::new(chain_dir); 35 | 36 | let mut wallets = Vec::new(); 37 | for id in vec!["node", "funder", "redeemer"].iter() { 38 | let node_client = LocalWalletClient::new(id, wallet_proxy.tx.clone()); 39 | let mut wallet = Box::new( 40 | DefaultWalletImpl::::new(node_client.clone()) 41 | .map_err(|e| anyhow::anyhow!("failed to instantiate Grin wallet: {}", e))?, 42 | ) 43 | as Box< 44 | dyn WalletInst< 45 | DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, 46 | LocalWalletClient, 47 | ExtKeychain, 48 | >, 49 | >; 50 | let lc_provider = wallet.lc_provider().map_err(|e| { 51 | anyhow::anyhow!("failed to get stored instance of lifecycle provider: {}", e) 52 | })?; 53 | lc_provider 54 | .set_top_level_directory(&format!("{}/{}", "target/test_output/", id)) 55 | .map_err(|e| anyhow::anyhow!("failed to set top level directory: {}", e))?; 56 | lc_provider 57 | .create_wallet(None, None, 32, ZeroingString::from(""), false) 58 | .map_err(|e| anyhow::anyhow!("failed to create Grin wallet: {}", e))?; 59 | let mask = lc_provider 60 | .open_wallet(None, ZeroingString::from(""), false, false) 61 | .map_err(|e| anyhow::anyhow!("failed to open Grin wallet: {}", e))?; 62 | 63 | let wallet = Arc::new(Mutex::new(wallet)); 64 | wallet_proxy.add_wallet( 65 | &id, 66 | node_client.get_send_instance(), 67 | wallet.clone(), 68 | mask.clone(), 69 | ); 70 | 71 | let chain = wallet_proxy.chain.clone(); 72 | 73 | let wallet = Wallet { 74 | inner: wallet, 75 | node_client, 76 | mask, 77 | chain, 78 | }; 79 | 80 | wallets.push(wallet) 81 | } 82 | 83 | thread::spawn({ 84 | move || { 85 | if let Err(e) = wallet_proxy.run() { 86 | panic!("Wallet Proxy error: {}", e); 87 | } 88 | } 89 | }); 90 | 91 | let node = Self { 92 | chain_dir: String::from(chain_dir), 93 | wallet: wallets.remove(0), 94 | }; 95 | 96 | let funder_wallet = wallets.remove(0); 97 | let redeemer_wallet = wallets.remove(0); 98 | 99 | node.award_60_grin(&funder_wallet)?; 100 | 101 | Ok((node, Wallets { 102 | funder_wallet, 103 | redeemer_wallet, 104 | })) 105 | } 106 | 107 | // 1 block reward (60 grin) is spendable after 4 blocks have been mined 108 | fn award_60_grin(&self, wallet: &Wallet) -> anyhow::Result<()> { 109 | award_block_to_wallet( 110 | wallet.chain.as_ref(), 111 | Vec::new(), 112 | wallet.inner.clone(), 113 | wallet.mask.as_ref(), 114 | ) 115 | .map_err(|e| anyhow::anyhow!("could not award grin to wallet: {}", e))?; 116 | 117 | for _ in 1..4 { 118 | award_blocks_to_wallet( 119 | self.wallet.chain.as_ref(), 120 | self.wallet.inner.clone(), 121 | self.wallet.mask.as_ref(), 122 | 3, 123 | false, 124 | ) 125 | .map_err(|e| anyhow::anyhow!("could not award grin to wallet: {}", e))?; 126 | } 127 | 128 | Ok(()) 129 | } 130 | 131 | pub fn kill(&self) { 132 | let _ = std::fs::remove_dir_all(self.chain_dir.clone()); 133 | } 134 | } 135 | 136 | pub struct Wallets { 137 | pub funder_wallet: Wallet, 138 | pub redeemer_wallet: Wallet, 139 | } 140 | 141 | #[allow(clippy::type_complexity)] 142 | pub struct Wallet { 143 | inner: Arc< 144 | Mutex< 145 | Box< 146 | dyn WalletInst< 147 | 'static, 148 | DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>, 149 | LocalWalletClient, 150 | ExtKeychain, 151 | > + 'static, 152 | >, 153 | >, 154 | >, 155 | node_client: LocalWalletClient, 156 | mask: Option, 157 | chain: Arc, 158 | } 159 | 160 | impl Wallet { 161 | pub fn get_chain_tip(&self) -> anyhow::Result { 162 | self.node_client 163 | .get_chain_tip() 164 | .map(|(tip, _)| tip) 165 | .map_err(|e| anyhow::anyhow!("could not get Grin chain tip: {}", e)) 166 | } 167 | 168 | pub fn process_invoice(&self, slate: Slate) -> anyhow::Result { 169 | let mut processed_slate = Slate::blank(2); 170 | grin_wallet_controller::controller::owner_single_use( 171 | self.inner.clone(), 172 | self.mask.as_ref(), 173 | |api, m| { 174 | let args = InitTxArgs { 175 | src_acct_name: None, 176 | amount: slate.amount, 177 | minimum_confirmations: 1, 178 | max_outputs: 500, 179 | num_change_outputs: 1, 180 | selection_strategy_is_use_all: true, 181 | ..Default::default() 182 | }; 183 | processed_slate = api.process_invoice_tx(m, &slate, args)?; 184 | api.tx_lock_outputs(m, &processed_slate, 0)?; 185 | Ok(()) 186 | }, 187 | ) 188 | .map(|_| processed_slate) 189 | .map_err(|e| anyhow::anyhow!("could not process invoice: {}", e)) 190 | } 191 | pub fn post_transaction(&self, transaction: Transaction) -> anyhow::Result<()> { 192 | grin_wallet_controller::controller::owner_single_use( 193 | self.inner.clone(), 194 | self.mask.as_ref(), 195 | |api, m| { 196 | api.post_tx(m, &transaction, false)?; 197 | Ok(()) 198 | }, 199 | ) 200 | .map_err(|e| anyhow::anyhow!("could not post transaction: {}", e)) 201 | } 202 | 203 | pub fn issue_invoice(&self, amount: u64) -> anyhow::Result { 204 | let mut invoice_slate = Slate::blank(2); 205 | grin_wallet_controller::controller::owner_single_use( 206 | self.inner.clone(), 207 | self.mask.as_ref(), 208 | |api, m| { 209 | let args = IssueInvoiceTxArgs { 210 | amount, 211 | ..Default::default() 212 | }; 213 | invoice_slate = api.issue_invoice_tx(m, args)?; 214 | Ok(()) 215 | }, 216 | ) 217 | .map(|_| invoice_slate) 218 | .map_err(|e| anyhow::anyhow!("could not issue invoice: {}", e)) 219 | } 220 | 221 | pub fn finalize_invoice(&self, slate: Slate) -> anyhow::Result { 222 | let mut finalized_slate = Slate::blank(2); 223 | grin_wallet_controller::controller::foreign_single_use( 224 | self.inner.clone(), 225 | self.mask.clone(), 226 | |api| { 227 | finalized_slate = api.finalize_invoice_tx(&slate)?; 228 | Ok(()) 229 | }, 230 | ) 231 | .map(|_| finalized_slate.tx) 232 | .map_err(|e| anyhow::anyhow!("could not finalize invoice: {}", e)) 233 | } 234 | 235 | pub fn get_balance(&self) -> anyhow::Result { 236 | wallet_info(self.inner.clone(), self.mask.as_ref()) 237 | .map(|info| info.amount_currently_spendable) 238 | .map_err(|e| anyhow::anyhow!("failed to access wallet balance: {}", e)) 239 | } 240 | 241 | pub fn find_kernel(&self, excess: &Commitment) -> anyhow::Result { 242 | self.chain 243 | .get_kernel_height(&excess, None, None) 244 | .map_err(|e| anyhow::anyhow!("failed to search for kernel: {}", e))? 245 | .map(|(kernel, ..)| kernel) 246 | .ok_or_else(|| anyhow::anyhow!("could not find kernel for commitment: {:?}", excess)) 247 | } 248 | } 249 | 250 | impl LookFor for Wallet { 251 | type Event = event::Redeem; 252 | type Extract = Signature; 253 | 254 | fn look_for(&self, event: Self::Event) -> anyhow::Result { 255 | let kernel = self.find_kernel(&event.excess)?; 256 | 257 | Ok(kernel.excess_sig) 258 | } 259 | } 260 | 261 | pub fn build_input(amount: u64, secret_key: &SecretKey) -> anyhow::Result { 262 | let commit = SECP 263 | .commit(amount, secret_key.clone()) 264 | .map_err(|e| anyhow::anyhow!("failed to build Pedersen commitment: {}", e))?; 265 | 266 | Ok(Input { 267 | features: OutputFeatures::Plain, 268 | commit, 269 | }) 270 | } 271 | 272 | pub fn build_output(amount: u64, secret_key: &SecretKey) -> anyhow::Result { 273 | let commit = SECP 274 | .commit(amount, secret_key.clone()) 275 | .map_err(|e| anyhow::anyhow!("failed to build Pedersen commitment: {}", e))?; 276 | 277 | // These are just used for random number generation inside bullet proof C 278 | let rewind_nonce = random_secret_key(); 279 | let private_nonce = random_secret_key(); 280 | 281 | let proof = SECP.bullet_proof( 282 | amount, 283 | secret_key.clone(), 284 | rewind_nonce, 285 | private_nonce, 286 | None, 287 | None, 288 | ); 289 | 290 | Ok(Output { 291 | features: OutputFeatures::Plain, 292 | commit, 293 | proof, 294 | }) 295 | } 296 | -------------------------------------------------------------------------------- /src/grin/sign.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | grin::{ 3 | action, bulletproof, compute_excess_pk, compute_excess_sk, compute_offset, KernelFeatures, 4 | Offer, PKs, SKs, SpecialOutputKeyPairsFunder, SpecialOutputKeyPairsRedeemer, 5 | SpecialOutputs, 6 | }, 7 | keypair::{random_secret_key, KeyPair, PublicKey, SECP}, 8 | schnorr, 9 | }; 10 | 11 | pub struct RedeemerSigs { 12 | pub s_fund: schnorr::PartialSignature, 13 | pub s_refund: schnorr::PartialSignature, 14 | pub s_hat_redeem: schnorr::PartialEncryptedSignature, 15 | } 16 | 17 | #[allow(clippy::too_many_arguments)] 18 | pub fn redeemer( 19 | offer: &Offer, 20 | special_outputs: &SpecialOutputs, 21 | special_output_keypairs_redeemer: &SpecialOutputKeyPairsRedeemer, 22 | redeemer_SKs: &SKs, 23 | funder_PKs: &PKs, 24 | Y: &PublicKey, 25 | bulletproof_common_nonce: &bulletproof::CommonNonce, 26 | bulletproof_round_1_redeemer: &bulletproof::Round1, 27 | bulletproof_round_1_funder: &bulletproof::Round1, 28 | ) -> anyhow::Result<(RedeemerSigs, bulletproof::Round2)> { 29 | let (s_fund, bulletproof_round_2_redeemer) = { 30 | let offset = compute_offset(&funder_PKs.R_fund, &redeemer_SKs.r_fund.public_key)?; 31 | 32 | let half_excess_keypair_redeemer = KeyPair::new(compute_excess_sk( 33 | vec![], 34 | vec![&redeemer_SKs.x.secret_key], 35 | Some(&offset), 36 | )?); 37 | 38 | let s_fund = { 39 | let half_excess_pk_funder = compute_excess_pk( 40 | vec![&special_outputs.fund_input_key], 41 | vec![&funder_PKs.X], 42 | None, 43 | )?; 44 | 45 | schnorr::sign_2p_0( 46 | &half_excess_keypair_redeemer, 47 | &redeemer_SKs.r_fund, 48 | &half_excess_pk_funder, 49 | &funder_PKs.R_fund, 50 | &KernelFeatures::Plain { fee: 0 }.kernel_sig_msg()?, 51 | )? 52 | }; 53 | 54 | let bulletproof_round_2_redeemer = { 55 | let excess_pk = PublicKey::from_combination(&*SECP, vec![ 56 | &redeemer_SKs.x.public_key, 57 | &funder_PKs.X, 58 | ])?; 59 | 60 | bulletproof::Round2::new( 61 | &redeemer_SKs.x.secret_key, 62 | &redeemer_SKs.x.secret_key, 63 | &excess_pk, 64 | offer.fund_output_amount(), 65 | &bulletproof_common_nonce, 66 | &bulletproof_round_1_redeemer, 67 | &bulletproof_round_1_funder, 68 | )? 69 | }; 70 | 71 | (s_fund, bulletproof_round_2_redeemer) 72 | }; 73 | 74 | let s_refund = { 75 | let offset = compute_offset(&funder_PKs.R_refund, &redeemer_SKs.r_refund.public_key)?; 76 | 77 | let half_excess_keypair_redeemer = { 78 | let half_excess_sk_redeemer = 79 | compute_excess_sk(vec![&redeemer_SKs.x.secret_key], vec![], Some(&offset))?; 80 | KeyPair::new(half_excess_sk_redeemer) 81 | }; 82 | 83 | let half_excess_pk_funder = compute_excess_pk( 84 | vec![&funder_PKs.X], 85 | vec![&special_outputs.refund_output_key], 86 | None, 87 | )?; 88 | 89 | schnorr::sign_2p_0( 90 | &half_excess_keypair_redeemer, 91 | &redeemer_SKs.r_refund, 92 | &half_excess_pk_funder, 93 | &funder_PKs.R_refund, 94 | &KernelFeatures::HeightLocked { 95 | fee: 0, 96 | lock_height: offer.expiry, 97 | } 98 | .kernel_sig_msg()?, 99 | )? 100 | }; 101 | 102 | let s_hat_redeem = { 103 | let offset = compute_offset(&funder_PKs.R_redeem, &redeemer_SKs.r_redeem.public_key)?; 104 | 105 | let half_excess_keypair_redeemer = KeyPair::new(compute_excess_sk( 106 | vec![&redeemer_SKs.x.secret_key], 107 | vec![ 108 | &special_output_keypairs_redeemer 109 | .redeem_output_key 110 | .secret_key, 111 | ], 112 | Some(&offset), 113 | )?); 114 | 115 | let half_excess_pk_funder = compute_excess_pk(vec![&funder_PKs.X], vec![], None)?; 116 | 117 | schnorr::encsign_2p_0( 118 | &half_excess_keypair_redeemer, 119 | &redeemer_SKs.r_redeem, 120 | &half_excess_pk_funder, 121 | &funder_PKs.R_redeem, 122 | &Y, 123 | &KernelFeatures::Plain { fee: 0 }.kernel_sig_msg()?, 124 | )? 125 | }; 126 | 127 | Ok(( 128 | RedeemerSigs { 129 | s_fund, 130 | s_refund, 131 | s_hat_redeem, 132 | }, 133 | bulletproof_round_2_redeemer, 134 | )) 135 | } 136 | 137 | #[derive(Debug, Clone, thiserror::Error)] 138 | pub enum RedeemerSignatureError { 139 | #[error("fund")] 140 | Fund, 141 | #[error("redeem")] 142 | Redeem, 143 | #[error("refund")] 144 | Refund, 145 | } 146 | 147 | pub struct FunderActions { 148 | pub fund: action::Fund, 149 | pub refund: action::Refund, 150 | } 151 | 152 | #[allow(clippy::too_many_arguments)] 153 | pub fn funder( 154 | offer: &Offer, 155 | special_outputs: &SpecialOutputs, 156 | special_output_keypairs_funder: &SpecialOutputKeyPairsFunder, 157 | funder_SKs: &SKs, 158 | redeemer_PKs: &PKs, 159 | Y: &PublicKey, 160 | RedeemerSigs { 161 | s_fund: s_fund_redeemer, 162 | s_refund: s_refund_redeemer, 163 | s_hat_redeem: s_hat_redeem_redeemer, 164 | }: RedeemerSigs, 165 | bulletproof_common_nonce: &bulletproof::CommonNonce, 166 | bulletproof_round_1_redeemer: &bulletproof::Round1, 167 | bulletproof_round_1_funder: &bulletproof::Round1, 168 | bulletproof_round_2_redeemer: &bulletproof::Round2, 169 | ) -> anyhow::Result<(FunderActions, schnorr::EncryptedSignature)> { 170 | let X = PublicKey::from_combination(&*SECP, vec![&redeemer_PKs.X, &funder_SKs.x.public_key])?; 171 | 172 | let fund = { 173 | let offset = compute_offset(&funder_SKs.r_fund.public_key, &redeemer_PKs.R_fund)?; 174 | 175 | let half_excess_keypair_funder = KeyPair::new(compute_excess_sk( 176 | vec![&special_output_keypairs_funder.fund_input_key.secret_key], 177 | vec![&funder_SKs.x.secret_key], 178 | None, 179 | )?); 180 | 181 | let half_excess_pk_redeemer = 182 | compute_excess_pk(vec![], vec![&redeemer_PKs.X], Some(&offset))?; 183 | 184 | let kernel_features = KernelFeatures::Plain { fee: 0 }; 185 | 186 | let (excess_sig, excess_pk) = schnorr::sign_2p_1( 187 | &half_excess_keypair_funder, 188 | &funder_SKs.r_fund, 189 | &half_excess_pk_redeemer, 190 | &redeemer_PKs.R_fund, 191 | &kernel_features.kernel_sig_msg()?, 192 | &s_fund_redeemer, 193 | ) 194 | .map_err(|_| RedeemerSignatureError::Fund)?; 195 | 196 | let bulletproof = { 197 | let bulletproof_round_2_funder = bulletproof::Round2::new( 198 | &funder_SKs.x.secret_key, 199 | &funder_SKs.x.secret_key, 200 | &X, 201 | offer.fund_output_amount(), 202 | &bulletproof_common_nonce, 203 | &bulletproof_round_1_redeemer, 204 | &bulletproof_round_1_funder, 205 | )?; 206 | bulletproof::Round3::new( 207 | &funder_SKs.x.secret_key, 208 | &funder_SKs.x.secret_key, 209 | &X, 210 | offer.fund_output_amount(), 211 | &bulletproof_common_nonce, 212 | &bulletproof_round_1_redeemer, 213 | &bulletproof_round_1_funder, 214 | &bulletproof_round_2_redeemer, 215 | &bulletproof_round_2_funder, 216 | )? 217 | .bulletproof 218 | }; 219 | 220 | action::Fund::new( 221 | vec![( 222 | offer.fund_output_amount(), 223 | special_output_keypairs_funder.fund_input_key.public_key, 224 | )], 225 | vec![(offer.fund_output_amount(), X, bulletproof)], 226 | excess_pk, 227 | excess_sig, 228 | kernel_features, 229 | offset, 230 | ( 231 | offer.fund_output_amount(), 232 | special_output_keypairs_funder.fund_input_key.clone(), 233 | ), 234 | )? 235 | }; 236 | 237 | let refund = { 238 | let offset = compute_offset(&funder_SKs.r_refund.public_key, &redeemer_PKs.R_refund)?; 239 | 240 | let half_excess_keypair_funder = KeyPair::new(compute_excess_sk( 241 | vec![&funder_SKs.x.secret_key], 242 | vec![&special_output_keypairs_funder.refund_output_key.secret_key], 243 | None, 244 | )?); 245 | 246 | let half_excess_pk_redeemer = 247 | compute_excess_pk(vec![&redeemer_PKs.X], vec![], Some(&offset))?; 248 | 249 | let kernel_features = KernelFeatures::HeightLocked { 250 | fee: 0, 251 | lock_height: offer.expiry, 252 | }; 253 | 254 | let (excess_sig, excess) = schnorr::sign_2p_1( 255 | &half_excess_keypair_funder, 256 | &funder_SKs.r_refund, 257 | &half_excess_pk_redeemer, 258 | &redeemer_PKs.R_refund, 259 | &kernel_features.kernel_sig_msg()?, 260 | &s_refund_redeemer, 261 | ) 262 | .map_err(|_| RedeemerSignatureError::Refund)?; 263 | 264 | let bulletproof = SECP.bullet_proof( 265 | offer.fund_output_amount(), 266 | special_output_keypairs_funder 267 | .refund_output_key 268 | .secret_key 269 | .clone(), 270 | random_secret_key(), 271 | random_secret_key(), 272 | None, 273 | None, 274 | ); 275 | 276 | action::Refund::new( 277 | vec![(offer.fund_output_amount(), X)], 278 | vec![( 279 | offer.fund_output_amount(), 280 | special_output_keypairs_funder.refund_output_key.public_key, 281 | bulletproof, 282 | )], 283 | excess, 284 | excess_sig, 285 | kernel_features, 286 | offset, 287 | ( 288 | offer.fund_output_amount(), 289 | special_output_keypairs_funder.refund_output_key.clone(), 290 | ), 291 | offer.fee, 292 | )? 293 | }; 294 | 295 | let encsign_redeem = { 296 | let offset = compute_offset(&funder_SKs.r_redeem.public_key, &redeemer_PKs.R_redeem)?; 297 | 298 | let half_excess_keypair_funder = KeyPair::new(compute_excess_sk( 299 | vec![&funder_SKs.x.secret_key], 300 | vec![], 301 | None, 302 | )?); 303 | 304 | let half_excess_pk_redeemer = compute_excess_pk( 305 | vec![&redeemer_PKs.X], 306 | vec![&special_outputs.redeem_output_key], 307 | Some(&offset), 308 | )?; 309 | 310 | schnorr::encsign_2p_1( 311 | &half_excess_keypair_funder, 312 | &funder_SKs.r_redeem, 313 | &half_excess_pk_redeemer, 314 | &redeemer_PKs.R_redeem, 315 | &Y, 316 | &KernelFeatures::Plain { fee: 0 }.kernel_sig_msg()?, 317 | &s_hat_redeem_redeemer, 318 | ) 319 | .map_err(|_| RedeemerSignatureError::Redeem)? 320 | }; 321 | 322 | Ok((FunderActions { fund, refund }, encsign_redeem)) 323 | } 324 | -------------------------------------------------------------------------------- /src/schnorr.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | grin, 3 | keypair::{KeyPair, Negate, PublicKey, SecretKey, XCoor, YCoor, SECP}, 4 | }; 5 | use secp256k1zkp::{aggsig, Message, Signature}; 6 | use std::convert::{TryFrom, TryInto}; 7 | 8 | pub type EncryptedSignature = Signature; 9 | pub type PartialEncryptedSignature = PartialSignature; 10 | 11 | #[derive(Debug, Clone)] 12 | pub struct PartialSignature(pub SecretKey); 13 | 14 | pub fn sign_2p_0( 15 | x0: &KeyPair, 16 | r0: &KeyPair, 17 | X1: &PublicKey, 18 | R1: &PublicKey, 19 | message: &Message, 20 | ) -> anyhow::Result { 21 | let R = PublicKey::from_combination(&*SECP, vec![&r0.public_key, &R1])?; 22 | let X = PublicKey::from_combination(&*SECP, vec![&x0.public_key, &X1])?; 23 | 24 | grin::calculate_partial_sig( 25 | &*SECP, 26 | &x0.secret_key, 27 | &r0.secret_key, 28 | &R, 29 | Some(&X), 30 | &message, 31 | ) 32 | .map_err(|_| Error::CalculatePartialSig)? 33 | .try_into() 34 | } 35 | 36 | pub fn sign_2p_1( 37 | x1: &KeyPair, 38 | r1: &KeyPair, 39 | X0: &PublicKey, 40 | R0: &PublicKey, 41 | message: &Message, 42 | partial_sig_0: &PartialSignature, 43 | ) -> anyhow::Result<(Signature, PublicKey)> { 44 | let R = PublicKey::from_combination(&*SECP, vec![&r1.public_key, &R0])?; 45 | let X = PublicKey::from_combination(&*SECP, vec![&x1.public_key, &X0])?; 46 | 47 | let partial_sig_1 = PartialSignature::try_from( 48 | grin::calculate_partial_sig( 49 | &*SECP, 50 | &x1.secret_key, 51 | &r1.secret_key, 52 | &R, 53 | Some(&X), 54 | message, 55 | ) 56 | .map_err(|_| Error::CalculatePartialSig)?, 57 | )?; 58 | 59 | let sig = { 60 | let mut sig = partial_sig_0.0.clone(); 61 | sig.add_assign(&*SECP, &partial_sig_1.0)?; 62 | 63 | PartialSignature(sig).to_signature(&R)? 64 | }; 65 | 66 | if !aggsig::verify_single(&*SECP, &sig, message, None, &X, Some(&X), None, false) { 67 | return Err(Error::VerifySig.into()); 68 | } 69 | 70 | Ok((sig, X)) 71 | } 72 | 73 | pub fn encsign_2p_0( 74 | x0: &KeyPair, 75 | r0: &KeyPair, 76 | X1: &PublicKey, 77 | R1: &PublicKey, 78 | Y: &PublicKey, 79 | message: &Message, 80 | ) -> anyhow::Result { 81 | let R = PublicKey::from_combination(&*SECP, vec![&r0.public_key, &R1, &Y])?; 82 | 83 | let X = PublicKey::from_combination(&*SECP, vec![&x0.public_key, &X1])?; 84 | 85 | grin::calculate_partial_sig( 86 | &*SECP, 87 | &x0.secret_key, 88 | &r0.secret_key, 89 | &R, 90 | Some(&X), 91 | &message, 92 | ) 93 | .map_err(|_| Error::CalculatePartialEncSig)? 94 | .try_into() 95 | } 96 | 97 | pub fn encsign_2p_1( 98 | x1: &KeyPair, 99 | r1: &KeyPair, 100 | X0: &PublicKey, 101 | R0: &PublicKey, 102 | Y: &PublicKey, 103 | message: &Message, 104 | partial_encsig_0: &PartialEncryptedSignature, 105 | ) -> anyhow::Result { 106 | let R_hat = PublicKey::from_combination(&*SECP, vec![&r1.public_key, &R0])?; 107 | let R = PublicKey::from_combination(&*SECP, vec![&R_hat, &Y])?; 108 | 109 | let X = PublicKey::from_combination(&*SECP, vec![&x1.public_key, &X0])?; 110 | 111 | let partial_encsig_1 = PartialEncryptedSignature::try_from( 112 | grin::calculate_partial_sig( 113 | &*SECP, 114 | &x1.secret_key, 115 | &r1.secret_key, 116 | &R, 117 | Some(&X), 118 | message, 119 | ) 120 | .map_err(|_| Error::CalculatePartialEncSig)?, 121 | )?; 122 | 123 | let encsig = { 124 | let mut sig = partial_encsig_0.0.clone(); 125 | sig.add_assign(&*SECP, &partial_encsig_1.0)?; 126 | 127 | PartialSignature(sig).to_signature(&R_hat)? 128 | }; 129 | 130 | if !aggsig::verify_single(&*SECP, &encsig, message, Some(&R), &X, Some(&X), None, true) { 131 | return Err(Error::VerifySig.into()); 132 | } 133 | 134 | Ok(encsig) 135 | } 136 | 137 | // TODO: Should be able to get R_hat from encsig 138 | pub fn decsig( 139 | y: &KeyPair, 140 | encsig: &EncryptedSignature, 141 | R_hat: &PublicKey, 142 | ) -> anyhow::Result { 143 | // let mut R_hat_x = [0u8; 32]; 144 | // R_hat_x.copy_from_slice(&encsig.as_ref()[0..32]); 145 | let R = PublicKey::from_combination(&*SECP, vec![&R_hat, &y.public_key])?; 146 | 147 | let mut s_hat = [0u8; 32]; 148 | s_hat.copy_from_slice(&encsig.as_ref()[32..64]); 149 | let mut s = SecretKey::from_slice(&*SECP, &s_hat)?; 150 | s.add_assign(&*SECP, &y.secret_key)?; 151 | 152 | let mut buffer = [0u8; 64]; 153 | buffer[0..32].copy_from_slice(&R.x_coor()[..]); 154 | buffer[32..64].copy_from_slice(&s.0[..]); 155 | Ok(Signature::from_raw_data(&buffer)?) 156 | } 157 | 158 | pub fn recover(sig: &Signature, recovery_key: &RecoveryKey) -> anyhow::Result { 159 | let s = SecretKey::from_slice(&*SECP, &sig.as_ref()[32..64])?; 160 | let s_hat = &recovery_key.0; 161 | 162 | let mut y = s; 163 | y.add_assign(&*SECP, &s_hat.negate())?; 164 | Ok(KeyPair::new(y)) 165 | } 166 | 167 | #[derive(Debug, Clone)] 168 | pub struct RecoveryKey(pub SecretKey); 169 | 170 | impl TryFrom for RecoveryKey { 171 | type Error = anyhow::Error; 172 | fn try_from(from: EncryptedSignature) -> anyhow::Result { 173 | Ok(RecoveryKey(SecretKey::from_slice( 174 | &*SECP, 175 | &from.as_ref()[32..64], 176 | )?)) 177 | } 178 | } 179 | 180 | impl TryFrom for PartialSignature { 181 | type Error = anyhow::Error; 182 | fn try_from(from: Signature) -> anyhow::Result { 183 | let mut s = [0u8; 32]; 184 | s.copy_from_slice(&from.as_ref()[32..64]); 185 | 186 | Ok(PartialSignature(SecretKey::from_slice(&*SECP, &s)?)) 187 | } 188 | } 189 | 190 | impl PartialSignature { 191 | pub fn to_signature(&self, R: &PublicKey) -> anyhow::Result { 192 | let mut sig = [0u8; 64]; 193 | sig[0..32].copy_from_slice(&R.x_coor()[..]); 194 | sig[32..64].copy_from_slice(&(self.0).0[..]); 195 | 196 | Ok(Signature::from_raw_data(&sig)?) 197 | } 198 | } 199 | 200 | #[derive(Debug, Clone, thiserror::Error)] 201 | pub enum Error { 202 | #[error("failed to calculate partial encsig")] 203 | CalculatePartialEncSig, 204 | #[error("failed to calculate partial sig")] 205 | CalculatePartialSig, 206 | #[error("failed to verify sig")] 207 | VerifySig, 208 | } 209 | 210 | // This is only used during tests when you have all the secret keys 211 | pub fn normalize_keypairs( 212 | r0: KeyPair, 213 | r1: KeyPair, 214 | y: KeyPair, 215 | ) -> anyhow::Result<(KeyPair, KeyPair, KeyPair)> { 216 | let R = 217 | PublicKey::from_combination(&*SECP, vec![&r0.public_key, &r1.public_key, &y.public_key])?; 218 | let mut R_y = purerust_secp256k1::curve::Field::default(); 219 | assert!(R_y.set_b32(&R.y_coor())); 220 | 221 | if !R_y.is_quad_var() { 222 | Ok((r0.negate(), r1.negate(), y.negate())) 223 | } else { 224 | Ok((r0, r1, y)) 225 | } 226 | } 227 | 228 | #[cfg(test)] 229 | mod test { 230 | use super::*; 231 | 232 | #[test] 233 | fn sign_and_verify() -> anyhow::Result<()> { 234 | let x0 = KeyPair::new_random(); 235 | let x1 = KeyPair::new_random(); 236 | let r0 = KeyPair::new_random(); 237 | let r1 = KeyPair::new_random(); 238 | 239 | let message = Message::from_slice(b"mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm")?; 240 | 241 | let partial_sig = sign_2p_0(&x0, &r0, &x1.public_key, &r1.public_key, &message)?; 242 | 243 | assert!(sign_2p_1( 244 | &x1, 245 | &r1, 246 | &x0.public_key, 247 | &r0.public_key, 248 | &message, 249 | &partial_sig, 250 | ) 251 | .is_ok()); 252 | 253 | Ok(()) 254 | } 255 | 256 | #[test] 257 | fn encsign_and_encverify() -> anyhow::Result<()> { 258 | let x0 = KeyPair::new_random(); 259 | let x1 = KeyPair::new_random(); 260 | let r0 = KeyPair::new_random(); 261 | let r1 = KeyPair::new_random(); 262 | 263 | let y = KeyPair::new_random(); 264 | 265 | let message = Message::from_slice(b"mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm")?; 266 | 267 | let partial_encsig = encsign_2p_0( 268 | &x0, 269 | &r0, 270 | &x1.public_key, 271 | &r1.public_key, 272 | &y.public_key, 273 | &message, 274 | )?; 275 | 276 | assert!(encsign_2p_1( 277 | &x1, 278 | &r1, 279 | &x0.public_key, 280 | &r0.public_key, 281 | &y.public_key, 282 | &message, 283 | &partial_encsig, 284 | ) 285 | .is_ok()); 286 | 287 | Ok(()) 288 | } 289 | 290 | #[test] 291 | fn encsign_and_decsig() -> anyhow::Result<()> { 292 | let x0 = KeyPair::new_random(); 293 | let x1 = KeyPair::new_random(); 294 | let r0 = KeyPair::new_random(); 295 | let r1 = KeyPair::new_random(); 296 | 297 | let y = KeyPair::new_random(); 298 | 299 | let (r0, r1, y) = normalize_keypairs(r0, r1, y)?; 300 | 301 | let message = Message::from_slice(b"mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm")?; 302 | 303 | let partial_encsig = encsign_2p_0( 304 | &x0, 305 | &r0, 306 | &x1.public_key, 307 | &r1.public_key, 308 | &y.public_key, 309 | &message, 310 | )?; 311 | 312 | let encsig = encsign_2p_1( 313 | &x1, 314 | &r1, 315 | &x0.public_key, 316 | &r0.public_key, 317 | &y.public_key, 318 | &message, 319 | &partial_encsig, 320 | )?; 321 | 322 | let R_hat = PublicKey::from_combination(&*SECP, vec![&r0.public_key, &r1.public_key])?; 323 | let sig = decsig(&y, &encsig, &R_hat)?; 324 | 325 | let X = PublicKey::from_combination(&*SECP, vec![&x0.public_key, &x1.public_key])?; 326 | 327 | assert!(aggsig::verify_single( 328 | &*SECP, 329 | &sig, 330 | &message, 331 | None, 332 | &X, 333 | Some(&X), 334 | None, 335 | false 336 | )); 337 | 338 | Ok(()) 339 | } 340 | 341 | #[test] 342 | fn recover_key_from_decrypted_signature() -> anyhow::Result<()> { 343 | let x0 = KeyPair::new_random(); 344 | let x1 = KeyPair::new_random(); 345 | let r0 = KeyPair::new_random(); 346 | let r1 = KeyPair::new_random(); 347 | 348 | let y = KeyPair::new_random(); 349 | 350 | let (r0, r1, y) = normalize_keypairs(r0, r1, y)?; 351 | 352 | let message = Message::from_slice(b"mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm")?; 353 | 354 | let partial_encsig = encsign_2p_0( 355 | &x0, 356 | &r0, 357 | &x1.public_key, 358 | &r1.public_key, 359 | &y.public_key, 360 | &message, 361 | )?; 362 | 363 | let encsig = encsign_2p_1( 364 | &x1, 365 | &r1, 366 | &x0.public_key, 367 | &r0.public_key, 368 | &y.public_key, 369 | &message, 370 | &partial_encsig, 371 | )?; 372 | 373 | let R_hat = PublicKey::from_combination(&*SECP, vec![&r0.public_key, &r1.public_key])?; 374 | let sig = decsig(&y, &encsig, &R_hat)?; 375 | 376 | let rec_key = RecoveryKey::try_from(encsig)?; 377 | let y_tag = recover(&sig, &rec_key)?; 378 | 379 | assert_eq!(y.secret_key, y_tag.secret_key); 380 | 381 | Ok(()) 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /src/grin/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::keypair::{KeyPair, Negate, PublicKey, SecretKey, XCoor, YCoor, G, SECP}; 2 | use secp256k1zkp::{key::ZERO_KEY, pedersen}; 3 | use sha2::{Digest, Sha256}; 4 | 5 | pub mod action; 6 | pub mod alice; 7 | pub mod bob; 8 | pub mod bulletproof; 9 | pub mod event; 10 | pub mod keygen; 11 | pub mod keys; 12 | pub mod offer; 13 | pub mod sign; 14 | pub mod special_outputs; 15 | pub mod wallet; 16 | 17 | pub use crate::{ 18 | grin::{ 19 | alice::*, 20 | bob::*, 21 | keygen::keygen, 22 | keys::{PKs, SKs}, 23 | offer::Offer, 24 | sign::FunderActions, 25 | special_outputs::*, 26 | wallet::{Node, Wallet, Wallets}, 27 | }, 28 | schnorr::EncryptedSignature, 29 | }; 30 | pub use grin_core::{ 31 | core::KernelFeatures, 32 | libtx::aggsig::{add_signatures, calculate_partial_sig, verify_partial_sig}, 33 | }; 34 | pub use secp256k1zkp::Signature; 35 | pub use sign::RedeemerSigs; 36 | 37 | #[derive(Clone)] 38 | pub struct Funder0 { 39 | pub offer: Offer, 40 | pub special_outputs: SpecialOutputs, 41 | pub special_output_keypairs_funder: SpecialOutputKeyPairsFunder, 42 | pub SKs_self: SKs, 43 | } 44 | 45 | impl Funder0 { 46 | pub fn new( 47 | offer: Offer, 48 | special_outputs: SpecialOutputs, 49 | special_output_keypairs_funder: SpecialOutputKeyPairsFunder, 50 | ) -> Self { 51 | let SKs_self = keygen(); 52 | 53 | Self { 54 | offer, 55 | special_outputs, 56 | special_output_keypairs_funder, 57 | SKs_self, 58 | } 59 | } 60 | } 61 | 62 | pub struct Funder1 { 63 | pub offer: Offer, 64 | pub special_outputs: SpecialOutputs, 65 | pub special_output_keypairs_funder: SpecialOutputKeyPairsFunder, 66 | pub SKs_self: SKs, 67 | pub PKs_other: PKs, 68 | pub bulletproof_common_nonce: bulletproof::CommonNonce, 69 | pub bulletproof_round_1_self: bulletproof::Round1, 70 | pub bulletproof_round_1_other: bulletproof::Round1, 71 | } 72 | 73 | impl Funder1 { 74 | pub fn transition( 75 | self, 76 | redeemer_sigs: RedeemerSigs, 77 | Y: &PublicKey, 78 | bulletproof_round_2_other: bulletproof::Round2, 79 | ) -> anyhow::Result<(Funder2, EncryptedSignature)> { 80 | let (FunderActions { fund, refund }, redeem_encsig) = sign::funder( 81 | &self.offer, 82 | &self.special_outputs, 83 | &self.special_output_keypairs_funder, 84 | &self.SKs_self, 85 | &self.PKs_other, 86 | &Y, 87 | redeemer_sigs, 88 | &self.bulletproof_common_nonce, 89 | &self.bulletproof_round_1_other, 90 | &self.bulletproof_round_1_self, 91 | &bulletproof_round_2_other, 92 | )?; 93 | 94 | let state = Funder2 { 95 | fund_action: fund, 96 | refund_action: refund, 97 | }; 98 | 99 | Ok((state, redeem_encsig)) 100 | } 101 | } 102 | 103 | pub struct Funder2 { 104 | pub fund_action: action::Fund, 105 | pub refund_action: action::Refund, 106 | } 107 | 108 | #[derive(Clone)] 109 | pub struct Redeemer0 { 110 | pub offer: Offer, 111 | pub special_outputs: SpecialOutputs, 112 | pub special_output_keypairs_redeemer: SpecialOutputKeyPairsRedeemer, 113 | pub SKs_self: SKs, 114 | } 115 | 116 | impl Redeemer0 { 117 | pub fn new( 118 | offer: Offer, 119 | special_outputs: SpecialOutputs, 120 | special_output_keypairs_redeemer: SpecialOutputKeyPairsRedeemer, 121 | ) -> Self { 122 | let SKs_self = keygen(); 123 | 124 | Self { 125 | offer, 126 | special_outputs, 127 | special_output_keypairs_redeemer, 128 | SKs_self, 129 | } 130 | } 131 | 132 | pub fn transition( 133 | self, 134 | bulletproof_common_nonce: bulletproof::CommonNonce, 135 | bulletproof_round_1_self: bulletproof::Round1, 136 | bulletproof_round_1_other: bulletproof::Round1, 137 | PKs_other: PKs, 138 | Y: PublicKey, 139 | ) -> anyhow::Result<(Redeemer1, RedeemerSigs, bulletproof::Round2)> { 140 | let (redeemer_sigs, bulletproof_round_2_self) = sign::redeemer( 141 | &self.offer, 142 | &self.special_outputs, 143 | &self.special_output_keypairs_redeemer, 144 | &self.SKs_self, 145 | &PKs_other, 146 | &Y, 147 | &bulletproof_common_nonce, 148 | &bulletproof_round_1_self, 149 | &bulletproof_round_1_other, 150 | )?; 151 | 152 | let state = Redeemer1 { 153 | offer: self.offer, 154 | special_outputs: self.special_outputs, 155 | special_output_keypairs_redeemer: self.special_output_keypairs_redeemer, 156 | SKs_self: self.SKs_self, 157 | PKs_other, 158 | }; 159 | 160 | Ok((state, redeemer_sigs, bulletproof_round_2_self)) 161 | } 162 | } 163 | 164 | pub struct Redeemer1 { 165 | pub offer: Offer, 166 | pub special_outputs: SpecialOutputs, 167 | pub special_output_keypairs_redeemer: SpecialOutputKeyPairsRedeemer, 168 | pub SKs_self: SKs, 169 | pub PKs_other: PKs, 170 | } 171 | 172 | impl Redeemer1 { 173 | pub fn transition( 174 | self, 175 | Y: PublicKey, 176 | redeem_encsig: EncryptedSignature, 177 | ) -> anyhow::Result { 178 | let encrypted_redeem_action = action::EncryptedRedeem::new( 179 | self.offer, 180 | self.special_outputs, 181 | self.special_output_keypairs_redeemer, 182 | self.SKs_self, 183 | self.PKs_other, 184 | Y, 185 | redeem_encsig, 186 | )?; 187 | 188 | Ok(Redeemer2 { 189 | encrypted_redeem_action, 190 | }) 191 | } 192 | } 193 | 194 | pub struct Redeemer2 { 195 | pub encrypted_redeem_action: action::EncryptedRedeem, 196 | } 197 | 198 | pub fn compute_excess_sk( 199 | inputs: Vec<&SecretKey>, 200 | outputs: Vec<&SecretKey>, 201 | offset: Option<&SecretKey>, 202 | ) -> anyhow::Result { 203 | // TODO: Since this lets you use ZERO_KEY I don't see why you need the match 204 | // statements you should be able to do it all sequentially? Or is it that 205 | // add_assign will work if self is ZERO_KEY but not the argument? (that 206 | // would be dumb). 207 | let mut total = match (inputs.clone(), outputs.clone()) { 208 | (inputs, outputs) if inputs.is_empty() && outputs.is_empty() => { 209 | return Err(anyhow::anyhow!("invalid arguments")) 210 | } 211 | (inputs, outputs) if inputs.is_empty() && !outputs.is_empty() => { 212 | let mut total = ZERO_KEY; 213 | for o in outputs.iter() { 214 | total.add_assign(&*SECP, o)?; 215 | } 216 | total 217 | } 218 | (inputs, outputs) if !inputs.is_empty() && outputs.is_empty() => { 219 | let negated_inputs: Vec = inputs.iter().map(|i| i.negate()).collect(); 220 | let negated_inputs: Vec<&SecretKey> = negated_inputs.iter().map(|i| i).collect(); 221 | 222 | let mut total = ZERO_KEY; 223 | for i in negated_inputs.iter() { 224 | total.add_assign(&*SECP, i)?; 225 | } 226 | total 227 | } 228 | _ => { 229 | let negated_inputs: Vec = inputs.iter().map(|i| i.negate()).collect(); 230 | let negated_inputs: Vec<&SecretKey> = negated_inputs.iter().map(|i| i).collect(); 231 | 232 | let mut total = ZERO_KEY; 233 | for i in negated_inputs.iter() { 234 | total.add_assign(&*SECP, i)?; 235 | } 236 | for o in outputs.iter() { 237 | total.add_assign(&*SECP, o)?; 238 | } 239 | total 240 | } 241 | }; 242 | match offset { 243 | Some(offset) => { 244 | total.add_assign(&*SECP, &offset.negate())?; 245 | Ok(total) 246 | } 247 | None => Ok(total), 248 | } 249 | } 250 | 251 | // NOTE: the error case here is were the rust is when the result is the identity 252 | pub fn compute_excess_pk( 253 | inputs: Vec<&PublicKey>, 254 | outputs: Vec<&PublicKey>, 255 | offset: Option<&SecretKey>, 256 | ) -> anyhow::Result { 257 | let total = match (inputs.clone(), outputs.clone()) { 258 | (inputs, outputs) if inputs.is_empty() && outputs.is_empty() => { 259 | return Err(anyhow::anyhow!("invalid arguments")) 260 | } 261 | (inputs, outputs) if inputs.is_empty() && !outputs.is_empty() => { 262 | PublicKey::from_combination(&*SECP, outputs)? 263 | } 264 | (inputs, outputs) if !inputs.is_empty() && outputs.is_empty() => { 265 | let negated_inputs: Vec = inputs.iter().map(|i| i.negate()).collect(); 266 | let negated_inputs: Vec<&PublicKey> = negated_inputs.iter().map(|i| i).collect(); 267 | PublicKey::from_combination(&*SECP, negated_inputs)? 268 | } 269 | _ => { 270 | let negated_inputs: Vec = inputs.iter().map(|i| i.negate()).collect(); 271 | let mut total: Vec<&PublicKey> = negated_inputs.iter().map(|i| i).collect(); 272 | total.extend(outputs); 273 | PublicKey::from_combination(&*SECP, total)? 274 | } 275 | }; 276 | match offset { 277 | Some(offset) => { 278 | let mut offsetG = *G; 279 | offsetG.mul_assign(&*SECP, &offset)?; 280 | Ok(PublicKey::from_combination(&*SECP, vec![ 281 | &total, 282 | &offsetG.negate(), 283 | ])?) 284 | } 285 | None => Ok(total), 286 | } 287 | } 288 | 289 | pub fn compute_offset(funder_R: &PublicKey, redeemer_R: &PublicKey) -> anyhow::Result { 290 | let mut hasher = Sha256::default(); 291 | 292 | // NOTE: the offset is any random value known only to the two parties so we 293 | // just hash two nonces together. Other parties should never discover this 294 | // representation of R. 295 | hasher.input(&funder_R.x_coor()); 296 | hasher.input(&redeemer_R.x_coor()); 297 | 298 | Ok(SecretKey::from_slice(&*SECP, &hasher.result())?) 299 | } 300 | 301 | pub fn normalize_redeem_keys_alice( 302 | r0: &mut KeyPair, 303 | R1: &mut PublicKey, 304 | y: &mut KeyPair, 305 | ) -> anyhow::Result<()> { 306 | let R = PublicKey::from_combination(&*SECP, vec![&r0.public_key, &R1, &y.public_key])?; 307 | let mut R_y = purerust_secp256k1::curve::Field::default(); 308 | assert!(R_y.set_b32(&R.y_coor())); 309 | 310 | // For grin, the R's y value must be a quadratic residue. If it's not we 311 | // negate all the keys that combine to produce R to flip it to the y 312 | // coordinate that is. 313 | if !R_y.is_quad_var() { 314 | *r0 = r0.negate(); 315 | *R1 = R1.negate(); 316 | *y = y.negate(); 317 | 318 | Ok(()) 319 | } else { 320 | Ok(()) 321 | } 322 | } 323 | 324 | pub fn normalize_redeem_keys_bob( 325 | R0: &mut PublicKey, 326 | r1: &mut KeyPair, 327 | Y: &mut PublicKey, 328 | ) -> anyhow::Result<()> { 329 | let R = PublicKey::from_combination(&*SECP, vec![&R0, &r1.public_key, &Y])?; 330 | let mut R_y = purerust_secp256k1::curve::Field::default(); 331 | assert!(R_y.set_b32(&R.y_coor())); 332 | 333 | if !R_y.is_quad_var() { 334 | *R0 = R0.negate(); 335 | *r1 = r1.negate(); 336 | *Y = Y.negate(); 337 | 338 | Ok(()) 339 | } else { 340 | Ok(()) 341 | } 342 | } 343 | 344 | /// The library secp256k1-zkp does not offer an API to transform a public key 345 | /// into a Pedersen commitment, which is why we have to define it ourselves 346 | pub fn public_key_to_pedersen_commitment(pk: &PublicKey) -> pedersen::Commitment { 347 | // In secp256k1-zkp, Pedersen commitments are represented as arrays of 33 348 | // bytes 349 | let mut buffer = [0u8; 33]; 350 | 351 | // The first byte is 0x08 if the y-coordinate of the corresponding public key is 352 | // a quadratic residue, and 0x09 if it is not 353 | let mut pk_y = purerust_secp256k1::curve::Field::default(); 354 | assert!(pk_y.set_b32(&pk.y_coor())); 355 | 356 | if pk_y.is_quad_var() { 357 | buffer[0] = 0x08; 358 | } else { 359 | buffer[0] = 0x09; 360 | } 361 | 362 | // The last 32 bytes are filled in by the x-coordinate of the corresponding 363 | // public key, but in reverse order 364 | let mut commit = [0u8; 32]; 365 | commit.copy_from_slice(&pk.0[0..32]); 366 | commit.reverse(); 367 | buffer[1..33].copy_from_slice(&commit); 368 | 369 | pedersen::Commitment::from_vec(buffer.to_vec()) 370 | } 371 | 372 | #[cfg(test)] 373 | mod test { 374 | use super::*; 375 | 376 | #[test] 377 | fn to_commitment_roundtrip() { 378 | let x = KeyPair::new_random(); 379 | let commit = public_key_to_pedersen_commitment(&x.public_key); 380 | 381 | let theirs = commit.to_pubkey(&*SECP).unwrap(); 382 | let ours = x.public_key; 383 | 384 | assert_eq!(theirs, ours); 385 | } 386 | 387 | #[test] 388 | fn to_commitment_vs_commit() { 389 | let x = KeyPair::new_random(); 390 | let ours = public_key_to_pedersen_commitment(&x.public_key); 391 | let theirs = SECP.commit(0, x.secret_key).unwrap(); 392 | 393 | assert_eq!(theirs, ours); 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /src/grin/action.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | grin::{ 3 | compute_excess_pk, compute_excess_sk, compute_offset, public_key_to_pedersen_commitment, 4 | wallet::{build_input, build_output}, 5 | Offer, PKs, SKs, SpecialOutputKeyPairsRedeemer, SpecialOutputs, Wallet, 6 | }, 7 | keypair::{random_secret_key, KeyPair, PublicKey, SecretKey, SECP}, 8 | schnorr, Execute, 9 | }; 10 | use anyhow::Context; 11 | use grin_core::core::{Input, KernelFeatures, Output, OutputFeatures, Transaction, TxKernel}; 12 | use grin_keychain::BlindingFactor; 13 | use grin_wallet_libwallet::{ParticipantData, Slate}; 14 | use secp256k1zkp::{aggsig, pedersen::RangeProof, Signature}; 15 | use std::convert::TryInto; 16 | 17 | pub struct Fund { 18 | transaction_from_special_input: Transaction, 19 | special_input: (u64, KeyPair), 20 | } 21 | 22 | impl Fund { 23 | pub fn new( 24 | inputs: Vec<(u64, PublicKey)>, 25 | outputs: Vec<(u64, PublicKey, RangeProof)>, 26 | excess: PublicKey, 27 | excess_sig: Signature, 28 | kernel_features: KernelFeatures, 29 | offset: SecretKey, 30 | special_input: (u64, KeyPair), 31 | ) -> anyhow::Result { 32 | Ok(Self { 33 | transaction_from_special_input: new_transaction( 34 | inputs, 35 | outputs, 36 | excess, 37 | excess_sig, 38 | kernel_features, 39 | offset, 40 | ) 41 | .context("fund")?, 42 | special_input, 43 | }) 44 | } 45 | } 46 | 47 | pub struct Refund { 48 | transaction_to_special_output: Transaction, 49 | special_output: (u64, KeyPair), 50 | wallet_transaction_fee: u64, 51 | } 52 | 53 | impl Refund { 54 | #[allow(clippy::too_many_arguments)] 55 | pub fn new( 56 | inputs: Vec<(u64, PublicKey)>, 57 | outputs: Vec<(u64, PublicKey, RangeProof)>, 58 | excess: PublicKey, 59 | excess_sig: Signature, 60 | kernel_features: KernelFeatures, 61 | offset: SecretKey, 62 | special_output: (u64, KeyPair), 63 | wallet_transaction_fee: u64, 64 | ) -> anyhow::Result { 65 | Ok(Self { 66 | transaction_to_special_output: new_transaction( 67 | inputs, 68 | outputs, 69 | excess, 70 | excess_sig, 71 | kernel_features, 72 | offset, 73 | ) 74 | .context("refund")?, 75 | special_output, 76 | wallet_transaction_fee, 77 | }) 78 | } 79 | } 80 | 81 | pub struct EncryptedRedeem { 82 | incomplete_transaction_to_special_output: 83 | Box anyhow::Result>, 84 | special_output: (u64, KeyPair), 85 | wallet_transaction_fee: u64, 86 | encsig: schnorr::EncryptedSignature, 87 | R_hat: PublicKey, 88 | } 89 | 90 | impl EncryptedRedeem { 91 | pub fn new( 92 | offer: Offer, 93 | special_outputs: SpecialOutputs, 94 | special_output_keypairs_redeemer: SpecialOutputKeyPairsRedeemer, 95 | redeemer_SKs: SKs, 96 | funder_PKs: PKs, 97 | Y: PublicKey, 98 | encsig: schnorr::EncryptedSignature, 99 | ) -> anyhow::Result { 100 | let offset = compute_offset(&funder_PKs.R_redeem, &redeemer_SKs.r_redeem.public_key)?; 101 | 102 | let excess_pk = compute_excess_pk( 103 | vec![&redeemer_SKs.x.public_key, &funder_PKs.X], 104 | vec![&special_outputs.redeem_output_key], 105 | Some(&offset), 106 | )?; 107 | 108 | let R_hat = PublicKey::from_combination(&*SECP, vec![ 109 | &redeemer_SKs.r_redeem.public_key, 110 | &funder_PKs.R_redeem, 111 | ])?; 112 | let R = PublicKey::from_combination(&*SECP, vec![&R_hat, &Y])?; 113 | 114 | let kernel_features = KernelFeatures::Plain { fee: 0 }; 115 | 116 | if !aggsig::verify_single( 117 | &*SECP, 118 | &encsig, 119 | &kernel_features.kernel_sig_msg()?, 120 | Some(&R), 121 | &excess_pk, 122 | Some(&excess_pk), 123 | None, 124 | true, 125 | ) { 126 | return Err(anyhow::anyhow!( 127 | "failed to verify Grin encrypted redeem signature" 128 | )); 129 | } 130 | 131 | let incomplete_transaction_to_special_output = { 132 | let inputs = vec![( 133 | offer.fund_output_amount(), 134 | PublicKey::from_combination(&*SECP, vec![ 135 | &redeemer_SKs.x.public_key, 136 | &funder_PKs.X, 137 | ])?, 138 | )]; 139 | 140 | let bulletproof = SECP.bullet_proof( 141 | offer.fund_output_amount(), 142 | special_output_keypairs_redeemer 143 | .redeem_output_key 144 | .secret_key 145 | .clone(), 146 | random_secret_key(), 147 | random_secret_key(), 148 | None, 149 | None, 150 | ); 151 | 152 | let outputs = vec![( 153 | offer.fund_output_amount(), 154 | special_output_keypairs_redeemer 155 | .redeem_output_key 156 | .public_key, 157 | bulletproof, 158 | )]; 159 | 160 | Box::new(move |excess_sig| { 161 | if !aggsig::verify_single( 162 | &*SECP, 163 | &excess_sig, 164 | &kernel_features.kernel_sig_msg()?, 165 | Some(&R), 166 | &excess_pk, 167 | Some(&excess_pk), 168 | None, 169 | false, 170 | ) { 171 | return Err(anyhow::anyhow!( 172 | "failed to verify Grin decrypted redeem signature" 173 | )); 174 | } 175 | 176 | new_transaction( 177 | inputs, 178 | outputs, 179 | excess_pk, 180 | excess_sig, 181 | kernel_features, 182 | offset, 183 | ) 184 | }) 185 | }; 186 | 187 | Ok(Self { 188 | incomplete_transaction_to_special_output, 189 | special_output: ( 190 | offer.fund_output_amount(), 191 | special_output_keypairs_redeemer.redeem_output_key, 192 | ), 193 | wallet_transaction_fee: offer.fee, 194 | encsig, 195 | R_hat, 196 | }) 197 | } 198 | 199 | pub fn decrypt(self, y: &KeyPair) -> anyhow::Result { 200 | let excess_sig = schnorr::decsig(&y, &self.encsig, &self.R_hat)?; 201 | 202 | let transaction_to_special_output = 203 | (self.incomplete_transaction_to_special_output)(excess_sig).context("redeem")?; 204 | 205 | Ok(Redeem { 206 | transaction_to_special_output, 207 | special_output: self.special_output, 208 | wallet_transaction_fee: self.wallet_transaction_fee, 209 | }) 210 | } 211 | } 212 | 213 | #[derive(Debug)] 214 | pub struct Redeem { 215 | transaction_to_special_output: Transaction, 216 | special_output: (u64, KeyPair), 217 | wallet_transaction_fee: u64, 218 | } 219 | 220 | fn new_transaction( 221 | inputs: Vec<(u64, PublicKey)>, 222 | outputs: Vec<(u64, PublicKey, RangeProof)>, 223 | excess_pk: PublicKey, 224 | excess_sig: Signature, 225 | kernel_features: KernelFeatures, 226 | offset: SecretKey, 227 | ) -> anyhow::Result { 228 | let inputs = inputs 229 | .iter() 230 | .map(|(amount, blind_pk)| { 231 | let amount_pk = SECP.commit_value(amount.clone())?.to_pubkey(&*SECP)?; 232 | let commit_pk = PublicKey::from_combination(&*SECP, vec![&amount_pk, &blind_pk])?; 233 | 234 | Ok(Input::new( 235 | OutputFeatures::Plain, 236 | public_key_to_pedersen_commitment(&commit_pk), 237 | )) 238 | }) 239 | .collect::, anyhow::Error>>()?; 240 | 241 | let outputs = outputs 242 | .iter() 243 | .map(|(amount, blind_pk, proof)| { 244 | let amount_pk = SECP.commit_value(amount.clone())?.to_pubkey(&*SECP)?; 245 | let commit = 246 | public_key_to_pedersen_commitment(&PublicKey::from_combination(&*SECP, vec![ 247 | &amount_pk, &blind_pk, 248 | ])?); 249 | 250 | Ok(Output { 251 | features: OutputFeatures::Plain, 252 | commit, 253 | proof: *proof, 254 | }) 255 | }) 256 | .collect::, anyhow::Error>>()?; 257 | 258 | let excess = public_key_to_pedersen_commitment(&excess_pk); 259 | 260 | let kernel = { 261 | TxKernel { 262 | excess, 263 | excess_sig, 264 | features: kernel_features, 265 | } 266 | }; 267 | 268 | let offset = BlindingFactor::from_secret_key(offset); 269 | let transaction = Transaction::new(inputs, outputs, vec![kernel]).with_offset(offset); 270 | 271 | Ok(transaction) 272 | } 273 | 274 | impl Execute for Fund { 275 | type Wallet = Wallet; 276 | type Return = u64; 277 | 278 | fn execute(self, wallet: &Self::Wallet) -> anyhow::Result { 279 | // Build invoice to pay to special output 280 | let (slate, r, blind_excess_keypair) = { 281 | let mut slate = Slate::blank(2); 282 | 283 | slate.amount = self.special_input.0; 284 | slate.height = wallet.get_chain_tip()?; 285 | 286 | slate.version_info.block_header_version = 3; 287 | slate.lock_height = 0; 288 | 289 | let special_output = build_output(slate.amount, &self.special_input.1.secret_key)?; 290 | slate.tx = slate.tx.with_output(special_output); 291 | 292 | let r = KeyPair::new_random(); 293 | 294 | // Using zero offset for "internal" transaction 295 | let blind_excess = 296 | compute_excess_sk(vec![], vec![&self.special_input.1.secret_key], None)?; 297 | let blind_excess_keypair = KeyPair::new(blind_excess); 298 | 299 | slate.participant_data = vec![ParticipantData { 300 | id: 1, 301 | public_blind_excess: blind_excess_keypair.public_key, 302 | public_nonce: r.public_key, 303 | part_sig: None, 304 | message: None, 305 | message_sig: None, 306 | }]; 307 | 308 | (slate, r, blind_excess_keypair) 309 | }; 310 | 311 | let slate = wallet.process_invoice(slate)?; 312 | 313 | // Add special output partial signature and verify aggregate signature 314 | let transaction_from_funder_wallet_to_special_output = { 315 | let sender_data = slate 316 | .participant_data 317 | .iter() 318 | .find(|p| p.id == 0) 319 | .ok_or_else(|| anyhow::anyhow!("missing sender data"))?; 320 | 321 | let (sig, excess) = crate::schnorr::sign_2p_1( 322 | &blind_excess_keypair, 323 | &r, 324 | &sender_data.public_blind_excess, 325 | &sender_data.public_nonce, 326 | &KernelFeatures::Plain { fee: slate.fee }.kernel_sig_msg()?, 327 | &sender_data 328 | .part_sig 329 | .ok_or_else(|| anyhow::anyhow!("missing sender partsig"))? 330 | .try_into()?, 331 | )?; 332 | 333 | let mut tx = slate.tx; 334 | 335 | tx.body.kernels[0].excess = public_key_to_pedersen_commitment(&excess); 336 | tx.body.kernels[0].excess_sig = sig; 337 | 338 | tx.body.kernels[0].verify().map_err(|e| { 339 | anyhow::anyhow!("failed to verify grin fund transaction kernel: {}", e) 340 | })?; 341 | 342 | tx 343 | }; 344 | 345 | let aggregate_transaction = grin_core::core::transaction::aggregate(vec![ 346 | transaction_from_funder_wallet_to_special_output, 347 | self.transaction_from_special_input, 348 | ]) 349 | .map_err(|e| anyhow::anyhow!("failed to aggregate fund transaction: {}", e))?; 350 | 351 | let fee = aggregate_transaction.fee(); 352 | wallet.post_transaction(aggregate_transaction)?; 353 | 354 | Ok(fee) 355 | } 356 | } 357 | 358 | impl Execute for Redeem { 359 | type Wallet = Wallet; 360 | type Return = u64; 361 | fn execute(self, wallet: &Self::Wallet) -> anyhow::Result { 362 | aggregate_with_spending_transaction( 363 | self.transaction_to_special_output, 364 | self.special_output, 365 | self.wallet_transaction_fee, 366 | wallet, 367 | ) 368 | } 369 | } 370 | 371 | impl Execute for Refund { 372 | type Wallet = Wallet; 373 | type Return = u64; 374 | fn execute(self, wallet: &Self::Wallet) -> anyhow::Result { 375 | aggregate_with_spending_transaction( 376 | self.transaction_to_special_output, 377 | self.special_output, 378 | self.wallet_transaction_fee, 379 | wallet, 380 | ) 381 | } 382 | } 383 | 384 | pub fn aggregate_with_spending_transaction( 385 | transaction_to_special_output: Transaction, 386 | special_output: (u64, KeyPair), 387 | wallet_transaction_fee: u64, 388 | wallet: &Wallet, 389 | ) -> anyhow::Result { 390 | let mut slate = wallet.issue_invoice(special_output.0 - wallet_transaction_fee)?; 391 | 392 | slate.fee = wallet_transaction_fee; 393 | slate.update_kernel(); 394 | 395 | let special_input = build_input(special_output.0, &special_output.1.secret_key)?; 396 | slate.tx = slate.tx.with_input(special_input); 397 | 398 | let r = KeyPair::new_random(); 399 | 400 | let blind_excess = compute_excess_sk(vec![&special_output.1.secret_key], vec![], None)?; 401 | let blind_excess_keypair = KeyPair::new(blind_excess); 402 | 403 | slate.participant_data.push(ParticipantData { 404 | id: 0, 405 | public_blind_excess: blind_excess_keypair.public_key, 406 | public_nonce: r.public_key, 407 | part_sig: None, 408 | message: None, 409 | message_sig: None, 410 | }); 411 | 412 | let receiver_data = slate 413 | .participant_data 414 | .iter() 415 | .find(|p| p.id == 1) 416 | .ok_or_else(|| anyhow::anyhow!("missing sender data"))?; 417 | 418 | // The aggregate transaction will contain another kernel which will be height 419 | // locked according to the expiry defined in the offer. Therefore, there is no 420 | // need to height lock the kernel corresponding to the other transaction 421 | // involved 422 | let partial_sig = schnorr::sign_2p_0( 423 | &blind_excess_keypair, 424 | &r, 425 | &receiver_data.public_blind_excess, 426 | &receiver_data.public_nonce, 427 | &KernelFeatures::Plain { fee: slate.fee }.kernel_sig_msg()?, 428 | )?; 429 | 430 | for p in slate.participant_data.iter_mut() { 431 | if p.id == 0 { 432 | p.part_sig = Some(partial_sig.to_signature(&r.public_key)?); 433 | } 434 | } 435 | 436 | let transaction_from_special_input_to_wallet = wallet.finalize_invoice(slate)?; 437 | 438 | let aggregate_transaction = grin_core::core::transaction::aggregate(vec![ 439 | transaction_to_special_output, 440 | transaction_from_special_input_to_wallet, 441 | ]) 442 | .map_err(|e| anyhow::anyhow!("failed to aggregate refund transaction: {}", e))?; 443 | 444 | let fee = aggregate_transaction.fee(); 445 | wallet.post_transaction(aggregate_transaction)?; 446 | 447 | Ok(fee) 448 | } 449 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------