From c437c88b07280b3587813f7983285be1eef4864d Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Fri, 16 Nov 2018 14:59:56 +1100 Subject: [PATCH 01/10] Refactor State Machine and Events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Role/IntoSecret is no longer used in events/queries/validation code 🎉 - State machine takes a single type parameter which is role which has defines the types for the state - The response event is produced by another field of type ResponseSource on context to allow Events to be specifically for ledger events. --- api_tests/package-lock.json | 30 +- .../comit_node/src/comit_client/mod.rs | 2 +- .../comit_node/src/http_api/rfc003/swap.rs | 19 +- .../rfc003/actions/alice/btc_eth.rs | 11 +- .../rfc003/actions/bob/btc_eth.rs | 51 +- .../swap_protocols/rfc003/alice/handler.rs | 3 +- .../src/swap_protocols/rfc003/bitcoin/mod.rs | 36 +- .../swap_protocols/rfc003/bitcoin/queries.rs | 63 +-- .../rfc003/bitcoin/validation.rs | 203 ++++++++ .../src/swap_protocols/rfc003/ethereum/mod.rs | 26 +- .../swap_protocols/rfc003/ethereum/queries.rs | 36 +- .../rfc003/ethereum/validation.rs | 16 + .../swap_protocols/rfc003/events/default.rs | 199 ++------ .../src/swap_protocols/rfc003/events/mod.rs | 163 ++----- .../src/swap_protocols/rfc003/ledger.rs | 4 +- .../src/swap_protocols/rfc003/mod.rs | 6 +- .../src/swap_protocols/rfc003/roles.rs | 194 ++++++++ .../src/swap_protocols/rfc003/save_state.rs | 31 +- .../src/swap_protocols/rfc003/secret.rs | 28 +- .../swap_protocols/rfc003/state_machine.rs | 274 +++++++---- .../rfc003/state_machine_test.rs} | 107 ++--- .../src/swap_protocols/rfc003/state_store.rs | 72 +-- .../src/swap_protocols/rfc003/validation.rs | 446 +----------------- 23 files changed, 884 insertions(+), 1136 deletions(-) create mode 100644 application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs create mode 100644 application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs create mode 100644 application/comit_node/src/swap_protocols/rfc003/roles.rs rename application/comit_node/{tests/rfc003_states.rs => src/swap_protocols/rfc003/state_machine_test.rs} (67%) diff --git a/api_tests/package-lock.json b/api_tests/package-lock.json index dc748015e4..70c124beec 100644 --- a/api_tests/package-lock.json +++ b/api_tests/package-lock.json @@ -2913,8 +2913,19 @@ "integrity": "sha512-wAnENuZx75T5ZSrT2De2LOaUuPf2yRjq1VfcbD7+Zd79F3DZZLBJcPyCNVQ1U0fAXt0wfgCKl7sVw5pffqR9Bw==", "requires": { "underscore": "1.8.3", - "web3-core-helpers": "1.0.0-beta.36", - "websocket": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2" + "web3-core-helpers": "1.0.0-beta.36" + }, + "dependencies": { + "websocket": { + "version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", + "from": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", + "requires": { + "debug": "^2.2.0", + "nan": "^2.3.3", + "typedarray-to-buffer": "^3.1.2", + "yaeti": "^0.0.6" + } + } } }, "web3-shh": { @@ -2949,16 +2960,6 @@ } } }, - "websocket": { - "version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", - "from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible", - "requires": { - "debug": "^2.2.0", - "nan": "^2.3.3", - "typedarray-to-buffer": "^3.1.2", - "yaeti": "^0.0.6" - } - }, "wif": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", @@ -3033,11 +3034,6 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/application/comit_node/src/comit_client/mod.rs b/application/comit_node/src/comit_client/mod.rs index 0d076e6b43..9bc9a27f48 100644 --- a/application/comit_node/src/comit_client/mod.rs +++ b/application/comit_node/src/comit_client/mod.rs @@ -7,7 +7,7 @@ use std::{io, net::SocketAddr, sync::Arc}; use std::{fmt::Debug, panic::RefUnwindSafe}; use swap_protocols::{bam_types, rfc003}; -pub trait Client: Send + Sync { +pub trait Client: Send + Sync + 'static { fn send_swap_request< SL: rfc003::Ledger, TL: rfc003::Ledger, diff --git a/application/comit_node/src/http_api/rfc003/swap.rs b/application/comit_node/src/http_api/rfc003/swap.rs index e932e271d6..9ea5a6ad88 100644 --- a/application/comit_node/src/http_api/rfc003/swap.rs +++ b/application/comit_node/src/http_api/rfc003/swap.rs @@ -13,8 +13,9 @@ use swap_protocols::{ ledger::{Bitcoin, Ethereum}, rfc003::{ self, bitcoin, + roles::{Alice, Bob}, state_store::{self, StateStore}, - Ledger, Secret, SecretHash, + Ledger, Secret, }, Assets, Ledgers, Metadata, MetadataStore, Roles, }; @@ -290,14 +291,16 @@ fn handle_state_for_get_swap, S: state_store::StateStor target_asset: Assets::Ether, role, }) => match role { - Roles::Alice => match state_store - .get::(id) - { - Err(e) => error!("Could not retrieve state: {:?}", e), - Ok(state) => info!("Here is the state we have retrieved: {:?}", state), - }, + Roles::Alice => { + match state_store + .get::>(id) + { + Err(e) => error!("Could not retrieve state: {:?}", e), + Ok(state) => info!("Here is the state we have retrieved: {:?}", state), + } + } Roles::Bob => match state_store - .get::(id) + .get::>(id) { Err(e) => error!("Could not retrieve state: {:?}", e), Ok(state) => info!("Here is the state we have retrieved: {:?}", state), diff --git a/application/comit_node/src/swap_protocols/rfc003/actions/alice/btc_eth.rs b/application/comit_node/src/swap_protocols/rfc003/actions/alice/btc_eth.rs index 9fca47ed13..fe79fe46af 100644 --- a/application/comit_node/src/swap_protocols/rfc003/actions/alice/btc_eth.rs +++ b/application/comit_node/src/swap_protocols/rfc003/actions/alice/btc_eth.rs @@ -8,13 +8,12 @@ use swap_protocols::{ ethereum::EtherRedeem, Action, StateActions, }, - bitcoin::{bitcoin_htlc, bitcoin_htlc_address}, + roles::Alice, state_machine::*, - Secret, }, }; -impl StateActions for SwapStates { +impl StateActions for SwapStates> { type Accept = (); type Decline = (); type Fund = BitcoinFund; @@ -26,7 +25,7 @@ impl StateActions for SwapStates vec![], SS::Accepted(Accepted { ref swap, .. }) => vec![Action::Fund(BitcoinFund { - address: bitcoin_htlc_address(swap), + address: swap.source_htlc_params().compute_address(), value: swap.source_asset, })], SS::SourceFunded { .. } => vec![], @@ -44,7 +43,7 @@ impl StateActions for SwapStates vec![Action::Refund(BitcoinRefund { outpoint: *source_htlc_location, - htlc: bitcoin_htlc(swap), + htlc: swap.source_htlc_params().into(), value: swap.source_asset, transient_keypair: swap.source_ledger_refund_identity, })], diff --git a/application/comit_node/src/swap_protocols/rfc003/actions/bob/btc_eth.rs b/application/comit_node/src/swap_protocols/rfc003/actions/bob/btc_eth.rs index 418d7e6f1e..dcb55a2e64 100644 --- a/application/comit_node/src/swap_protocols/rfc003/actions/bob/btc_eth.rs +++ b/application/comit_node/src/swap_protocols/rfc003/actions/bob/btc_eth.rs @@ -8,14 +8,13 @@ use swap_protocols::{ ethereum::{EtherDeploy, EtherRefund}, Accept, Action, Decline, StateActions, }, - bitcoin::bitcoin_htlc, - ethereum::ethereum_htlc, + ethereum::{EtherHtlc, Htlc}, + roles::Bob, state_machine::*, - SecretHash, }, }; -impl StateActions for SwapStates { +impl StateActions for SwapStates> { type Accept = Accept; type Decline = Decline; type Fund = EtherDeploy; @@ -28,7 +27,7 @@ impl StateActions for SwapStates vec![Action::Accept(Accept), Action::Decline(Decline)], SS::Accepted { .. } => vec![], SS::SourceFunded(SourceFunded { ref swap, .. }) => { - let htlc = ethereum_htlc(swap); + let htlc: EtherHtlc = swap.target_htlc_params().into(); vec![Action::Fund(EtherDeploy { data: htlc.compile_to_hex().into(), value: swap.target_asset, @@ -68,9 +67,9 @@ impl StateActions for SwapStates vec![Action::Redeem(BitcoinRedeem { outpoint: *source_htlc_location, - htlc: bitcoin_htlc(swap), + htlc: swap.source_htlc_params().into(), value: swap.source_asset, - transient_keypair: swap.source_ledger_refund_identity, + transient_keypair: swap.source_ledger_success_identity, secret: *secret, })], SS::Error(_) => vec![], @@ -78,41 +77,3 @@ impl StateActions for SwapStates>( id: SwapId, - start_state: Start, + start_state: Start>, state_store: &S, ) where TL::Transaction: ExtractSecret, diff --git a/application/comit_node/src/swap_protocols/rfc003/bitcoin/mod.rs b/application/comit_node/src/swap_protocols/rfc003/bitcoin/mod.rs index f555c4e2da..a8a0c93339 100644 --- a/application/comit_node/src/swap_protocols/rfc003/bitcoin/mod.rs +++ b/application/comit_node/src/swap_protocols/rfc003/bitcoin/mod.rs @@ -1,19 +1,19 @@ use bitcoin_support::{Address, BitcoinQuantity, Blocks, OutPoint}; use secp256k1_support::KeyPair; -use swap_protocols::{ledger::Bitcoin, rfc003::Ledger}; +use swap_protocols::{ + ledger::Bitcoin, + rfc003::{state_machine::HtlcParams, Ledger}, +}; mod extract_secret; mod htlc; mod queries; +mod validation; pub use self::{ htlc::{Htlc, UnlockingError}, queries::*, }; -use swap_protocols::{ - asset::Asset, - rfc003::{state_machine::OngoingSwap, IntoSecretHash}, -}; impl Ledger for Bitcoin { type LockDuration = Blocks; @@ -21,19 +21,19 @@ impl Ledger for Bitcoin { type HtlcIdentity = KeyPair; } -pub fn bitcoin_htlc( - swap: &OngoingSwap, -) -> Htlc { - Htlc::new( - swap.source_ledger_success_identity, - swap.source_ledger_refund_identity, - swap.secret.clone().into(), - swap.source_ledger_lock_duration.into(), - ) +impl From> for Htlc { + fn from(htlc_params: HtlcParams) -> Self { + Htlc::new( + htlc_params.success_identity, + htlc_params.refund_identity, + htlc_params.secret_hash, + htlc_params.lock_duration.into(), + ) + } } -pub fn bitcoin_htlc_address( - swap: &OngoingSwap, -) -> Address { - bitcoin_htlc(swap).compute_address(swap.source_ledger.network) +impl HtlcParams { + pub fn compute_address(&self) -> Address { + Htlc::from(self.clone()).compute_address(self.ledger.network) + } } diff --git a/application/comit_node/src/swap_protocols/rfc003/bitcoin/queries.rs b/application/comit_node/src/swap_protocols/rfc003/bitcoin/queries.rs index 533a24e98d..808388c9ac 100644 --- a/application/comit_node/src/swap_protocols/rfc003/bitcoin/queries.rs +++ b/application/comit_node/src/swap_protocols/rfc003/bitcoin/queries.rs @@ -1,73 +1,46 @@ use bitcoin_support::{BitcoinQuantity, OutPoint}; use ledger_query_service::BitcoinQuery; use swap_protocols::{ - asset::Asset, ledger::Bitcoin, rfc003::{ - bitcoin::bitcoin_htlc_address, - events::{ - NewSourceHtlcFundedQuery, NewSourceHtlcRedeemedQuery, NewSourceHtlcRefundedQuery, - }, - state_machine::OngoingSwap, - IntoSecretHash, Ledger, + bitcoin::Htlc, + events::{NewHtlcFundedQuery, NewHtlcRedeemedQuery, NewHtlcRefundedQuery}, + state_machine::HtlcParams, }, }; -impl NewSourceHtlcFundedQuery for BitcoinQuery -where - TL: Ledger, - TA: Asset, - S: IntoSecretHash, -{ - fn new_source_htlc_funded_query( - swap: &OngoingSwap, - ) -> Self { +impl NewHtlcFundedQuery for BitcoinQuery { + fn new_htlc_funded_query(htlc_params: &HtlcParams) -> Self { + let htlc: Htlc = htlc_params.clone().into(); BitcoinQuery::Transaction { - to_address: Some(bitcoin_htlc_address(swap)), + to_address: Some(htlc.compute_address(htlc_params.ledger.network)), from_outpoint: None, unlock_script: None, } } } -impl NewSourceHtlcRefundedQuery for BitcoinQuery -where - TL: Ledger, - TA: Asset, - S: IntoSecretHash, -{ - fn new_source_htlc_refunded_query( - swap: &OngoingSwap, - source_htlc_location: &OutPoint, +impl NewHtlcRefundedQuery for BitcoinQuery { + fn new_htlc_refunded_query( + _htlc_params: &HtlcParams, + htlc_location: &OutPoint, ) -> Self { BitcoinQuery::Transaction { to_address: None, - from_outpoint: Some(source_htlc_location.clone()), - unlock_script: Some(vec![ - swap.source_ledger_refund_identity - .public_key() - .inner() - .serialize() - .to_vec(), - vec![0u8], - ]), + from_outpoint: Some(*htlc_location), + unlock_script: Some(vec![vec![0u8]]), } } } -impl NewSourceHtlcRedeemedQuery for BitcoinQuery -where - TL: Ledger, - TA: Asset, - S: IntoSecretHash, -{ - fn new_source_htlc_redeemed_query( - _swap: &OngoingSwap, - source_htlc_location: &OutPoint, +impl NewHtlcRedeemedQuery for BitcoinQuery { + fn new_htlc_redeemed_query( + _htlc_params: &HtlcParams, + htlc_location: &OutPoint, ) -> Self { BitcoinQuery::Transaction { to_address: None, - from_outpoint: Some(source_htlc_location.clone()), + from_outpoint: Some(*htlc_location), unlock_script: Some(vec![vec![1u8]]), } } diff --git a/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs b/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs new file mode 100644 index 0000000000..ee196817c1 --- /dev/null +++ b/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs @@ -0,0 +1,203 @@ +use bitcoin_support::{BitcoinQuantity, FindOutput, OutPoint, Transaction}; +use swap_protocols::{ + ledger::Bitcoin, + rfc003::{ + state_machine::HtlcParams, + validation::{Error, IsContainedInTransaction}, + }, +}; + +impl IsContainedInTransaction for BitcoinQuantity { + fn is_contained_in_transaction( + htlc_params: &HtlcParams, + transaction: Transaction, + ) -> Result> { + let address = htlc_params.compute_address(); + + let (vout, txout) = transaction + .find_output(&address) + .ok_or(Error::WrongTransaction)?; + + let location = OutPoint { + txid: transaction.txid(), + vout: vout as u32, + }; + + let actual_value = BitcoinQuantity::from_satoshi(txout.value); + let required_value = htlc_params.asset; + + debug!("Value of HTLC at {:?} is {}", location, actual_value); + + let has_enough_money = actual_value >= required_value; + + trace!( + "{} >= {} -> {}", + actual_value, + required_value, + has_enough_money + ); + if has_enough_money { + Ok(location) + } else { + Err(Error::UnexpectedAsset { + found: actual_value, + expected: required_value, + }) + } + } +} + +#[cfg(test)] +mod tests { + extern crate bitcoin_support; + + use super::{Error as ValidationError, *}; + use bitcoin_rpc_client::rpc::{ + ScriptPubKey, ScriptType, SerializedRawTransaction, TransactionOutput, + VerboseRawTransaction, + }; + use bitcoin_support::{BitcoinQuantity, Blocks, Sha256dHash, Transaction}; + use hex::FromHex; + use spectral::prelude::*; + use swap_protocols::rfc003::{state_machine::*, Secret}; + + fn gen_htlc_params(bitcoin_amount: f64) -> HtlcParams { + HtlcParams { + asset: BitcoinQuantity::from_bitcoin(bitcoin_amount), + ledger: Bitcoin::regtest(), + success_identity: bitcoin_support::PubkeyHash::from_hex( + "d38e554430c4035f2877a579a07a99886153f071", + ) + .unwrap(), + refund_identity: bitcoin_support::PubkeyHash::from_hex( + "d38e554430c4035f2877a579a07a99886153f072", + ) + .unwrap(), + lock_duration: Blocks::from(144), + secret_hash: Secret::from(*b"hello world, you are beautiful!!").into(), + } + } + + #[test] + fn transaction_contains_output_with_sufficient_money() { + let bitcoin_amount = 1.0; + let htlc_params = gen_htlc_params(bitcoin_amount); + let script = htlc_params.compute_address().script_pubkey(); + + let script_pub_key = ScriptPubKey { + asm: String::from(""), + hex: script.clone(), + req_sigs: None, + script_type: ScriptType::NullData, + addresses: None, + }; + + let transaction_output = TransactionOutput { + value: htlc_params.asset.bitcoin(), + n: 1, + script_pub_key, + }; + + let transaction = VerboseRawTransaction { + txid: Sha256dHash::from_data(b"a"), + hash: String::from(""), + size: 0, + vsize: 0, + version: 1, + locktime: 42, + vin: Vec::new(), + vout: vec![transaction_output], + hex: SerializedRawTransaction(String::from("")), + blockhash: Sha256dHash::from_data(b"blockhash"), + confirmations: 0, + time: 0, + blocktime: 0, + }; + + let bitcoin_transaction: Transaction = transaction.into(); + + let result = + BitcoinQuantity::is_contained_in_transaction(&htlc_params, bitcoin_transaction.clone()); + + let txid = bitcoin_transaction.txid(); + let expected_outpoint = OutPoint { txid, vout: 0 }; + + assert_that(&result).is_ok_containing(expected_outpoint) + } + + #[test] + fn transaction_does_not_contain_output() { + let bitcoin_amount = 1.0; + + let transaction = VerboseRawTransaction { + txid: Sha256dHash::from_data(b"refunded"), + hash: String::from(""), + size: 0, + vsize: 0, + version: 1, + locktime: 42, + vin: Vec::new(), + vout: Vec::new(), + hex: SerializedRawTransaction(String::from("")), + blockhash: Sha256dHash::from_data(b"blockhash"), + confirmations: 0, + time: 0, + blocktime: 0, + }; + + let result = BitcoinQuantity::is_contained_in_transaction( + &gen_htlc_params(bitcoin_amount), + transaction.into(), + ); + + assert_that(&result).is_err_containing(ValidationError::WrongTransaction) + } + + #[test] + fn transaction_does_not_contain_enough_money() { + let bitcoin_amount = 1.0; + let htlc_params = gen_htlc_params(bitcoin_amount); + + let script = htlc_params.compute_address().script_pubkey(); + let script_pub_key = ScriptPubKey { + asm: String::from(""), + hex: script.clone(), + req_sigs: None, + script_type: ScriptType::NullData, + addresses: None, + }; + + let provided_bitcoin_amount = 0.5; + + let transaction_output = TransactionOutput { + value: provided_bitcoin_amount, + n: 1, + script_pub_key, + }; + + let transaction = VerboseRawTransaction { + txid: Sha256dHash::from_data(b"a"), + hash: String::from(""), + size: 0, + vsize: 0, + version: 1, + locktime: 42, + vin: Vec::new(), + vout: vec![transaction_output], + hex: SerializedRawTransaction(String::from("")), + blockhash: Sha256dHash::from_data(b"blockhash"), + confirmations: 0, + time: 0, + blocktime: 0, + }; + + let result = BitcoinQuantity::is_contained_in_transaction(&htlc_params, transaction.into()); + + let expected_error = ValidationError::UnexpectedAsset { + found: BitcoinQuantity::from_bitcoin(provided_bitcoin_amount), + expected: BitcoinQuantity::from_bitcoin(bitcoin_amount), + }; + + assert_that(&result).is_err_containing(expected_error) + } +} diff --git a/application/comit_node/src/swap_protocols/rfc003/ethereum/mod.rs b/application/comit_node/src/swap_protocols/rfc003/ethereum/mod.rs index 368df86462..8d5d0c5adf 100644 --- a/application/comit_node/src/swap_protocols/rfc003/ethereum/mod.rs +++ b/application/comit_node/src/swap_protocols/rfc003/ethereum/mod.rs @@ -1,19 +1,17 @@ use ethereum_support::Bytes; use hex; +use swap_protocols::rfc003::state_machine::HtlcParams; pub use self::{erc20_htlc::*, ether_htlc::*, queries::*}; use ethereum_support::{web3::types::Address, EtherQuantity}; use std::time::Duration; -use swap_protocols::{ - asset::Asset, - ledger::Ethereum, - rfc003::{state_machine::OngoingSwap, IntoSecretHash, Ledger}, -}; +use swap_protocols::{ledger::Ethereum, rfc003::Ledger}; mod erc20_htlc; mod ether_htlc; mod extract_secret; mod queries; +mod validation; #[derive(Deserialize, Serialize, Debug)] pub struct ByteCode(pub String); @@ -49,13 +47,13 @@ impl Ledger for Ethereum { type HtlcIdentity = Address; } -pub fn ethereum_htlc( - swap: &OngoingSwap, -) -> Box { - Box::new(EtherHtlc::new( - swap.target_ledger_lock_duration, - swap.target_ledger_refund_identity, - swap.target_ledger_success_identity, - swap.secret.clone().into(), - )) +impl From> for EtherHtlc { + fn from(htlc_params: HtlcParams) -> Self { + EtherHtlc::new( + htlc_params.lock_duration, + htlc_params.refund_identity, + htlc_params.success_identity, + htlc_params.secret_hash, + ) + } } diff --git a/application/comit_node/src/swap_protocols/rfc003/ethereum/queries.rs b/application/comit_node/src/swap_protocols/rfc003/ethereum/queries.rs index e829f075be..acd5c83709 100644 --- a/application/comit_node/src/swap_protocols/rfc003/ethereum/queries.rs +++ b/application/comit_node/src/swap_protocols/rfc003/ethereum/queries.rs @@ -1,47 +1,35 @@ use ethereum_support::{web3::types::Address, EtherQuantity}; use ledger_query_service::EthereumQuery; use swap_protocols::{ - asset::Asset, ledger::Ethereum, rfc003::{ - events::{NewTargetHtlcRedeemedQuery, NewTargetHtlcRefundedQuery}, - state_machine::OngoingSwap, - IntoSecretHash, Ledger, + events::{NewHtlcRedeemedQuery, NewHtlcRefundedQuery}, + state_machine::HtlcParams, }, }; -impl NewTargetHtlcRefundedQuery for EthereumQuery -where - SL: Ledger, - SA: Asset, - S: IntoSecretHash, -{ - fn new_target_htlc_refunded_query( - _swap: &OngoingSwap, - target_htlc_location: &Address, +impl NewHtlcRefundedQuery for EthereumQuery { + fn new_htlc_refunded_query( + _htlc_params: &HtlcParams, + htlc_location: &Address, ) -> Self { EthereumQuery::Transaction { from_address: None, - to_address: Some(target_htlc_location.clone()), + to_address: Some(htlc_location.clone()), is_contract_creation: Some(false), transaction_data: None, } } } -impl NewTargetHtlcRedeemedQuery for EthereumQuery -where - SL: Ledger, - SA: Asset, - S: IntoSecretHash, -{ - fn new_target_htlc_redeemed_query( - _swap: &OngoingSwap, - target_htlc_location: &Address, +impl NewHtlcRedeemedQuery for EthereumQuery { + fn new_htlc_redeemed_query( + _htlc_params: &HtlcParams, + htlc_location: &Address, ) -> Self { EthereumQuery::Transaction { from_address: None, - to_address: Some(target_htlc_location.clone()), + to_address: Some(htlc_location.clone()), is_contract_creation: Some(false), transaction_data: None, } diff --git a/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs b/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs new file mode 100644 index 0000000000..fe973337e2 --- /dev/null +++ b/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs @@ -0,0 +1,16 @@ +use swap_protocols::ledger::Ethereum; + +use ethereum_support::{self, EtherQuantity}; +use swap_protocols::rfc003::{ + state_machine::HtlcParams, + validation::{Error, IsContainedInTransaction}, +}; + +impl IsContainedInTransaction for EtherQuantity { + fn is_contained_in_transaction( + _htlc_params: &HtlcParams, + _transaction: ethereum_support::Transaction, + ) -> Result> { + unimplemented!() + } +} diff --git a/application/comit_node/src/swap_protocols/rfc003/events/default.rs b/application/comit_node/src/swap_protocols/rfc003/events/default.rs index 52e4672919..994034cbc1 100644 --- a/application/comit_node/src/swap_protocols/rfc003/events/default.rs +++ b/application/comit_node/src/swap_protocols/rfc003/events/default.rs @@ -1,42 +1,29 @@ -use comit_client::Client; use futures::{future::Either, Future}; use ledger_query_service::{CreateQuery, FirstMatch, Query, QueryIdCache}; -use std::sync::Arc; use swap_protocols::{ asset::Asset, rfc003::{ self, events::{ - Events, Funded, NewSourceHtlcFundedQuery, NewSourceHtlcRedeemedQuery, - NewSourceHtlcRefundedQuery, NewTargetHtlcFundedQuery, NewTargetHtlcRedeemedQuery, - NewTargetHtlcRefundedQuery, RedeemedOrRefunded, RequestResponded, Response, - SourceHtlcFunded, SourceHtlcRedeemedOrRefunded, SourceHtlcRefundedTargetHtlcFunded, - SourceRefundedOrTargetFunded, TargetHtlcRedeemedOrRefunded, + Events, Funded, HtlcFunded, NewHtlcFundedQuery, NewHtlcRedeemedQuery, + NewHtlcRefundedQuery, RedeemedOrRefunded, SourceHtlcRedeemedOrRefunded, + SourceHtlcRefundedTargetHtlcFunded, SourceRefundedOrTargetFunded, + TargetHtlcRedeemedOrRefunded, }, - messages::Request, - state_machine::OngoingSwap, - validation::{IsContainedInSourceLedgerTransaction, IsContainedInTargetLedgerTransaction}, - IntoSecretHash, Ledger, + state_machine::HtlcParams, + validation::IsContainedInTransaction, + Ledger, }, }; -#[derive(Debug)] -pub enum Role { - Alice { client: Arc }, - Bob, -} - #[allow(missing_debug_implementations)] -pub struct DefaultEvents { - role: Role, - +pub struct DefaultEvents { create_source_ledger_query: QueryIdCache, source_ledger_first_match: FirstMatch, create_target_ledger_query: QueryIdCache, target_ledger_first_match: FirstMatch, - response: Option>>, source_htlc_funded_query: Option>>, source_htlc_refunded_target_htlc_funded_query: Option>>, @@ -44,66 +31,18 @@ pub struct DefaultEvents>>, } -impl RequestResponded - for DefaultEvents +impl HtlcFunded for DefaultEvents where SL: Ledger, TL: Ledger, - SA: Asset, - TA: Asset, - ComitClient: Client, - SLQuery: Query, + SA: Asset + IsContainedInTransaction, + SLQuery: Query + NewHtlcFundedQuery, TLQuery: Query, { - fn request_responded( - &mut self, - request: &Request, - ) -> &mut Box> { - match self.role { - Role::Alice {ref client}=> { - let client = client.clone(); - - self.response.get_or_insert_with(|| { - Box::new( - client - .send_swap_request(request.clone()) - .map_err(rfc003::Error::SwapResponse), - ) - }) - }, - Role::Bob => { - unimplemented!("return a future that resolves once the user sent a response to the COMIT node via the API") - } - } - } -} - -impl SourceHtlcFunded - for DefaultEvents -where - SL: Ledger, - TL: Ledger, - SA: Asset + IsContainedInSourceLedgerTransaction, - TA: Asset, - S: IntoSecretHash, - ComitClient: Client, - SLQuery: Query - + NewSourceHtlcFundedQuery - + NewSourceHtlcRefundedQuery - + NewSourceHtlcRedeemedQuery, - TLQuery: Query - + NewTargetHtlcFundedQuery - + NewTargetHtlcRefundedQuery - + NewTargetHtlcRedeemedQuery, -{ - fn source_htlc_funded<'s>( - &'s mut self, - swap: &OngoingSwap, - ) -> &'s mut Box> { - let swap = swap.clone(); + fn htlc_funded<'s>(&'s mut self, htlc_params: HtlcParams) -> &'s mut Funded { let source_ledger_first_match = self.source_ledger_first_match.clone(); - let query = SLQuery::new_source_htlc_funded_query(&swap); + let query = SLQuery::new_htlc_funded_query(&htlc_params); let query_id = self.create_source_ledger_query.create_query(query); self.source_htlc_funded_query.get_or_insert_with(move || { @@ -113,7 +52,7 @@ where source_ledger_first_match .first_match_of(query_id) .and_then(move |tx| { - SA::is_contained_in_source_ledger_transaction(swap, tx) + SA::is_contained_in_transaction(&htlc_params, tx) .map_err(|_| rfc003::Error::InsufficientFunding) }) }); @@ -123,41 +62,31 @@ where } } -impl - SourceHtlcRefundedTargetHtlcFunded - for DefaultEvents +impl SourceHtlcRefundedTargetHtlcFunded + for DefaultEvents where SL: Ledger, TL: Ledger, SA: Asset, - TA: Asset + IsContainedInTargetLedgerTransaction, - S: IntoSecretHash, - ComitClient: Client, - SLQuery: Query - + NewSourceHtlcFundedQuery - + NewSourceHtlcRefundedQuery - + NewSourceHtlcRedeemedQuery, - TLQuery: Query - + NewTargetHtlcFundedQuery - + NewTargetHtlcRefundedQuery - + NewTargetHtlcRedeemedQuery, + TA: Asset + IsContainedInTransaction, + SLQuery: Query + NewHtlcRefundedQuery, + TLQuery: Query + NewHtlcFundedQuery, { fn source_htlc_refunded_target_htlc_funded( &mut self, - swap: &OngoingSwap, + source_htlc_params: HtlcParams, + target_htlc_params: HtlcParams, source_htlc_location: &SL::HtlcLocation, - ) -> &mut Box> { - let swap = swap.clone(); - + ) -> &mut SourceRefundedOrTargetFunded { let source_ledger_first_match = self.source_ledger_first_match.clone(); let source_refunded_query = - SLQuery::new_source_htlc_refunded_query(&swap, source_htlc_location); + SLQuery::new_htlc_refunded_query(&source_htlc_params, source_htlc_location); let source_refunded_query_id = self .create_source_ledger_query .create_query(source_refunded_query); let target_ledger_first_match = self.target_ledger_first_match.clone(); - let target_funded_query = TLQuery::new_target_htlc_funded_query(&swap); + let target_funded_query = TLQuery::new_htlc_funded_query(&target_htlc_params); let target_funded_query_id = self .create_target_ledger_query .create_query(target_funded_query); @@ -174,7 +103,7 @@ where target_ledger_first_match .first_match_of(query_id) .and_then(move |tx| { - TA::is_contained_in_target_ledger_transaction(swap, tx) + TA::is_contained_in_transaction(&target_htlc_params, tx) .map_err(|_| rfc003::Error::InsufficientFunding) }) }); @@ -195,41 +124,29 @@ where } } -impl - TargetHtlcRedeemedOrRefunded - for DefaultEvents +impl TargetHtlcRedeemedOrRefunded + for DefaultEvents where SL: Ledger, TL: Ledger, - SA: Asset, TA: Asset, - S: IntoSecretHash, - ComitClient: Client, - SLQuery: Query - + NewSourceHtlcFundedQuery - + NewSourceHtlcRefundedQuery - + NewSourceHtlcRedeemedQuery, - TLQuery: Query - + NewTargetHtlcFundedQuery - + NewTargetHtlcRefundedQuery - + NewTargetHtlcRedeemedQuery, + TLQuery: Query + NewHtlcRefundedQuery + NewHtlcRedeemedQuery, + SLQuery: Query, { fn target_htlc_redeemed_or_refunded( &mut self, - swap: &OngoingSwap, + target_htlc_params: HtlcParams, target_htlc_location: &TL::HtlcLocation, - ) -> &mut Box> { - let swap = swap.clone(); - + ) -> &mut RedeemedOrRefunded { let target_ledger_first_match = self.target_ledger_first_match.clone(); let target_refunded_query = - TLQuery::new_target_htlc_refunded_query(&swap, target_htlc_location); + TLQuery::new_htlc_refunded_query(&target_htlc_params, target_htlc_location); let target_refunded_query_id = self .create_target_ledger_query .create_query(target_refunded_query); let target_redeemed_query = - TLQuery::new_target_htlc_redeemed_query(&swap, target_htlc_location); + TLQuery::new_htlc_redeemed_query(&target_htlc_params, target_htlc_location); let target_redeemed_query_id = self .create_target_ledger_query .create_query(target_redeemed_query); @@ -261,41 +178,29 @@ where } } -impl - SourceHtlcRedeemedOrRefunded - for DefaultEvents +impl SourceHtlcRedeemedOrRefunded + for DefaultEvents where SL: Ledger, TL: Ledger, SA: Asset, - TA: Asset, - S: IntoSecretHash, - ComitClient: Client, - SLQuery: Query - + NewSourceHtlcFundedQuery - + NewSourceHtlcRefundedQuery - + NewSourceHtlcRedeemedQuery, - TLQuery: Query - + NewTargetHtlcFundedQuery - + NewTargetHtlcRefundedQuery - + NewTargetHtlcRedeemedQuery, + SLQuery: Query + NewHtlcRefundedQuery + NewHtlcRedeemedQuery, + TLQuery: Query, { fn source_htlc_redeemed_or_refunded( &mut self, - swap: &OngoingSwap, + source_htlc_params: HtlcParams, source_htlc_location: &SL::HtlcLocation, - ) -> &mut Box> { - let swap = swap.clone(); - + ) -> &mut RedeemedOrRefunded { let source_ledger_first_match = self.source_ledger_first_match.clone(); let source_refunded_query = - SLQuery::new_source_htlc_refunded_query(&swap, source_htlc_location); + SLQuery::new_htlc_refunded_query(&source_htlc_params, source_htlc_location); let source_refunded_query_id = self .create_source_ledger_query .create_query(source_refunded_query); let source_redeemed_query = - SLQuery::new_source_htlc_redeemed_query(&swap, source_htlc_location); + SLQuery::new_htlc_redeemed_query(&source_htlc_params, source_htlc_location); let source_redeemed_query_id = self .create_source_ledger_query .create_query(source_redeemed_query); @@ -327,22 +232,20 @@ where } } -impl Events - for DefaultEvents +impl Events + for DefaultEvents where SL: Ledger, TL: Ledger, - SA: Asset + IsContainedInSourceLedgerTransaction, - TA: Asset + IsContainedInTargetLedgerTransaction, - S: IntoSecretHash, - ComitClient: Client, + SA: Asset + IsContainedInTransaction, + TA: Asset + IsContainedInTransaction, SLQuery: Query - + NewSourceHtlcFundedQuery - + NewSourceHtlcRefundedQuery - + NewSourceHtlcRedeemedQuery, + + NewHtlcFundedQuery + + NewHtlcRefundedQuery + + NewHtlcRedeemedQuery, TLQuery: Query - + NewTargetHtlcFundedQuery - + NewTargetHtlcRefundedQuery - + NewTargetHtlcRedeemedQuery, + + NewHtlcFundedQuery + + NewHtlcRefundedQuery + + NewHtlcRedeemedQuery, { } diff --git a/application/comit_node/src/swap_protocols/rfc003/events/mod.rs b/application/comit_node/src/swap_protocols/rfc003/events/mod.rs index 2c1c274fcf..713513826d 100644 --- a/application/comit_node/src/swap_protocols/rfc003/events/mod.rs +++ b/application/comit_node/src/swap_protocols/rfc003/events/mod.rs @@ -2,28 +2,19 @@ // see: https://github.com/rust-lang/rust/issues/21903 #![allow(type_alias_bounds)] -use comit_client::SwapReject; use ledger_query_service::Query; use swap_protocols::{ asset::Asset, - rfc003::{ - self, - ledger::Ledger, - messages::{AcceptResponseBody, Request}, - state_machine::OngoingSwap, - IntoSecretHash, - }, + rfc003::{self, ledger::Ledger}, }; use tokio::{self, prelude::future::Either}; -pub use self::default::{DefaultEvents, Role}; -use swap_protocols::rfc003::ExtractSecret; - +pub use self::default::DefaultEvents; mod default; +use swap_protocols::rfc003::state_machine::HtlcParams; type Future = tokio::prelude::Future + Send; -pub type Response = Future, SwapReject>>; pub type Funded = Future; pub type Refunded = Future; pub type Redeemed = Future; @@ -31,154 +22,68 @@ pub type SourceRefundedOrTargetFunded = Future>; pub type RedeemedOrRefunded = Future>; -pub trait RequestResponded: Send { - fn request_responded( - &mut self, - request: &Request, - ) -> &mut Box>; +pub trait HtlcFunded: Send { + fn htlc_funded(&mut self, htlc_params: HtlcParams) -> &mut Funded; } -pub trait SourceHtlcFunded: +pub trait SourceHtlcRefundedTargetHtlcFunded: Send -{ - fn source_htlc_funded(&mut self, swap: &OngoingSwap) - -> &mut Box>; -} - -pub trait SourceHtlcRefundedTargetHtlcFunded< - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset, - S: IntoSecretHash, ->: Send { fn source_htlc_refunded_target_htlc_funded( &mut self, - swap: &OngoingSwap, + source_htlc_params: HtlcParams, + target_htlc_params: HtlcParams, source_htlc_location: &SL::HtlcLocation, - ) -> &mut Box>; + ) -> &mut SourceRefundedOrTargetFunded; } -pub trait TargetHtlcRedeemedOrRefunded< - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset, - S: IntoSecretHash, ->: Send -{ - fn target_htlc_redeemed_or_refunded( +pub trait SourceHtlcRedeemedOrRefunded: Send { + fn source_htlc_redeemed_or_refunded( &mut self, - swap: &OngoingSwap, - target_htlc_location: &TL::HtlcLocation, - ) -> &mut Box> - where - TL::Transaction: ExtractSecret; + source_htlc_params: HtlcParams, + htlc_location: &L::HtlcLocation, + ) -> &mut RedeemedOrRefunded; } -pub trait SourceHtlcRedeemedOrRefunded< - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset, - S: IntoSecretHash, ->: Send -{ - fn source_htlc_redeemed_or_refunded( +pub trait TargetHtlcRedeemedOrRefunded: Send { + fn target_htlc_redeemed_or_refunded( &mut self, - swap: &OngoingSwap, - source_htlc_location: &SL::HtlcLocation, - ) -> &mut Box>; + target_htlc_params: HtlcParams, + htlc_location: &L::HtlcLocation, + ) -> &mut RedeemedOrRefunded; } -pub trait Events: - RequestResponded - + SourceHtlcFunded - + SourceHtlcRefundedTargetHtlcFunded - + TargetHtlcRedeemedOrRefunded - + SourceHtlcRedeemedOrRefunded +pub trait Events: + HtlcFunded + + SourceHtlcRefundedTargetHtlcFunded + + SourceHtlcRedeemedOrRefunded + + TargetHtlcRedeemedOrRefunded { } -pub trait NewSourceHtlcFundedQuery: Send + Sync +pub trait NewHtlcFundedQuery: Send + Sync where - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset, - S: Clone, Self: Query, { - fn new_source_htlc_funded_query(swap: &OngoingSwap) -> Self; + fn new_htlc_funded_query(htlc_params: &HtlcParams) -> Self; } -pub trait NewSourceHtlcRedeemedQuery: Send + Sync +pub trait NewHtlcRedeemedQuery: Send + Sync where - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset, - S: Clone, Self: Query, { - fn new_source_htlc_redeemed_query( - swap: &OngoingSwap, - source_htlc_location: &SL::HtlcLocation, + fn new_htlc_redeemed_query( + htlc_params: &HtlcParams, + source_htlc_location: &L::HtlcLocation, ) -> Self; } -pub trait NewSourceHtlcRefundedQuery: Send + Sync -where - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset, - S: Clone, - Self: Query, -{ - fn new_source_htlc_refunded_query( - swap: &OngoingSwap, - source_htlc_location: &SL::HtlcLocation, - ) -> Self; -} - -pub trait NewTargetHtlcFundedQuery: Send + Sync -where - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset, - S: Clone, - Self: Query, -{ - fn new_target_htlc_funded_query(swap: &OngoingSwap) -> Self; -} -pub trait NewTargetHtlcRedeemedQuery: Send + Sync -where - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset, - S: Clone, - Self: Query, -{ - fn new_target_htlc_redeemed_query( - swap: &OngoingSwap, - target_htlc_location: &TL::HtlcLocation, - ) -> Self; -} -pub trait NewTargetHtlcRefundedQuery: Send + Sync +pub trait NewHtlcRefundedQuery: Send + Sync where - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset, - S: Clone, Self: Query, { - fn new_target_htlc_refunded_query( - swap: &OngoingSwap, - target_htlc_location: &TL::HtlcLocation, + fn new_htlc_refunded_query( + htlc_params: &HtlcParams, + source_htlc_location: &L::HtlcLocation, ) -> Self; } diff --git a/application/comit_node/src/swap_protocols/rfc003/ledger.rs b/application/comit_node/src/swap_protocols/rfc003/ledger.rs index 92ccfbf374..c333f8dd82 100644 --- a/application/comit_node/src/swap_protocols/rfc003/ledger.rs +++ b/application/comit_node/src/swap_protocols/rfc003/ledger.rs @@ -1,8 +1,8 @@ use serde::{de::DeserializeOwned, Serialize}; use std::fmt::Debug; -use swap_protocols; +use swap_protocols::{self, rfc003::secret::LedgerExtractSecret}; -pub trait Ledger: swap_protocols::ledger::Ledger { +pub trait Ledger: LedgerExtractSecret { type LockDuration: PartialEq + Debug + Clone diff --git a/application/comit_node/src/swap_protocols/rfc003/mod.rs b/application/comit_node/src/swap_protocols/rfc003/mod.rs index 383e148c2b..c7241b885d 100644 --- a/application/comit_node/src/swap_protocols/rfc003/mod.rs +++ b/application/comit_node/src/swap_protocols/rfc003/mod.rs @@ -8,6 +8,7 @@ pub mod bitcoin; pub mod ethereum; pub mod events; pub mod ledger_htlc_service; +pub mod roles; pub mod state_machine; pub mod state_store; pub mod validation; @@ -19,11 +20,14 @@ mod outcome; mod save_state; mod secret; +#[cfg(test)] +mod state_machine_test; + pub use self::{ error::Error, ledger::Ledger, messages::*, outcome::SwapOutcome, save_state::SaveState, - secret::{ExtractSecret, IntoSecretHash, RandomnessSource, Secret, SecretFromErr, SecretHash}, + secret::{ExtractSecret, RandomnessSource, Secret, SecretFromErr, SecretHash}, }; diff --git a/application/comit_node/src/swap_protocols/rfc003/roles.rs b/application/comit_node/src/swap_protocols/rfc003/roles.rs new file mode 100644 index 0000000000..1ca3d7fae6 --- /dev/null +++ b/application/comit_node/src/swap_protocols/rfc003/roles.rs @@ -0,0 +1,194 @@ +use comit_client; +use futures::Future; +use std::{ + fmt::{self, Debug}, + marker::PhantomData, + sync::Arc, +}; + +use swap_protocols::{ + self, + asset::Asset, + rfc003::{ + self, + ledger::Ledger, + messages::Request, + state_machine::{ResponseFuture, ResponseSource, StateMachineResponseFuture}, + Secret, SecretHash, + }, +}; + +pub trait Role: Send + Clone + 'static { + type SourceLedger: Ledger; + type TargetLedger: Ledger; + type SourceAsset: Asset; + type TargetAsset: Asset; + type SourceSuccessHtlcIdentity: Send + + Sync + + Clone + + Debug + + PartialEq + + Into<::Identity>; + + type SourceRefundHtlcIdentity: Send + + Sync + + Clone + + Debug + + PartialEq + + Into<::Identity>; + + type TargetSuccessHtlcIdentity: Send + + Sync + + Clone + + Debug + + PartialEq + + Into<::Identity>; + + type TargetRefundHtlcIdentity: Send + + Sync + + Clone + + Debug + + PartialEq + + Into<::Identity>; + + type Secret: Send + Sync + Clone + Into + Debug + PartialEq; +} + +#[allow(dead_code)] // TODO: Remove "allow" when used +struct AliceComitClient { + #[allow(clippy::type_complexity)] + response_future: + Option>>, + client: Arc, +} + +impl + ResponseSource> for AliceComitClient +{ + fn request_responded( + &mut self, + request: &Request, + ) -> &mut ResponseFuture> { + let client = Arc::clone(&self.client); + self.response_future.get_or_insert_with(|| { + Box::new( + client + .send_swap_request(request.clone()) + .map_err(rfc003::Error::SwapResponse) + .map(|result| result.map(Into::into)), + ) + }) + } +} + +pub struct Alice { + phantom_data: PhantomData<(SL, TL, SA, TA)>, +} + +impl Debug for Alice { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "Alice") + } +} + +impl Clone for Alice { + fn clone(&self) -> Alice { + unreachable!("Rust is requiring me to be clone erroneously") + } +} + +impl Role for Alice { + type SourceLedger = SL; + type TargetLedger = TL; + type SourceAsset = SA; + type TargetAsset = TA; + type SourceSuccessHtlcIdentity = SL::Identity; + type SourceRefundHtlcIdentity = SL::HtlcIdentity; + type TargetSuccessHtlcIdentity = TL::HtlcIdentity; + type TargetRefundHtlcIdentity = TL::Identity; + type Secret = Secret; +} + +#[derive(Debug, Clone)] +pub struct Bob { + phantom_data: PhantomData<(SL, TL, SA, TA)>, +} + +impl Role for Bob { + type SourceLedger = SL; + type TargetLedger = TL; + type SourceAsset = SA; + type TargetAsset = TA; + type SourceSuccessHtlcIdentity = SL::HtlcIdentity; + type SourceRefundHtlcIdentity = SL::Identity; + type TargetSuccessHtlcIdentity = TL::Identity; + type TargetRefundHtlcIdentity = TL::HtlcIdentity; + type Secret = SecretHash; +} + +#[cfg(test)] +pub mod test { + use super::*; + use bitcoin_support::{self, BitcoinQuantity}; + use ethereum_support::{self, EtherQuantity}; + use secp256k1_support::KeyPair; + use swap_protocols::{ + ledger::{Bitcoin, Ethereum}, + rfc003::ethereum::Seconds, + }; + + pub struct Alisha {} + + impl Debug for Alisha { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "Alisha") + } + } + + impl PartialEq for Alisha { + fn eq(&self, _: &Alisha) -> bool { + unreachable!() + } + } + + impl Clone for Alisha { + fn clone(&self) -> Self { + unreachable!() + } + } + + impl Role for Alisha { + type SourceLedger = Bitcoin; + type TargetLedger = Ethereum; + type SourceAsset = BitcoinQuantity; + type TargetAsset = EtherQuantity; + type SourceSuccessHtlcIdentity = bitcoin_support::PubkeyHash; + type SourceRefundHtlcIdentity = KeyPair; + type TargetSuccessHtlcIdentity = ethereum_support::Address; + type TargetRefundHtlcIdentity = ethereum_support::Address; + type Secret = Secret; + } + + #[allow(missing_debug_implementations)] + pub struct AlishaResponseSource { + pub response: Option< + Box< + StateMachineResponseFuture< + bitcoin_support::PubkeyHash, + ethereum_support::Address, + Seconds, + >, + >, + >, + } + + impl ResponseSource for AlishaResponseSource { + fn request_responded( + &mut self, + _request: &Request, + ) -> &mut ResponseFuture { + self.response.as_mut().unwrap() + } + } + +} diff --git a/application/comit_node/src/swap_protocols/rfc003/save_state.rs b/application/comit_node/src/swap_protocols/rfc003/save_state.rs index d5e776043a..e7e267fc35 100644 --- a/application/comit_node/src/swap_protocols/rfc003/save_state.rs +++ b/application/comit_node/src/swap_protocols/rfc003/save_state.rs @@ -1,35 +1,20 @@ use futures::sync::mpsc; -use std::{fmt::Debug, sync::RwLock}; -use swap_protocols::{ - asset::Asset, - rfc003::{state_machine::SwapStates, ExtractSecret, IntoSecretHash, Ledger}, -}; +use std::sync::RwLock; +use swap_protocols::rfc003::{roles::Role, state_machine::SwapStates}; -pub trait SaveState: - Send + Sync + Debug -where - TL::Transaction: ExtractSecret, -{ - fn save(&self, state: SwapStates); +pub trait SaveState: Send + Sync { + fn save(&self, state: SwapStates); } -impl SaveState - for RwLock> -where - TL::Transaction: ExtractSecret, -{ - fn save(&self, state: SwapStates) { +impl SaveState for RwLock> { + fn save(&self, state: SwapStates) { let _self = &mut *self.write().unwrap(); *_self = state; } } -impl SaveState - for mpsc::UnboundedSender> -where - TL::Transaction: ExtractSecret, -{ - fn save(&self, state: SwapStates) { +impl SaveState for mpsc::UnboundedSender> { + fn save(&self, state: SwapStates) { // ignore error the subscriber is no longer interested in state updates let _ = self.unbounded_send(state); } diff --git a/application/comit_node/src/swap_protocols/rfc003/secret.rs b/application/comit_node/src/swap_protocols/rfc003/secret.rs index b8ede97b14..671584b899 100644 --- a/application/comit_node/src/swap_protocols/rfc003/secret.rs +++ b/application/comit_node/src/swap_protocols/rfc003/secret.rs @@ -6,6 +6,7 @@ use std::{ fmt::{self, Debug}, str::FromStr, }; +use swap_protocols::Ledger; const SHA256_DIGEST_LENGTH: usize = 32; @@ -81,14 +82,6 @@ impl FromStr for SecretHash { } } -pub trait IntoSecretHash: - PartialEq + Debug + Clone + Send + Sync + Into + 'static -{ -} - -impl IntoSecretHash for Secret {} -impl IntoSecretHash for SecretHash {} - #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] pub struct Secret([u8; SHA256_DIGEST_LENGTH]); @@ -219,6 +212,25 @@ pub trait ExtractSecret { fn extract_secret(&self, secret_hash: &SecretHash) -> Option; } +pub trait LedgerExtractSecret: Ledger { + fn extract_secret_from_transaction( + txn: &Self::Transaction, + secret_hash: &SecretHash, + ) -> Option; +} + +impl LedgerExtractSecret for L +where + L::Transaction: ExtractSecret, +{ + fn extract_secret_from_transaction( + txn: &Self::Transaction, + secret_hash: &SecretHash, + ) -> Option { + txn.extract_secret(secret_hash) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/application/comit_node/src/swap_protocols/rfc003/state_machine.rs b/application/comit_node/src/swap_protocols/rfc003/state_machine.rs index 5286016016..ca1cbcc203 100755 --- a/application/comit_node/src/swap_protocols/rfc003/state_machine.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_machine.rs @@ -1,34 +1,83 @@ +use comit_client::SwapReject; use futures::{future::Either, Async, Future}; use state_machine_future::{RentToOwn, StateMachineFuture}; use std::sync::Arc; +use swap_protocols::rfc003::secret::LedgerExtractSecret; + use swap_protocols::{ + self, asset::Asset, rfc003::{ - self, events, ledger::Ledger, messages::Request, AcceptResponseBody, ExtractSecret, - IntoSecretHash, SaveState, Secret, SwapOutcome, + self, events, ledger::Ledger, messages::Request, roles::Role, AcceptResponseBody, + SaveState, Secret, SecretHash, SwapOutcome, }, }; -#[derive(Debug, Clone, PartialEq)] -pub struct OngoingSwap { - pub source_ledger: SL, - pub target_ledger: TL, - pub source_asset: SA, - pub target_asset: TA, - pub source_ledger_success_identity: SL::Identity, - pub source_ledger_refund_identity: SL::HtlcIdentity, - pub target_ledger_success_identity: TL::HtlcIdentity, - pub target_ledger_refund_identity: TL::Identity, - pub source_ledger_lock_duration: SL::LockDuration, - pub target_ledger_lock_duration: TL::LockDuration, - pub secret: S, +#[derive(Debug, Clone)] +pub struct StateMachineResponse { + pub source_ledger_success_identity: SLSI, + pub target_ledger_refund_identity: TLRI, + pub target_ledger_lock_duration: TLLD, } -impl OngoingSwap -where - TL::Transaction: ExtractSecret, +impl From> + for StateMachineResponse { - pub fn new(start: Start, response: AcceptResponseBody) -> Self { + fn from(accept_response: AcceptResponseBody) -> Self { + Self { + source_ledger_success_identity: accept_response.source_ledger_success_identity, + target_ledger_refund_identity: accept_response.target_ledger_refund_identity, + target_ledger_lock_duration: accept_response.target_ledger_lock_duration, + } + } +} + +pub type StateMachineResponseFuture = Future< + Item = Result, SwapReject>, + Error = rfc003::Error, + > + Send; + +#[allow(type_alias_bounds)] +pub type ResponseFuture = StateMachineResponseFuture< + R::SourceSuccessHtlcIdentity, + R::TargetRefundHtlcIdentity, + ::LockDuration, +>; + +#[derive(Clone, Debug)] +pub struct HtlcParams { + pub asset: A, + pub ledger: L, + pub success_identity: L::Identity, + pub refund_identity: L::Identity, + pub lock_duration: L::LockDuration, + pub secret_hash: SecretHash, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct OngoingSwap { + pub source_ledger: R::SourceLedger, + pub target_ledger: R::TargetLedger, + pub source_asset: R::SourceAsset, + pub target_asset: R::TargetAsset, + pub source_ledger_success_identity: R::SourceSuccessHtlcIdentity, + pub source_ledger_refund_identity: R::SourceRefundHtlcIdentity, + pub target_ledger_success_identity: R::TargetSuccessHtlcIdentity, + pub target_ledger_refund_identity: R::TargetRefundHtlcIdentity, + pub source_ledger_lock_duration: ::LockDuration, + pub target_ledger_lock_duration: ::LockDuration, + pub secret: R::Secret, +} + +impl OngoingSwap { + pub fn new( + start: Start, + response: StateMachineResponse< + R::SourceSuccessHtlcIdentity, + R::TargetRefundHtlcIdentity, + ::LockDuration, + >, + ) -> Self { OngoingSwap { source_ledger: start.source_ledger, target_ledger: start.target_ledger, @@ -43,42 +92,68 @@ where secret: start.secret, } } + + pub fn source_htlc_params(&self) -> HtlcParams { + HtlcParams { + asset: self.source_asset.clone(), + ledger: self.source_ledger.clone(), + success_identity: self.source_ledger_success_identity.clone().into(), + refund_identity: self.source_ledger_refund_identity.clone().into(), + lock_duration: self.source_ledger_lock_duration.clone(), + secret_hash: self.secret.clone().into(), + } + } + + pub fn target_htlc_params(&self) -> HtlcParams { + HtlcParams { + asset: self.target_asset.clone(), + ledger: self.target_ledger.clone(), + success_identity: self.target_ledger_success_identity.clone().into(), + refund_identity: self.target_ledger_refund_identity.clone().into(), + lock_duration: self.target_ledger_lock_duration.clone(), + secret_hash: self.secret.clone().into(), + } + } +} + +pub trait ResponseSource { + fn request_responded( + &mut self, + request: &Request, + ) -> &mut ResponseFuture; } #[allow(missing_debug_implementations)] -pub struct Context { - pub events: Box>, - pub state_repo: Arc>, +pub struct Context { + pub events: + Box>, + pub state_repo: Arc>, + pub response_source: Box + Send>, } #[derive(StateMachineFuture)] #[state_machine_future(context = "Context", derive(Clone, Debug, PartialEq))] -#[allow(missing_debug_implementations)] -pub enum Swap -where - TL::Transaction: ExtractSecret, -{ +#[allow(missing_debug_implementations, clippy::too_many_arguments)] +pub enum Swap { #[state_machine_future(start, transitions(Accepted, Final))] Start { - source_ledger_refund_identity: SL::HtlcIdentity, - target_ledger_success_identity: TL::HtlcIdentity, - source_ledger: SL, - target_ledger: TL, - source_asset: SA, - target_asset: TA, - source_ledger_lock_duration: SL::LockDuration, - secret: S, + source_ledger_refund_identity: R::SourceRefundHtlcIdentity, + target_ledger_success_identity: R::TargetSuccessHtlcIdentity, + source_ledger: R::SourceLedger, + target_ledger: R::TargetLedger, + source_asset: R::SourceAsset, + target_asset: R::TargetAsset, + source_ledger_lock_duration: ::LockDuration, + secret: R::Secret, }, #[state_machine_future(transitions(SourceFunded))] - Accepted { - swap: OngoingSwap, - }, + Accepted { swap: OngoingSwap }, #[state_machine_future(transitions(BothFunded, Final))] SourceFunded { - swap: OngoingSwap, - source_htlc_location: SL::HtlcLocation, + swap: OngoingSwap, + source_htlc_location: ::HtlcLocation, }, #[state_machine_future(transitions( @@ -88,34 +163,34 @@ where SourceRedeemedTargetFunded, ))] BothFunded { - swap: OngoingSwap, - target_htlc_location: TL::HtlcLocation, - source_htlc_location: SL::HtlcLocation, + swap: OngoingSwap, + target_htlc_location: ::HtlcLocation, + source_htlc_location: ::HtlcLocation, }, #[state_machine_future(transitions(Final))] SourceFundedTargetRefunded { - swap: OngoingSwap, - source_htlc_location: SL::HtlcLocation, + swap: OngoingSwap, + source_htlc_location: ::HtlcLocation, }, #[state_machine_future(transitions(Final))] SourceRefundedTargetFunded { - swap: OngoingSwap, - target_htlc_location: TL::HtlcLocation, + swap: OngoingSwap, + target_htlc_location: ::HtlcLocation, }, #[state_machine_future(transitions(Final))] SourceRedeemedTargetFunded { - swap: OngoingSwap, - target_htlc_location: TL::HtlcLocation, + swap: OngoingSwap, + target_htlc_location: ::HtlcLocation, }, #[state_machine_future(transitions(Final))] SourceFundedTargetRedeemed { - swap: OngoingSwap, - target_redeemed_tx: TL::Transaction, - source_htlc_location: SL::HtlcLocation, + swap: OngoingSwap, + target_redeemed_tx: ::Transaction, + source_htlc_location: ::HtlcLocation, secret: Secret, }, @@ -126,15 +201,11 @@ where Error(rfc003::Error), } -impl PollSwap - for Swap -where - TL::Transaction: ExtractSecret, -{ +impl PollSwap for Swap { fn poll_start<'s, 'c>( - state: &'s mut RentToOwn<'s, Start>, - context: &'c mut RentToOwn<'c, Context>, - ) -> Result>, rfc003::Error> { + state: &'s mut RentToOwn<'s, Start>, + context: &'c mut RentToOwn<'c, Context>, + ) -> Result>, rfc003::Error> { let request = Request { source_asset: state.source_asset.clone(), target_asset: state.target_asset.clone(), @@ -146,7 +217,7 @@ where secret_hash: state.secret.clone().into(), }; - let response = try_ready!(context.events.request_responded(&request).poll()); + let response = try_ready!(context.response_source.request_responded(&request).poll()); let state = state.take(); @@ -162,11 +233,13 @@ where } fn poll_accepted<'s, 'c>( - state: &'s mut RentToOwn<'s, Accepted>, - context: &'c mut RentToOwn<'c, Context>, - ) -> Result>, rfc003::Error> { - let source_htlc_location = - try_ready!(context.events.source_htlc_funded(&state.swap).poll()); + state: &'s mut RentToOwn<'s, Accepted>, + context: &'c mut RentToOwn<'c, Context>, + ) -> Result>, rfc003::Error> { + let source_htlc_location = try_ready!(context + .events + .htlc_funded(state.swap.source_htlc_params()) + .poll()); let state = state.take(); @@ -180,12 +253,16 @@ where } fn poll_source_funded<'s, 'c>( - state: &'s mut RentToOwn<'s, SourceFunded>, - context: &'c mut RentToOwn<'c, Context>, - ) -> Result>, rfc003::Error> { + state: &'s mut RentToOwn<'s, SourceFunded>, + context: &'c mut RentToOwn<'c, Context>, + ) -> Result>, rfc003::Error> { match try_ready!(context .events - .source_htlc_refunded_target_htlc_funded(&state.swap, &state.source_htlc_location) + .source_htlc_refunded_target_htlc_funded( + state.swap.source_htlc_params(), + state.swap.target_htlc_params(), + &state.source_htlc_location + ) .poll()) { Either::A(_source_refunded_txid) => { @@ -206,19 +283,25 @@ where } fn poll_both_funded<'s, 'c>( - state: &'s mut RentToOwn<'s, BothFunded>, - context: &'c mut RentToOwn<'c, Context>, - ) -> Result>, rfc003::Error> { + state: &'s mut RentToOwn<'s, BothFunded>, + context: &'c mut RentToOwn<'c, Context>, + ) -> Result>, rfc003::Error> { if let Async::Ready(redeemed_or_refunded) = context .events - .target_htlc_redeemed_or_refunded(&state.swap, &state.target_htlc_location) + .target_htlc_redeemed_or_refunded( + state.swap.target_htlc_params(), + &state.target_htlc_location, + ) .poll()? { let state = state.take(); let secret_hash = state.swap.secret.clone().into(); match redeemed_or_refunded { Either::A(target_redeemed_tx) => { - match target_redeemed_tx.extract_secret(&secret_hash) { + match R::TargetLedger::extract_secret_from_transaction( + &target_redeemed_tx, + &secret_hash, + ) { Some(secret) => transition_save!( context.state_repo, SourceFundedTargetRedeemed { @@ -245,7 +328,10 @@ where match try_ready!(context .events - .source_htlc_redeemed_or_refunded(&state.swap, &state.source_htlc_location) + .source_htlc_redeemed_or_refunded( + state.swap.source_htlc_params(), + &state.source_htlc_location + ) .poll()) { Either::A(_source_redeemed_tx) => { @@ -272,12 +358,15 @@ where } fn poll_source_funded_target_refunded<'s, 'c>( - state: &'s mut RentToOwn<'s, SourceFundedTargetRefunded>, - context: &'c mut RentToOwn<'c, Context>, + state: &'s mut RentToOwn<'s, SourceFundedTargetRefunded>, + context: &'c mut RentToOwn<'c, Context>, ) -> Result, rfc003::Error> { match try_ready!(context .events - .source_htlc_redeemed_or_refunded(&state.swap, &state.source_htlc_location) + .source_htlc_redeemed_or_refunded( + state.swap.source_htlc_params(), + &state.source_htlc_location + ) .poll()) { Either::A(_source_redeemed_txid) => transition_save!( @@ -291,12 +380,15 @@ where } fn poll_source_refunded_target_funded<'s, 'c>( - state: &'s mut RentToOwn<'s, SourceRefundedTargetFunded>, - context: &'c mut RentToOwn<'c, Context>, + state: &'s mut RentToOwn<'s, SourceRefundedTargetFunded>, + context: &'c mut RentToOwn<'c, Context>, ) -> Result, rfc003::Error> { match try_ready!(context .events - .target_htlc_redeemed_or_refunded(&state.swap, &state.target_htlc_location) + .target_htlc_redeemed_or_refunded( + state.swap.target_htlc_params(), + &state.target_htlc_location + ) .poll()) { Either::A(_target_redeemed_txid) => transition_save!( @@ -310,12 +402,15 @@ where } fn poll_source_redeemed_target_funded<'s, 'c>( - state: &'s mut RentToOwn<'s, SourceRedeemedTargetFunded>, - context: &'c mut RentToOwn<'c, Context>, + state: &'s mut RentToOwn<'s, SourceRedeemedTargetFunded>, + context: &'c mut RentToOwn<'c, Context>, ) -> Result, rfc003::Error> { match try_ready!(context .events - .target_htlc_redeemed_or_refunded(&state.swap, &state.target_htlc_location) + .target_htlc_redeemed_or_refunded( + state.swap.target_htlc_params(), + &state.target_htlc_location + ) .poll()) { Either::A(_target_redeemed_txid) => { @@ -329,12 +424,15 @@ where } fn poll_source_funded_target_redeemed<'s, 'c>( - state: &'s mut RentToOwn<'s, SourceFundedTargetRedeemed>, - context: &'c mut RentToOwn<'c, Context>, + state: &'s mut RentToOwn<'s, SourceFundedTargetRedeemed>, + context: &'c mut RentToOwn<'c, Context>, ) -> Result, rfc003::Error> { match try_ready!(context .events - .source_htlc_redeemed_or_refunded(&state.swap, &state.source_htlc_location) + .source_htlc_redeemed_or_refunded( + state.swap.source_htlc_params(), + &state.source_htlc_location + ) .poll()) { Either::A(_target_redeemed_txid) => { diff --git a/application/comit_node/tests/rfc003_states.rs b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs similarity index 67% rename from application/comit_node/tests/rfc003_states.rs rename to application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs index fb2c73b73f..c7371858a4 100644 --- a/application/comit_node/tests/rfc003_states.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs @@ -1,29 +1,20 @@ -extern crate bitcoin_rpc_client; -extern crate bitcoin_support; -extern crate comit_node; -extern crate ethereum_support; -extern crate futures; -extern crate hex; -extern crate secp256k1_support; -extern crate tokio; -extern crate tokio_timer; use bitcoin_rpc_client::rpc::{SerializedRawTransaction, VerboseRawTransaction}; use bitcoin_support::{BitcoinQuantity, Blocks, OutPoint, Sha256dHash}; -use comit_node::{ - comit_client::SwapReject, - swap_protocols::{ - ledger::{Bitcoin, Ethereum}, - rfc003::{ - ethereum::Seconds, - events::{ - self, Events, RequestResponded, SourceHtlcFunded, SourceHtlcRedeemedOrRefunded, - SourceHtlcRefundedTargetHtlcFunded, TargetHtlcRedeemedOrRefunded, - }, - state_machine::*, - AcceptResponseBody, Request, Secret, +use comit_client::SwapReject; +use swap_protocols::{ + ledger::{Bitcoin, Ethereum}, + rfc003::{ + ethereum::Seconds, + events::{ + self, Events, HtlcFunded, SourceHtlcRedeemedOrRefunded, + SourceHtlcRefundedTargetHtlcFunded, TargetHtlcRedeemedOrRefunded, }, + roles::test::{Alisha, AlishaResponseSource}, + state_machine::*, + Secret, }, }; + use ethereum_support::EtherQuantity; use futures::{ future::{self, Either}, @@ -35,71 +26,58 @@ use std::{str::FromStr, sync::Arc}; #[derive(Default)] struct FakeEvents { - pub response: Option>>, pub source_htlc_funded: Option>>, pub source_htlc_refunded_target_htlc_funded: Option>>, } -impl RequestResponded for FakeEvents { - fn request_responded( - &mut self, - _request: &Request, - ) -> &mut Box> { - self.response.as_mut().unwrap() - } -} - -impl SourceHtlcFunded for FakeEvents { - fn source_htlc_funded( +impl HtlcFunded for FakeEvents { + fn htlc_funded( &mut self, - _swap: &OngoingSwap, - ) -> &mut Box> { + _htlc_params: HtlcParams, + ) -> &mut events::Funded { self.source_htlc_funded.as_mut().unwrap() } } -impl SourceHtlcRefundedTargetHtlcFunded +impl SourceHtlcRefundedTargetHtlcFunded for FakeEvents { fn source_htlc_refunded_target_htlc_funded( &mut self, - _swap: &OngoingSwap, + _source_htlc_params: HtlcParams, + _target_htlc_params: HtlcParams, _source_htlc_location: &bitcoin_support::OutPoint, - ) -> &mut Box> { + ) -> &mut events::SourceRefundedOrTargetFunded { self.source_htlc_refunded_target_htlc_funded .as_mut() .unwrap() } } -impl TargetHtlcRedeemedOrRefunded - for FakeEvents -{ +impl TargetHtlcRedeemedOrRefunded for FakeEvents { fn target_htlc_redeemed_or_refunded( &mut self, - _swap: &OngoingSwap, + _target_htlc_params: HtlcParams, _target_htlc_location: ðereum_support::Address, - ) -> &mut Box> { + ) -> &mut events::RedeemedOrRefunded { unimplemented!() } } -impl SourceHtlcRedeemedOrRefunded - for FakeEvents -{ +impl SourceHtlcRedeemedOrRefunded for FakeEvents { fn source_htlc_redeemed_or_refunded( &mut self, - _swap: &OngoingSwap, + _source_htlc_params: HtlcParams, _target_htlc_location: &bitcoin_support::OutPoint, - ) -> &mut Box> { + ) -> &mut events::RedeemedOrRefunded { unimplemented!() } } -impl Events for FakeEvents {} +impl Events for FakeEvents {} -fn gen_start_state() -> Start { +fn gen_start_state() -> Start { Start { source_ledger_refund_identity: secp256k1_support::KeyPair::from_secret_key_slice( &hex::decode("18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725") @@ -120,19 +98,18 @@ fn gen_start_state() -> Start, + response_source: AlishaResponseSource, + state: SwapStates, events: FakeEvents, ) -> ( - SwapFuture, - impl Stream< - Item = SwapStates, - Error = (), - >, + SwapFuture, + impl Stream, Error = ()>, ) { let (state_sender, state_receiver) = mpsc::unbounded(); let context = Context { events: Box::new(events), state_repo: Arc::new(state_sender), + response_source: Box::new(response_source), }; let final_state_future = Swap::start_in(state, context); (final_state_future, state_receiver.map_err(|_| ())) @@ -147,9 +124,9 @@ macro_rules! run_state_machine { let state = $expected_state; expected_states.push(SwapStates::from(state)); ) - * + * - let number_of_expected_states = expected_states.len() + 1; + let number_of_expected_states = expected_states.len() + 1; let mut runtime = tokio::runtime::Runtime::new().unwrap(); @@ -172,9 +149,11 @@ fn when_swap_is_rejected_go_to_final_reject() { let start = gen_start_state(); let (state_machine, states) = init( + AlishaResponseSource { + response: Some(Box::new(future::ok(Err(SwapReject::Rejected)))), + }, start.clone().into(), FakeEvents { - response: Some(Box::new(future::ok(Err(SwapReject::Rejected)))), ..Default::default() }, ); @@ -184,7 +163,7 @@ fn when_swap_is_rejected_go_to_final_reject() { #[test] fn source_refunded() { - let bob_response = AcceptResponseBody { + let bob_response = StateMachineResponse { target_ledger_refund_identity: ethereum_support::Address::from_str( "71b9f69dcabb340a3fe229c3f94f1662ad85e5e8", ) @@ -199,9 +178,11 @@ fn source_refunded() { let start = gen_start_state(); let (state_machine, states) = init( + AlishaResponseSource { + response: Some(Box::new(future::ok(Ok(bob_response.clone())))), + }, start.clone().into(), FakeEvents { - response: Some(Box::new(future::ok(Ok(bob_response.clone())))), source_htlc_funded: Some(Box::new(future::ok(OutPoint { txid: Sha256dHash::from_data(b"funding"), vout: 0, @@ -232,10 +213,10 @@ fn source_refunded() { state_machine, states, Accepted { - swap: OngoingSwap::new(start.clone(), bob_response.clone()), + swap: OngoingSwap::new(start.clone(), bob_response.clone().into()), }, SourceFunded { - swap: OngoingSwap::new(start.clone(), bob_response.clone()), + swap: OngoingSwap::new(start.clone(), bob_response.clone().into()), source_htlc_location: OutPoint { txid: Sha256dHash::from_data(b"funding"), vout: 0 diff --git a/application/comit_node/src/swap_protocols/rfc003/state_store.rs b/application/comit_node/src/swap_protocols/rfc003/state_store.rs index 84e1acf119..4ede323e5e 100644 --- a/application/comit_node/src/swap_protocols/rfc003/state_store.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_store.rs @@ -4,10 +4,7 @@ use std::{ hash::Hash, sync::{Arc, Mutex, RwLock}, }; -use swap_protocols::{ - asset::Asset, - rfc003::{state_machine::SwapStates, ExtractSecret, IntoSecretHash, Ledger, SaveState}, -}; +use swap_protocols::rfc003::{roles::Role, state_machine::SwapStates, SaveState}; #[derive(Debug)] pub enum Error { @@ -16,28 +13,12 @@ pub enum Error { } pub trait StateStore: Send + Sync + 'static { - fn insert( - &self, - key: K, - state: SwapStates, - ) -> Result>, Error> - where - TL::Transaction: ExtractSecret; - - fn get( - &self, - key: &K, - ) -> Result, Error> - where - TL::Transaction: ExtractSecret; + fn insert(&self, key: K, state: SwapStates) -> Result>, Error>; + + fn get(&self, key: &K) -> Result, Error>; #[allow(clippy::type_complexity)] - fn save_state_for_key( - &self, - key: &K, - ) -> Result>, Error> - where - TL::Transaction: ExtractSecret; + fn save_state_for_key(&self, key: &K) -> Result>, Error>; } #[derive(Default, Debug)] @@ -46,14 +27,7 @@ pub struct InMemoryStateStore { } impl StateStore for InMemoryStateStore { - fn insert( - &self, - key: K, - state: SwapStates, - ) -> Result>, Error> - where - TL::Transaction: ExtractSecret, - { + fn insert(&self, key: K, state: SwapStates) -> Result>, Error> { let mut states = self.states.lock().unwrap(); if states.contains_key(&key) { @@ -68,40 +42,24 @@ impl StateStore for InMemorySta Ok(state) } - fn get( - &self, - key: &K, - ) -> Result, Error> - where - TL::Transaction: ExtractSecret, - { + fn get(&self, key: &K) -> Result, Error> { let states = self.states.lock().unwrap(); states .get(key) .map(|state| { - let state = state - .downcast_ref::>>>() - .unwrap(); + let state = state.downcast_ref::>>>().unwrap(); let state = state.read().unwrap(); state.clone() }) .ok_or(Error::NotFound) } - fn save_state_for_key( - &self, - key: &K, - ) -> Result>, Error> - where - TL::Transaction: ExtractSecret, - { + fn save_state_for_key(&self, key: &K) -> Result>, Error> { let states = self.states.lock().unwrap(); states .get(key) - .map(|state| -> Arc> { - let state = state - .downcast_ref::>>>() - .unwrap(); + .map(|state| -> Arc> { + let state = state.downcast_ref::>>>().unwrap(); state.clone() }) .ok_or(Error::NotFound) @@ -118,13 +76,13 @@ mod tests { use spectral::prelude::*; use swap_protocols::{ ledger::{Bitcoin, Ethereum}, - rfc003::{state_machine::Start, Secret}, + rfc003::{roles::test::Alisha, state_machine::Start, Secret}, }; #[test] fn store_get_and_save_state() { let state_store = InMemoryStateStore::default(); - let start_state = Start { + let start_state = Start:: { source_ledger_refund_identity: secp256k1_support::KeyPair::from_secret_key_slice( &hex::decode("18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725") .unwrap(), @@ -145,10 +103,10 @@ mod tests { let id = 1; let res = state_store.insert(id, state.clone()); - assert_that(&res).is_ok(); + assert!(res.is_ok()); let res = state_store.get(&id); - assert_that(&res).is_ok().is_equal_to(state); + assert_that(&res).is_ok_containing(state); let save_state = state_store.save_state_for_key(&id).unwrap(); diff --git a/application/comit_node/src/swap_protocols/rfc003/validation.rs b/application/comit_node/src/swap_protocols/rfc003/validation.rs index e4028315e3..c05dc45118 100644 --- a/application/comit_node/src/swap_protocols/rfc003/validation.rs +++ b/application/comit_node/src/swap_protocols/rfc003/validation.rs @@ -1,13 +1,7 @@ -use bitcoin_support::{BitcoinQuantity, FindOutput, OutPoint}; -use ethereum_support::{self, CalculateContractAddress, EtherQuantity}; use swap_protocols::{ + self, asset::Asset, - ledger::{Bitcoin, Ethereum}, - rfc003::{ - self, bitcoin::bitcoin_htlc_address, ethereum::ethereum_htlc, state_machine::OngoingSwap, - IntoSecretHash, - }, - Ledger, + rfc003::{state_machine::HtlcParams, Ledger}, }; #[derive(Debug, PartialEq)] @@ -16,437 +10,13 @@ pub enum Error { WrongTransaction, } -pub trait IsContainedInSourceLedgerTransaction: Send + Sync +pub trait IsContainedInTransaction: Send + Sync where - SL: rfc003::Ledger, - TL: rfc003::Ledger, + L: Ledger, Self: Asset, - TA: Asset, - S: IntoSecretHash, { - fn is_contained_in_source_ledger_transaction( - swap: OngoingSwap, - transaction: SL::Transaction, - ) -> Result>; -} - -pub trait IsContainedInTargetLedgerTransaction: Send + Sync -where - SL: rfc003::Ledger, - TL: rfc003::Ledger, - SA: Asset, - Self: Asset, - S: IntoSecretHash, -{ - fn is_contained_in_target_ledger_transaction( - swap: OngoingSwap, - tx: TL::Transaction, - ) -> Result>; -} - -impl IsContainedInSourceLedgerTransaction for BitcoinQuantity -where - TL: rfc003::Ledger, - TA: Asset, - S: IntoSecretHash, -{ - fn is_contained_in_source_ledger_transaction( - swap: OngoingSwap, - transaction: ::Transaction, - ) -> Result> { - let address = bitcoin_htlc_address(&swap); - - let (vout, txout) = transaction - .find_output(&address) - .ok_or(Error::WrongTransaction)?; - - let location = OutPoint { - txid: transaction.txid(), - vout: vout as u32, - }; - - let actual_value = BitcoinQuantity::from_satoshi(txout.value); - let required_value = swap.source_asset; - - debug!("Value of HTLC at {:?} is {}", location, actual_value); - - let has_enough_money = actual_value >= required_value; - - trace!( - "{} >= {} -> {}", - actual_value, - required_value, - has_enough_money - ); - if has_enough_money { - Ok(location) - } else { - Err(Error::UnexpectedAsset { - found: actual_value, - expected: required_value, - }) - } - } -} - -impl IsContainedInTargetLedgerTransaction for EtherQuantity -where - SL: rfc003::Ledger, - SA: Asset, - S: IntoSecretHash, -{ - fn is_contained_in_target_ledger_transaction( - swap: OngoingSwap, - tx: ethereum_support::Transaction, - ) -> Result> { - if tx.to != None { - return Err(Error::WrongTransaction); - } - - if tx.input != ethereum_htlc(&swap).compile_to_hex().into() { - return Err(Error::WrongTransaction); - } - - if tx.value < swap.target_asset.wei() { - return Err(Error::UnexpectedAsset { - found: EtherQuantity::from_wei(tx.value), - expected: swap.target_asset, - }); - } - - let from_address: ethereum_support::Address = tx.from; - - Ok(from_address.calculate_contract_address(&tx.nonce)) - } -} - -#[cfg(test)] -mod tests { - extern crate bitcoin_support; - extern crate ethereum_support; - extern crate hex; - extern crate secp256k1_support; - - use super::{Error as ValidationError, *}; - use bitcoin_rpc_client::rpc::{ - ScriptPubKey, ScriptType, SerializedRawTransaction, TransactionOutput, - VerboseRawTransaction, - }; - use bitcoin_support::{BitcoinQuantity, Blocks, Sha256dHash, Transaction}; - use ethereum_support::{ - web3::types::{Bytes, H256, U256}, - EtherQuantity, - }; - use hex::FromHex; - use spectral::prelude::*; - use std::str::FromStr; - use swap_protocols::{ - ledger::Ethereum, - rfc003::{ - ethereum::{ethereum_htlc, Seconds}, - state_machine::*, - AcceptResponseBody, Secret, - }, - }; - - fn gen_start_state( - bitcoin_amount: f64, - ether_amount: U256, - ) -> Start { - Start { - source_ledger_refund_identity: secp256k1_support::KeyPair::from_secret_key_slice( - &hex::decode("18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725") - .unwrap(), - ) - .unwrap(), - target_ledger_success_identity: ethereum_support::Address::from_str( - "8457037fcd80a8650c4692d7fcfc1d0a96b92867", - ) - .unwrap(), - source_ledger: Bitcoin::regtest(), - target_ledger: Ethereum::default(), - source_asset: BitcoinQuantity::from_bitcoin(bitcoin_amount), - target_asset: EtherQuantity::from_wei(ether_amount), - source_ledger_lock_duration: Blocks::from(144), - secret: Secret::from(*b"hello world, you are beautiful!!"), - } - } - - fn gen_response() -> AcceptResponseBody { - AcceptResponseBody { - target_ledger_refund_identity: ethereum_support::Address::from_str( - "71b9f69dcabb340a3fe229c3f94f1662ad85e5e8", - ) - .unwrap(), - source_ledger_success_identity: bitcoin_support::PubkeyHash::from_hex( - "d38e554430c4035f2877a579a07a99886153f071", - ) - .unwrap(), - target_ledger_lock_duration: Seconds(42), - } - } - - #[test] - fn bitcoin_transaction_contains_output_with_sufficient_money() { - let bitcoin_amount = 1.0; - - let start = gen_start_state(bitcoin_amount, U256::from(10)); - let response = gen_response(); - let swap = OngoingSwap::new(start, response); - - let script = bitcoin_htlc_address(&swap).script_pubkey(); - - let script_pub_key = ScriptPubKey { - asm: String::from(""), - hex: script.clone(), - req_sigs: None, - script_type: ScriptType::NullData, - addresses: None, - }; - - let transaction_output = TransactionOutput { - value: swap.clone().source_asset.bitcoin(), - n: 1, - script_pub_key, - }; - - let transaction = VerboseRawTransaction { - txid: Sha256dHash::from_data(b"a"), - hash: String::from(""), - size: 0, - vsize: 0, - version: 1, - locktime: 42, - vin: Vec::new(), - vout: vec![transaction_output], - hex: SerializedRawTransaction(String::from("")), - blockhash: Sha256dHash::from_data(b"blockhash"), - confirmations: 0, - time: 0, - blocktime: 0, - }; - - let bitcoin_transaction: Transaction = transaction.into(); - - let result = BitcoinQuantity::is_contained_in_source_ledger_transaction( - swap.clone(), - bitcoin_transaction.clone(), - ); - - let txid = bitcoin_transaction.txid(); - let expected_outpoint = OutPoint { txid, vout: 0 }; - - assert_that(&result).is_ok_containing(expected_outpoint) - } - - #[test] - fn bitcoin_transaction_does_not_contain_output() { - let bitcoin_amount = 1.0; - - let start = gen_start_state(bitcoin_amount, U256::from(10)); - let response = gen_response(); - let swap = OngoingSwap::new(start, response); - - let transaction = VerboseRawTransaction { - txid: Sha256dHash::from_data(b"refunded"), - hash: String::from(""), - size: 0, - vsize: 0, - version: 1, - locktime: 42, - vin: Vec::new(), - vout: Vec::new(), - hex: SerializedRawTransaction(String::from("")), - blockhash: Sha256dHash::from_data(b"blockhash"), - confirmations: 0, - time: 0, - blocktime: 0, - }; - - let result = - BitcoinQuantity::is_contained_in_source_ledger_transaction(swap, transaction.into()); - - assert_that(&result).is_err_containing(ValidationError::WrongTransaction) - } - - #[test] - fn bitcoin_transaction_does_not_contain_enough_money() { - let bitcoin_amount = 1.0; - - let start = gen_start_state(bitcoin_amount, U256::from(10)); - let response = gen_response(); - let swap = OngoingSwap::new(start, response); - - let script = bitcoin_htlc_address(&swap).script_pubkey(); - let script_pub_key = ScriptPubKey { - asm: String::from(""), - hex: script.clone(), - req_sigs: None, - script_type: ScriptType::NullData, - addresses: None, - }; - - let provided_bitcoin_amount = 0.5; - - let transaction_output = TransactionOutput { - value: provided_bitcoin_amount, - n: 1, - script_pub_key, - }; - - let transaction = VerboseRawTransaction { - txid: Sha256dHash::from_data(b"a"), - hash: String::from(""), - size: 0, - vsize: 0, - version: 1, - locktime: 42, - vin: Vec::new(), - vout: vec![transaction_output], - hex: SerializedRawTransaction(String::from("")), - blockhash: Sha256dHash::from_data(b"blockhash"), - confirmations: 0, - time: 0, - blocktime: 0, - }; - - let result = - BitcoinQuantity::is_contained_in_source_ledger_transaction(swap, transaction.into()); - - let expected_error = ValidationError::UnexpectedAsset { - found: BitcoinQuantity::from_bitcoin(provided_bitcoin_amount), - expected: BitcoinQuantity::from_bitcoin(bitcoin_amount), - }; - - assert_that(&result).is_err_containing(expected_error) - } - - #[test] - pub fn ethereum_tx_has_correct_funding_and_correct_data_should_return_contract_address() { - let ether_amount = U256::from(10); - - let start = gen_start_state(1.0, ether_amount); - let response = gen_response(); - let swap = OngoingSwap::new(start, response); - - let provided_ether_amount = U256::from(10); - let ethereum_transaction = ethereum_support::Transaction { - hash: H256::from(123), - nonce: U256::from(1), - block_hash: None, - block_number: None, - transaction_index: None, - from: "0a81e8be41b21f651a71aab1a85c6813b8bbccf8".parse().unwrap(), - to: None, - value: provided_ether_amount, - gas_price: U256::from(0), - gas: U256::from(0), - input: ethereum_htlc(&swap).compile_to_hex().into(), - }; - - let expected_address = - ethereum_support::Address::from_str("994a1e7928556ba81b85bf3c665a3f4a0f0d4cd9") - .unwrap(); - - let result = - EtherQuantity::is_contained_in_target_ledger_transaction(swap, ethereum_transaction); - - assert_that(&result).is_ok_containing(expected_address) - } - - #[test] - pub fn ethereum_tx_has_incorrect_funding_and_correct_data_should_return_error() { - let ether_amount = U256::from(10); - - let start = gen_start_state(1.0, ether_amount); - let response = gen_response(); - let swap = OngoingSwap::new(start, response); - - let provided_ether_amount = U256::from(9); - let ethereum_transaction = ethereum_support::Transaction { - hash: H256::from(123), - nonce: U256::from(1), - block_hash: None, - block_number: None, - transaction_index: None, - from: "0a81e8be41b21f651a71aab1a85c6813b8bbccf8".parse().unwrap(), - to: None, - value: provided_ether_amount, - gas_price: U256::from(0), - gas: U256::from(0), - input: ethereum_htlc(&swap).compile_to_hex().into(), - }; - - let result = - EtherQuantity::is_contained_in_target_ledger_transaction(swap, ethereum_transaction); - - let expected_error = ValidationError::UnexpectedAsset { - found: EtherQuantity::from_wei(provided_ether_amount), - expected: EtherQuantity::from_wei(ether_amount), - }; - - assert_that(&result).is_err_containing(expected_error) - } - - #[test] - pub fn ethereum_tx_has_correct_funding_but_incorrect_data_should_return_error() { - let ether_amount = U256::from(10); - - let start = gen_start_state(1.0, ether_amount); - let response = gen_response(); - let swap = OngoingSwap::new(start, response); - - let provided_ether_amount = U256::from(9); - let ethereum_transaction = ethereum_support::Transaction { - hash: H256::from(123), - nonce: U256::from(1), - block_hash: None, - block_number: None, - transaction_index: None, - from: "0a81e8be41b21f651a71aab1a85c6813b8bbccf8".parse().unwrap(), - to: None, - value: provided_ether_amount, - gas_price: U256::from(0), - gas: U256::from(0), - input: Bytes::from(vec![1, 2, 3]), - }; - - let result = - EtherQuantity::is_contained_in_target_ledger_transaction(swap, ethereum_transaction); - - let expected_error = ValidationError::WrongTransaction; - - assert_that(&result).is_err_containing(expected_error) - } - - #[test] - pub fn ethereum_tx_has_correct_funding_but_not_sending_to_0_should_return_error() { - let ether_amount = U256::from(10); - - let start = gen_start_state(1.0, ether_amount); - let response = gen_response(); - let swap = OngoingSwap::new(start, response); - - let provided_ether_amount = U256::from(9); - let ethereum_transaction = ethereum_support::Transaction { - hash: H256::from(123), - nonce: U256::from(1), - block_hash: None, - block_number: None, - transaction_index: None, - from: "0a81e8be41b21f651a71aab1a85c6813b8bbccf8".parse().unwrap(), - to: Some("0000000000000000000000000000000000000001".parse().unwrap()), - value: provided_ether_amount, - gas_price: U256::from(0), - gas: U256::from(0), - input: ethereum_htlc(&swap).compile_to_hex().into(), - }; - - let result = - EtherQuantity::is_contained_in_target_ledger_transaction(swap, ethereum_transaction); - - let expected_error = ValidationError::WrongTransaction; - - assert_that(&result).is_err_containing(expected_error) - } + fn is_contained_in_transaction( + htlc_params: &HtlcParams, + transaction: ::Transaction, + ) -> Result>; } From 14fe1b91387c69a6c6ca16db6cec60c6b1e075a8 Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Fri, 16 Nov 2018 15:05:04 +1100 Subject: [PATCH 02/10] Update package-lock.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit running end-to-end updated it for me🤷‍♂️ --- api_tests/package-lock.json | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/api_tests/package-lock.json b/api_tests/package-lock.json index 70c124beec..dc748015e4 100644 --- a/api_tests/package-lock.json +++ b/api_tests/package-lock.json @@ -2913,19 +2913,8 @@ "integrity": "sha512-wAnENuZx75T5ZSrT2De2LOaUuPf2yRjq1VfcbD7+Zd79F3DZZLBJcPyCNVQ1U0fAXt0wfgCKl7sVw5pffqR9Bw==", "requires": { "underscore": "1.8.3", - "web3-core-helpers": "1.0.0-beta.36" - }, - "dependencies": { - "websocket": { - "version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", - "from": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", - "requires": { - "debug": "^2.2.0", - "nan": "^2.3.3", - "typedarray-to-buffer": "^3.1.2", - "yaeti": "^0.0.6" - } - } + "web3-core-helpers": "1.0.0-beta.36", + "websocket": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2" } }, "web3-shh": { @@ -2960,6 +2949,16 @@ } } }, + "websocket": { + "version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", + "from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible", + "requires": { + "debug": "^2.2.0", + "nan": "^2.3.3", + "typedarray-to-buffer": "^3.1.2", + "yaeti": "^0.0.6" + } + }, "wif": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", @@ -3034,6 +3033,11 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", From 13970b18b287337286d3ffa5a49755b40130528c Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Fri, 16 Nov 2018 17:19:33 +1100 Subject: [PATCH 03/10] Add test for Bob to get to SourceRefunded in state machine --- .../src/swap_protocols/rfc003/roles.rs | 52 ++----- .../rfc003/state_machine_test.rs | 130 ++++++++++++++---- 2 files changed, 121 insertions(+), 61 deletions(-) diff --git a/application/comit_node/src/swap_protocols/rfc003/roles.rs b/application/comit_node/src/swap_protocols/rfc003/roles.rs index 1ca3d7fae6..d51d32c0fe 100644 --- a/application/comit_node/src/swap_protocols/rfc003/roles.rs +++ b/application/comit_node/src/swap_protocols/rfc003/roles.rs @@ -131,62 +131,40 @@ pub mod test { use super::*; use bitcoin_support::{self, BitcoinQuantity}; use ethereum_support::{self, EtherQuantity}; - use secp256k1_support::KeyPair; use swap_protocols::{ ledger::{Bitcoin, Ethereum}, rfc003::ethereum::Seconds, }; - pub struct Alisha {} - - impl Debug for Alisha { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "Alisha") - } - } + pub type Alisha = Alice; + pub type Bobisha = Bob; impl PartialEq for Alisha { fn eq(&self, _: &Alisha) -> bool { - unreachable!() + unreachable!( + "Rust erroneously forces me to be PartialEq even though I'm never instantiated" + ) } } - impl Clone for Alisha { - fn clone(&self) -> Self { - unreachable!() + impl PartialEq for Bobisha { + fn eq(&self, _: &Bobisha) -> bool { + unreachable!( + "Rust erroneously forces me to be PartialEq even though I'm never instantiated" + ) } } - impl Role for Alisha { - type SourceLedger = Bitcoin; - type TargetLedger = Ethereum; - type SourceAsset = BitcoinQuantity; - type TargetAsset = EtherQuantity; - type SourceSuccessHtlcIdentity = bitcoin_support::PubkeyHash; - type SourceRefundHtlcIdentity = KeyPair; - type TargetSuccessHtlcIdentity = ethereum_support::Address; - type TargetRefundHtlcIdentity = ethereum_support::Address; - type Secret = Secret; - } - #[allow(missing_debug_implementations)] - pub struct AlishaResponseSource { - pub response: Option< - Box< - StateMachineResponseFuture< - bitcoin_support::PubkeyHash, - ethereum_support::Address, - Seconds, - >, - >, - >, + pub struct FakeResponseSource { + pub response: Option>>, } - impl ResponseSource for AlishaResponseSource { + impl ResponseSource for FakeResponseSource { fn request_responded( &mut self, - _request: &Request, - ) -> &mut ResponseFuture { + _request: &Request, + ) -> &mut ResponseFuture { self.response.as_mut().unwrap() } } diff --git a/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs index c7371858a4..428ada9db9 100644 --- a/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs @@ -9,9 +9,12 @@ use swap_protocols::{ self, Events, HtlcFunded, SourceHtlcRedeemedOrRefunded, SourceHtlcRefundedTargetHtlcFunded, TargetHtlcRedeemedOrRefunded, }, - roles::test::{Alisha, AlishaResponseSource}, + roles::{ + test::{Alisha, Bobisha, FakeResponseSource}, + Bob, Role, + }, state_machine::*, - Secret, + Ledger, Secret, }, }; @@ -97,22 +100,18 @@ fn gen_start_state() -> Start { } } -fn init( - response_source: AlishaResponseSource, - state: SwapStates, - events: FakeEvents, -) -> ( - SwapFuture, - impl Stream, Error = ()>, -) { - let (state_sender, state_receiver) = mpsc::unbounded(); - let context = Context { - events: Box::new(events), - state_repo: Arc::new(state_sender), - response_source: Box::new(response_source), - }; - let final_state_future = Swap::start_in(state, context); - (final_state_future, state_receiver.map_err(|_| ())) +macro_rules! init { + ($role:ty, $response_source:expr, $state:expr, $events:expr) => {{ + let (state_sender, state_receiver) = mpsc::unbounded(); + let context = Context { + events: Box::new($events), + state_repo: Arc::new(state_sender), + response_source: Box::new($response_source), + }; + let state: SwapStates<$role> = $state; + let final_state_future = Swap::start_in(state, context); + (final_state_future, state_receiver.map_err(|_| ())) + }}; } macro_rules! run_state_machine { @@ -148,14 +147,15 @@ macro_rules! run_state_machine { fn when_swap_is_rejected_go_to_final_reject() { let start = gen_start_state(); - let (state_machine, states) = init( - AlishaResponseSource { + let (state_machine, states) = init!( + Alisha, + FakeResponseSource:: { response: Some(Box::new(future::ok(Err(SwapReject::Rejected)))), }, start.clone().into(), FakeEvents { ..Default::default() - }, + } ); run_state_machine!(state_machine, states); @@ -177,8 +177,9 @@ fn source_refunded() { let start = gen_start_state(); - let (state_machine, states) = init( - AlishaResponseSource { + let (state_machine, states) = init!( + Alisha, + FakeResponseSource:: { response: Some(Box::new(future::ok(Ok(bob_response.clone())))), }, start.clone().into(), @@ -206,7 +207,7 @@ fn source_refunded() { .into(), )))), ..Default::default() - }, + } ); run_state_machine!( @@ -224,3 +225,84 @@ fn source_refunded() { } ); } + +#[test] +fn bob_transition_source_refunded() { + let start = Start { + source_ledger_refund_identity: bitcoin_support::PubkeyHash::from_hex( + "d38e554430c4035f2877a579a07a99886153f071", + ) + .unwrap(), + target_ledger_success_identity: ethereum_support::Address::from_str( + "8457037fcd80a8650c4692d7fcfc1d0a96b92867", + ) + .unwrap(), + source_ledger: Bitcoin::regtest(), + target_ledger: Ethereum::default(), + source_asset: BitcoinQuantity::from_bitcoin(1.0), + target_asset: EtherQuantity::from_eth(10.0), + source_ledger_lock_duration: Blocks::from(144), + secret: Secret::from(*b"hello world, you are beautiful!!").hash(), + }; + + let response = StateMachineResponse { + source_ledger_success_identity: secp256k1_support::KeyPair::from_secret_key_slice( + &hex::decode("18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725") + .unwrap(), + ) + .unwrap(), + target_ledger_refund_identity: ethereum_support::Address::from_str( + "8457037fcd80a8650c4692d7fcfc1d0a96b92867", + ) + .unwrap(), + target_ledger_lock_duration: Seconds(42), + }; + + let (state_machine, states) = init!( + Bobisha, + FakeResponseSource:: { + response: Some(Box::new(future::ok(Ok(response.clone())))) + }, + start.clone().into(), + FakeEvents { + source_htlc_funded: Some(Box::new(future::ok(OutPoint { + txid: Sha256dHash::from_data(b"funding"), + vout: 0, + }))), + source_htlc_refunded_target_htlc_funded: Some(Box::new(future::ok(Either::A( + VerboseRawTransaction { + txid: Sha256dHash::from_data(b"refunded"), + hash: String::from(""), + size: 0, + vsize: 0, + version: 1, + locktime: 42, + vin: Vec::new(), + vout: Vec::new(), + hex: SerializedRawTransaction(String::from("")), + blockhash: Sha256dHash::from_data(b"blockhash"), + confirmations: 0, + time: 0, + blocktime: 0, + } + .into(), + )))), + ..Default::default() + } + ); + + run_state_machine!( + state_machine, + states, + Accepted { + swap: OngoingSwap::new(start.clone(), response.clone().into()) + }, + SourceFunded { + swap: OngoingSwap::new(start.clone(), response.clone().into()), + source_htlc_location: OutPoint { + txid: Sha256dHash::from_data(b"funding"), + vout: 0 + } + } + ); +} From d99200e1760ad781bef2917907996b739982b422 Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Mon, 19 Nov 2018 09:47:14 +1100 Subject: [PATCH 04/10] Re-implement ether IsContainedInTransaction --- .../rfc003/ethereum/validation.rs | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs b/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs index fe973337e2..b1da4a7451 100644 --- a/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs +++ b/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs @@ -1,16 +1,35 @@ -use swap_protocols::ledger::Ethereum; - -use ethereum_support::{self, EtherQuantity}; -use swap_protocols::rfc003::{ - state_machine::HtlcParams, - validation::{Error, IsContainedInTransaction}, +use ethereum_support::{self, CalculateContractAddress, EtherQuantity}; +use swap_protocols::{ + ledger::Ethereum, + rfc003::{ + ethereum::{EtherHtlc, Htlc}, + state_machine::HtlcParams, + validation::{Error, IsContainedInTransaction}, + }, }; impl IsContainedInTransaction for EtherQuantity { fn is_contained_in_transaction( - _htlc_params: &HtlcParams, - _transaction: ethereum_support::Transaction, + htlc_params: &HtlcParams, + tx: ethereum_support::Transaction, ) -> Result> { - unimplemented!() + if tx.to != None { + return Err(Error::WrongTransaction); + } + + if tx.input != EtherHtlc::from(htlc_params.clone()).compile_to_hex().into() { + return Err(Error::WrongTransaction); + } + + if tx.value < htlc_params.asset.wei() { + return Err(Error::UnexpectedAsset { + found: EtherQuantity::from_wei(tx.value), + expected: htlc_params.asset, + }); + } + + let from_address: ethereum_support::Address = tx.from; + + Ok(from_address.calculate_contract_address(&tx.nonce)) } } From 52b013dfe782c6760a3fe9861ea4017b9e2735f8 Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Mon, 19 Nov 2018 12:09:05 +1100 Subject: [PATCH 05/10] Move/Rename various things in rfc003 --- .../swap_protocols/rfc003/bitcoin/queries.rs | 4 +- .../rfc003/bitcoin/validation.rs | 2 +- .../rfc003/ethereum/validation.rs | 2 +- .../rfc003/events/{default.rs => lqs.rs} | 18 ++--- .../src/swap_protocols/rfc003/events/mod.rs | 46 +++++++++---- .../swap_protocols/rfc003/events/response.rs | 40 +++++++++++ ...tion.rs => is_contained_in_transaction.rs} | 0 .../src/swap_protocols/rfc003/ledger.rs | 24 ++++++- .../src/swap_protocols/rfc003/mod.rs | 5 +- .../src/swap_protocols/rfc003/roles.rs | 69 +++---------------- .../src/swap_protocols/rfc003/secret.rs | 20 ------ .../swap_protocols/rfc003/state_machine.rs | 48 ++++--------- .../rfc003/state_machine_test.rs | 39 +++++------ 13 files changed, 155 insertions(+), 162 deletions(-) rename application/comit_node/src/swap_protocols/rfc003/events/{default.rs => lqs.rs} (94%) create mode 100644 application/comit_node/src/swap_protocols/rfc003/events/response.rs rename application/comit_node/src/swap_protocols/rfc003/{validation.rs => is_contained_in_transaction.rs} (100%) diff --git a/application/comit_node/src/swap_protocols/rfc003/bitcoin/queries.rs b/application/comit_node/src/swap_protocols/rfc003/bitcoin/queries.rs index 808388c9ac..f86424f84d 100644 --- a/application/comit_node/src/swap_protocols/rfc003/bitcoin/queries.rs +++ b/application/comit_node/src/swap_protocols/rfc003/bitcoin/queries.rs @@ -3,7 +3,6 @@ use ledger_query_service::BitcoinQuery; use swap_protocols::{ ledger::Bitcoin, rfc003::{ - bitcoin::Htlc, events::{NewHtlcFundedQuery, NewHtlcRedeemedQuery, NewHtlcRefundedQuery}, state_machine::HtlcParams, }, @@ -11,9 +10,8 @@ use swap_protocols::{ impl NewHtlcFundedQuery for BitcoinQuery { fn new_htlc_funded_query(htlc_params: &HtlcParams) -> Self { - let htlc: Htlc = htlc_params.clone().into(); BitcoinQuery::Transaction { - to_address: Some(htlc.compute_address(htlc_params.ledger.network)), + to_address: Some(htlc_params.compute_address()), from_outpoint: None, unlock_script: None, } diff --git a/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs b/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs index ee196817c1..f3bdc1ec27 100644 --- a/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs +++ b/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs @@ -2,8 +2,8 @@ use bitcoin_support::{BitcoinQuantity, FindOutput, OutPoint, Transaction}; use swap_protocols::{ ledger::Bitcoin, rfc003::{ + is_contained_in_transaction::{Error, IsContainedInTransaction}, state_machine::HtlcParams, - validation::{Error, IsContainedInTransaction}, }, }; diff --git a/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs b/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs index b1da4a7451..69e6c14440 100644 --- a/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs +++ b/application/comit_node/src/swap_protocols/rfc003/ethereum/validation.rs @@ -3,8 +3,8 @@ use swap_protocols::{ ledger::Ethereum, rfc003::{ ethereum::{EtherHtlc, Htlc}, + is_contained_in_transaction::{Error, IsContainedInTransaction}, state_machine::HtlcParams, - validation::{Error, IsContainedInTransaction}, }, }; diff --git a/application/comit_node/src/swap_protocols/rfc003/events/default.rs b/application/comit_node/src/swap_protocols/rfc003/events/lqs.rs similarity index 94% rename from application/comit_node/src/swap_protocols/rfc003/events/default.rs rename to application/comit_node/src/swap_protocols/rfc003/events/lqs.rs index 994034cbc1..34eedb47ce 100644 --- a/application/comit_node/src/swap_protocols/rfc003/events/default.rs +++ b/application/comit_node/src/swap_protocols/rfc003/events/lqs.rs @@ -5,19 +5,19 @@ use swap_protocols::{ rfc003::{ self, events::{ - Events, Funded, HtlcFunded, NewHtlcFundedQuery, NewHtlcRedeemedQuery, + Funded, HtlcFunded, LedgerEvents, NewHtlcFundedQuery, NewHtlcRedeemedQuery, NewHtlcRefundedQuery, RedeemedOrRefunded, SourceHtlcRedeemedOrRefunded, SourceHtlcRefundedTargetHtlcFunded, SourceRefundedOrTargetFunded, TargetHtlcRedeemedOrRefunded, }, + is_contained_in_transaction::IsContainedInTransaction, state_machine::HtlcParams, - validation::IsContainedInTransaction, Ledger, }, }; #[allow(missing_debug_implementations)] -pub struct DefaultEvents { +pub struct LqsEvents { create_source_ledger_query: QueryIdCache, source_ledger_first_match: FirstMatch, @@ -31,7 +31,7 @@ pub struct DefaultEvents target_htlc_redeemed_or_refunded: Option>>, } -impl HtlcFunded for DefaultEvents +impl HtlcFunded for LqsEvents where SL: Ledger, TL: Ledger, @@ -63,7 +63,7 @@ where } impl SourceHtlcRefundedTargetHtlcFunded - for DefaultEvents + for LqsEvents where SL: Ledger, TL: Ledger, @@ -125,7 +125,7 @@ where } impl TargetHtlcRedeemedOrRefunded - for DefaultEvents + for LqsEvents where SL: Ledger, TL: Ledger, @@ -179,7 +179,7 @@ where } impl SourceHtlcRedeemedOrRefunded - for DefaultEvents + for LqsEvents where SL: Ledger, TL: Ledger, @@ -232,8 +232,8 @@ where } } -impl Events - for DefaultEvents +impl LedgerEvents + for LqsEvents where SL: Ledger, TL: Ledger, diff --git a/application/comit_node/src/swap_protocols/rfc003/events/mod.rs b/application/comit_node/src/swap_protocols/rfc003/events/mod.rs index 713513826d..bd8caa783a 100644 --- a/application/comit_node/src/swap_protocols/rfc003/events/mod.rs +++ b/application/comit_node/src/swap_protocols/rfc003/events/mod.rs @@ -5,16 +5,31 @@ use ledger_query_service::Query; use swap_protocols::{ asset::Asset, - rfc003::{self, ledger::Ledger}, + rfc003::{self, ledger::Ledger, messages::Request}, }; use tokio::{self, prelude::future::Either}; -pub use self::default::DefaultEvents; -mod default; -use swap_protocols::rfc003::state_machine::HtlcParams; +pub use self::lqs::LqsEvents; +mod lqs; +mod response; +use comit_client::SwapReject; +use swap_protocols::rfc003::{ + roles::Role, + state_machine::{HtlcParams, StateMachineResponse}, +}; type Future = tokio::prelude::Future + Send; +pub type StateMachineResponseFuture = + Future, SwapReject>>; + +#[allow(type_alias_bounds)] +pub type ResponseFuture = StateMachineResponseFuture< + R::SourceSuccessHtlcIdentity, + R::TargetRefundHtlcIdentity, + ::LockDuration, +>; + pub type Funded = Future; pub type Refunded = Future; pub type Redeemed = Future; @@ -22,6 +37,21 @@ pub type SourceRefundedOrTargetFunded = Future>; pub type RedeemedOrRefunded = Future>; +pub trait LedgerEvents: + HtlcFunded + + SourceHtlcRefundedTargetHtlcFunded + + SourceHtlcRedeemedOrRefunded + + TargetHtlcRedeemedOrRefunded +{ +} + +pub trait ResponseEvent { + fn request_responded( + &mut self, + request: &Request, + ) -> &mut ResponseFuture; +} + pub trait HtlcFunded: Send { fn htlc_funded(&mut self, htlc_params: HtlcParams) -> &mut Funded; } @@ -53,14 +83,6 @@ pub trait TargetHtlcRedeemedOrRefunded: Send { ) -> &mut RedeemedOrRefunded; } -pub trait Events: - HtlcFunded - + SourceHtlcRefundedTargetHtlcFunded - + SourceHtlcRedeemedOrRefunded - + TargetHtlcRedeemedOrRefunded -{ -} - pub trait NewHtlcFundedQuery: Send + Sync where Self: Query, diff --git a/application/comit_node/src/swap_protocols/rfc003/events/response.rs b/application/comit_node/src/swap_protocols/rfc003/events/response.rs new file mode 100644 index 0000000000..86837bab72 --- /dev/null +++ b/application/comit_node/src/swap_protocols/rfc003/events/response.rs @@ -0,0 +1,40 @@ +use comit_client; +use futures::Future; +use std::sync::Arc; +use swap_protocols::rfc003::roles::Alice; + +use swap_protocols::{ + asset::Asset, + rfc003::{ + self, + events::{ResponseEvent, ResponseFuture, StateMachineResponseFuture}, + ledger::Ledger, + messages::Request, + }, +}; + +struct AliceComitClient { + #[allow(clippy::type_complexity)] + response_future: + Option>>, + client: Arc, +} + +impl + ResponseEvent> for AliceComitClient +{ + fn request_responded( + &mut self, + request: &Request, + ) -> &mut ResponseFuture> { + let client = Arc::clone(&self.client); + self.response_future.get_or_insert_with(|| { + Box::new( + client + .send_swap_request(request.clone()) + .map_err(rfc003::Error::SwapResponse) + .map(|result| result.map(Into::into)), + ) + }) + } +} diff --git a/application/comit_node/src/swap_protocols/rfc003/validation.rs b/application/comit_node/src/swap_protocols/rfc003/is_contained_in_transaction.rs similarity index 100% rename from application/comit_node/src/swap_protocols/rfc003/validation.rs rename to application/comit_node/src/swap_protocols/rfc003/is_contained_in_transaction.rs diff --git a/application/comit_node/src/swap_protocols/rfc003/ledger.rs b/application/comit_node/src/swap_protocols/rfc003/ledger.rs index c333f8dd82..b2b0da7a42 100644 --- a/application/comit_node/src/swap_protocols/rfc003/ledger.rs +++ b/application/comit_node/src/swap_protocols/rfc003/ledger.rs @@ -1,6 +1,9 @@ use serde::{de::DeserializeOwned, Serialize}; use std::fmt::Debug; -use swap_protocols::{self, rfc003::secret::LedgerExtractSecret}; +use swap_protocols::{ + self, + rfc003::secret::{ExtractSecret, Secret, SecretHash}, +}; pub trait Ledger: LedgerExtractSecret { type LockDuration: PartialEq @@ -19,3 +22,22 @@ pub trait Ledger: LedgerExtractSecret { + Debug + Into<::Identity>; } + +pub trait LedgerExtractSecret: swap_protocols::ledger::Ledger { + fn extract_secret_from_transaction( + txn: &Self::Transaction, + secret_hash: &SecretHash, + ) -> Option; +} + +impl LedgerExtractSecret for L +where + L::Transaction: ExtractSecret, +{ + fn extract_secret_from_transaction( + txn: &Self::Transaction, + secret_hash: &SecretHash, + ) -> Option { + txn.extract_secret(secret_hash) + } +} diff --git a/application/comit_node/src/swap_protocols/rfc003/mod.rs b/application/comit_node/src/swap_protocols/rfc003/mod.rs index c7241b885d..4078377d35 100644 --- a/application/comit_node/src/swap_protocols/rfc003/mod.rs +++ b/application/comit_node/src/swap_protocols/rfc003/mod.rs @@ -7,13 +7,14 @@ pub mod alice_ledger_actor; pub mod bitcoin; pub mod ethereum; pub mod events; +pub mod is_contained_in_transaction; pub mod ledger_htlc_service; pub mod roles; pub mod state_machine; pub mod state_store; -pub mod validation; mod error; + mod ledger; mod messages; mod outcome; @@ -25,7 +26,7 @@ mod state_machine_test; pub use self::{ error::Error, - ledger::Ledger, + ledger::{Ledger, LedgerExtractSecret}, messages::*, outcome::SwapOutcome, save_state::SaveState, diff --git a/application/comit_node/src/swap_protocols/rfc003/roles.rs b/application/comit_node/src/swap_protocols/rfc003/roles.rs index d51d32c0fe..8973b0a483 100644 --- a/application/comit_node/src/swap_protocols/rfc003/roles.rs +++ b/application/comit_node/src/swap_protocols/rfc003/roles.rs @@ -1,21 +1,9 @@ -use comit_client; -use futures::Future; -use std::{ - fmt::{self, Debug}, - marker::PhantomData, - sync::Arc, -}; +use std::{fmt::Debug, marker::PhantomData}; use swap_protocols::{ self, asset::Asset, - rfc003::{ - self, - ledger::Ledger, - messages::Request, - state_machine::{ResponseFuture, ResponseSource, StateMachineResponseFuture}, - Secret, SecretHash, - }, + rfc003::{ledger::Ledger, Secret, SecretHash}, }; pub trait Role: Send + Clone + 'static { @@ -54,49 +42,11 @@ pub trait Role: Send + Clone + 'static { type Secret: Send + Sync + Clone + Into + Debug + PartialEq; } -#[allow(dead_code)] // TODO: Remove "allow" when used -struct AliceComitClient { - #[allow(clippy::type_complexity)] - response_future: - Option>>, - client: Arc, -} - -impl - ResponseSource> for AliceComitClient -{ - fn request_responded( - &mut self, - request: &Request, - ) -> &mut ResponseFuture> { - let client = Arc::clone(&self.client); - self.response_future.get_or_insert_with(|| { - Box::new( - client - .send_swap_request(request.clone()) - .map_err(rfc003::Error::SwapResponse) - .map(|result| result.map(Into::into)), - ) - }) - } -} - +#[derive(Clone, Debug)] pub struct Alice { phantom_data: PhantomData<(SL, TL, SA, TA)>, } -impl Debug for Alice { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "Alice") - } -} - -impl Clone for Alice { - fn clone(&self) -> Alice { - unreachable!("Rust is requiring me to be clone erroneously") - } -} - impl Role for Alice { type SourceLedger = SL; type TargetLedger = TL; @@ -129,11 +79,14 @@ impl Role for Bob #[cfg(test)] pub mod test { use super::*; - use bitcoin_support::{self, BitcoinQuantity}; - use ethereum_support::{self, EtherQuantity}; + use bitcoin_support::BitcoinQuantity; + use ethereum_support::EtherQuantity; use swap_protocols::{ ledger::{Bitcoin, Ethereum}, - rfc003::ethereum::Seconds, + rfc003::{ + events::{ResponseEvent, ResponseFuture}, + messages::Request, + }, }; pub type Alisha = Alice; @@ -156,11 +109,11 @@ pub mod test { } #[allow(missing_debug_implementations)] - pub struct FakeResponseSource { + pub struct FakeResponseEvent { pub response: Option>>, } - impl ResponseSource for FakeResponseSource { + impl ResponseEvent for FakeResponseEvent { fn request_responded( &mut self, _request: &Request, diff --git a/application/comit_node/src/swap_protocols/rfc003/secret.rs b/application/comit_node/src/swap_protocols/rfc003/secret.rs index 671584b899..b520ef3dc1 100644 --- a/application/comit_node/src/swap_protocols/rfc003/secret.rs +++ b/application/comit_node/src/swap_protocols/rfc003/secret.rs @@ -6,7 +6,6 @@ use std::{ fmt::{self, Debug}, str::FromStr, }; -use swap_protocols::Ledger; const SHA256_DIGEST_LENGTH: usize = 32; @@ -212,25 +211,6 @@ pub trait ExtractSecret { fn extract_secret(&self, secret_hash: &SecretHash) -> Option; } -pub trait LedgerExtractSecret: Ledger { - fn extract_secret_from_transaction( - txn: &Self::Transaction, - secret_hash: &SecretHash, - ) -> Option; -} - -impl LedgerExtractSecret for L -where - L::Transaction: ExtractSecret, -{ - fn extract_secret_from_transaction( - txn: &Self::Transaction, - secret_hash: &SecretHash, - ) -> Option { - txn.extract_secret(secret_hash) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/application/comit_node/src/swap_protocols/rfc003/state_machine.rs b/application/comit_node/src/swap_protocols/rfc003/state_machine.rs index ca1cbcc203..41ab42abc2 100755 --- a/application/comit_node/src/swap_protocols/rfc003/state_machine.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_machine.rs @@ -1,8 +1,7 @@ -use comit_client::SwapReject; -use futures::{future::Either, Async, Future}; +use futures::{future::Either, Async}; use state_machine_future::{RentToOwn, StateMachineFuture}; use std::sync::Arc; -use swap_protocols::rfc003::secret::LedgerExtractSecret; +use swap_protocols::rfc003::LedgerExtractSecret; use swap_protocols::{ self, @@ -32,18 +31,6 @@ impl From> } } -pub type StateMachineResponseFuture = Future< - Item = Result, SwapReject>, - Error = rfc003::Error, - > + Send; - -#[allow(type_alias_bounds)] -pub type ResponseFuture = StateMachineResponseFuture< - R::SourceSuccessHtlcIdentity, - R::TargetRefundHtlcIdentity, - ::LockDuration, ->; - #[derive(Clone, Debug)] pub struct HtlcParams { pub asset: A, @@ -116,19 +103,12 @@ impl OngoingSwap { } } -pub trait ResponseSource { - fn request_responded( - &mut self, - request: &Request, - ) -> &mut ResponseFuture; -} - #[allow(missing_debug_implementations)] pub struct Context { - pub events: - Box>, + pub ledger_events: + Box>, pub state_repo: Arc>, - pub response_source: Box + Send>, + pub response_event: Box + Send>, } #[derive(StateMachineFuture)] @@ -217,7 +197,7 @@ impl PollSwap for Swap { secret_hash: state.secret.clone().into(), }; - let response = try_ready!(context.response_source.request_responded(&request).poll()); + let response = try_ready!(context.response_event.request_responded(&request).poll()); let state = state.take(); @@ -237,7 +217,7 @@ impl PollSwap for Swap { context: &'c mut RentToOwn<'c, Context>, ) -> Result>, rfc003::Error> { let source_htlc_location = try_ready!(context - .events + .ledger_events .htlc_funded(state.swap.source_htlc_params()) .poll()); @@ -257,7 +237,7 @@ impl PollSwap for Swap { context: &'c mut RentToOwn<'c, Context>, ) -> Result>, rfc003::Error> { match try_ready!(context - .events + .ledger_events .source_htlc_refunded_target_htlc_funded( state.swap.source_htlc_params(), state.swap.target_htlc_params(), @@ -287,7 +267,7 @@ impl PollSwap for Swap { context: &'c mut RentToOwn<'c, Context>, ) -> Result>, rfc003::Error> { if let Async::Ready(redeemed_or_refunded) = context - .events + .ledger_events .target_htlc_redeemed_or_refunded( state.swap.target_htlc_params(), &state.target_htlc_location, @@ -327,7 +307,7 @@ impl PollSwap for Swap { } match try_ready!(context - .events + .ledger_events .source_htlc_redeemed_or_refunded( state.swap.source_htlc_params(), &state.source_htlc_location @@ -362,7 +342,7 @@ impl PollSwap for Swap { context: &'c mut RentToOwn<'c, Context>, ) -> Result, rfc003::Error> { match try_ready!(context - .events + .ledger_events .source_htlc_redeemed_or_refunded( state.swap.source_htlc_params(), &state.source_htlc_location @@ -384,7 +364,7 @@ impl PollSwap for Swap { context: &'c mut RentToOwn<'c, Context>, ) -> Result, rfc003::Error> { match try_ready!(context - .events + .ledger_events .target_htlc_redeemed_or_refunded( state.swap.target_htlc_params(), &state.target_htlc_location @@ -406,7 +386,7 @@ impl PollSwap for Swap { context: &'c mut RentToOwn<'c, Context>, ) -> Result, rfc003::Error> { match try_ready!(context - .events + .ledger_events .target_htlc_redeemed_or_refunded( state.swap.target_htlc_params(), &state.target_htlc_location @@ -428,7 +408,7 @@ impl PollSwap for Swap { context: &'c mut RentToOwn<'c, Context>, ) -> Result, rfc003::Error> { match try_ready!(context - .events + .ledger_events .source_htlc_redeemed_or_refunded( state.swap.source_htlc_params(), &state.source_htlc_location diff --git a/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs index 428ada9db9..7be38ed621 100644 --- a/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs @@ -6,15 +6,12 @@ use swap_protocols::{ rfc003::{ ethereum::Seconds, events::{ - self, Events, HtlcFunded, SourceHtlcRedeemedOrRefunded, + self, HtlcFunded, LedgerEvents, SourceHtlcRedeemedOrRefunded, SourceHtlcRefundedTargetHtlcFunded, TargetHtlcRedeemedOrRefunded, }, - roles::{ - test::{Alisha, Bobisha, FakeResponseSource}, - Bob, Role, - }, + roles::test::{Alisha, Bobisha, FakeResponseEvent}, state_machine::*, - Ledger, Secret, + Secret, }, }; @@ -28,13 +25,13 @@ use hex::FromHex; use std::{str::FromStr, sync::Arc}; #[derive(Default)] -struct FakeEvents { +struct FakeLedgerEvents { pub source_htlc_funded: Option>>, pub source_htlc_refunded_target_htlc_funded: Option>>, } -impl HtlcFunded for FakeEvents { +impl HtlcFunded for FakeLedgerEvents { fn htlc_funded( &mut self, _htlc_params: HtlcParams, @@ -44,7 +41,7 @@ impl HtlcFunded for FakeEvents { } impl SourceHtlcRefundedTargetHtlcFunded - for FakeEvents + for FakeLedgerEvents { fn source_htlc_refunded_target_htlc_funded( &mut self, @@ -58,7 +55,7 @@ impl SourceHtlcRefundedTargetHtlcFunded for FakeEvents { +impl TargetHtlcRedeemedOrRefunded for FakeLedgerEvents { fn target_htlc_redeemed_or_refunded( &mut self, _target_htlc_params: HtlcParams, @@ -68,7 +65,7 @@ impl TargetHtlcRedeemedOrRefunded for FakeEvents { } } -impl SourceHtlcRedeemedOrRefunded for FakeEvents { +impl SourceHtlcRedeemedOrRefunded for FakeLedgerEvents { fn source_htlc_redeemed_or_refunded( &mut self, _source_htlc_params: HtlcParams, @@ -78,7 +75,7 @@ impl SourceHtlcRedeemedOrRefunded for FakeEvents { } } -impl Events for FakeEvents {} +impl LedgerEvents for FakeLedgerEvents {} fn gen_start_state() -> Start { Start { @@ -101,12 +98,12 @@ fn gen_start_state() -> Start { } macro_rules! init { - ($role:ty, $response_source:expr, $state:expr, $events:expr) => {{ + ($role:ty, $response_event:expr, $state:expr, $events:expr) => {{ let (state_sender, state_receiver) = mpsc::unbounded(); let context = Context { - events: Box::new($events), + ledger_events: Box::new($events), state_repo: Arc::new(state_sender), - response_source: Box::new($response_source), + response_event: Box::new($response_event), }; let state: SwapStates<$role> = $state; let final_state_future = Swap::start_in(state, context); @@ -149,11 +146,11 @@ fn when_swap_is_rejected_go_to_final_reject() { let (state_machine, states) = init!( Alisha, - FakeResponseSource:: { + FakeResponseEvent:: { response: Some(Box::new(future::ok(Err(SwapReject::Rejected)))), }, start.clone().into(), - FakeEvents { + FakeLedgerEvents { ..Default::default() } ); @@ -179,11 +176,11 @@ fn source_refunded() { let (state_machine, states) = init!( Alisha, - FakeResponseSource:: { + FakeResponseEvent:: { response: Some(Box::new(future::ok(Ok(bob_response.clone())))), }, start.clone().into(), - FakeEvents { + FakeLedgerEvents { source_htlc_funded: Some(Box::new(future::ok(OutPoint { txid: Sha256dHash::from_data(b"funding"), vout: 0, @@ -260,11 +257,11 @@ fn bob_transition_source_refunded() { let (state_machine, states) = init!( Bobisha, - FakeResponseSource:: { + FakeResponseEvent:: { response: Some(Box::new(future::ok(Ok(response.clone())))) }, start.clone().into(), - FakeEvents { + FakeLedgerEvents { source_htlc_funded: Some(Box::new(future::ok(OutPoint { txid: Sha256dHash::from_data(b"funding"), vout: 0, From da6c204f1c9191931d71672ac5618b460e024805 Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Mon, 19 Nov 2018 14:30:57 +1100 Subject: [PATCH 06/10] Use bitcoin_support::Transaction instead of VerboseRawTransaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤷‍♂️ --- .../rfc003/bitcoin/validation.rs | 107 +++++------------- .../rfc003/state_machine_test.rs | 37 ++---- 2 files changed, 37 insertions(+), 107 deletions(-) diff --git a/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs b/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs index f3bdc1ec27..8439b1adcc 100644 --- a/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs +++ b/application/comit_node/src/swap_protocols/rfc003/bitcoin/validation.rs @@ -52,18 +52,14 @@ mod tests { extern crate bitcoin_support; use super::{Error as ValidationError, *}; - use bitcoin_rpc_client::rpc::{ - ScriptPubKey, ScriptType, SerializedRawTransaction, TransactionOutput, - VerboseRawTransaction, - }; - use bitcoin_support::{BitcoinQuantity, Blocks, Sha256dHash, Transaction}; + use bitcoin_support::{BitcoinQuantity, Blocks, Transaction, TxOut}; use hex::FromHex; use spectral::prelude::*; use swap_protocols::rfc003::{state_machine::*, Secret}; - fn gen_htlc_params(bitcoin_amount: f64) -> HtlcParams { + fn gen_htlc_params(bitcoin_amount: BitcoinQuantity) -> HtlcParams { HtlcParams { - asset: BitcoinQuantity::from_bitcoin(bitcoin_amount), + asset: bitcoin_amount, ledger: Bitcoin::regtest(), success_identity: bitcoin_support::PubkeyHash::from_hex( "d38e554430c4035f2877a579a07a99886153f071", @@ -80,38 +76,20 @@ mod tests { #[test] fn transaction_contains_output_with_sufficient_money() { - let bitcoin_amount = 1.0; + let bitcoin_amount = BitcoinQuantity::from_bitcoin(1.0); let htlc_params = gen_htlc_params(bitcoin_amount); - let script = htlc_params.compute_address().script_pubkey(); - - let script_pub_key = ScriptPubKey { - asm: String::from(""), - hex: script.clone(), - req_sigs: None, - script_type: ScriptType::NullData, - addresses: None, - }; + let script_pubkey = htlc_params.compute_address().script_pubkey(); - let transaction_output = TransactionOutput { - value: htlc_params.asset.bitcoin(), - n: 1, - script_pub_key, + let transaction_output = TxOut { + value: htlc_params.asset.satoshi(), + script_pubkey, }; - let transaction = VerboseRawTransaction { - txid: Sha256dHash::from_data(b"a"), - hash: String::from(""), - size: 0, - vsize: 0, + let transaction = Transaction { version: 1, - locktime: 42, - vin: Vec::new(), - vout: vec![transaction_output], - hex: SerializedRawTransaction(String::from("")), - blockhash: Sha256dHash::from_data(b"blockhash"), - confirmations: 0, - time: 0, - blocktime: 0, + lock_time: 42, + input: vec![], + output: vec![transaction_output], }; let bitcoin_transaction: Transaction = transaction.into(); @@ -127,22 +105,12 @@ mod tests { #[test] fn transaction_does_not_contain_output() { - let bitcoin_amount = 1.0; - - let transaction = VerboseRawTransaction { - txid: Sha256dHash::from_data(b"refunded"), - hash: String::from(""), - size: 0, - vsize: 0, + let bitcoin_amount = BitcoinQuantity::from_bitcoin(1.0); + let transaction = Transaction { version: 1, - locktime: 42, - vin: Vec::new(), - vout: Vec::new(), - hex: SerializedRawTransaction(String::from("")), - blockhash: Sha256dHash::from_data(b"blockhash"), - confirmations: 0, - time: 0, - blocktime: 0, + lock_time: 42, + input: vec![], + output: vec![], }; let result = BitcoinQuantity::is_contained_in_transaction( @@ -155,47 +123,30 @@ mod tests { #[test] fn transaction_does_not_contain_enough_money() { - let bitcoin_amount = 1.0; + let bitcoin_amount = BitcoinQuantity::from_bitcoin(1.0); let htlc_params = gen_htlc_params(bitcoin_amount); - let script = htlc_params.compute_address().script_pubkey(); - let script_pub_key = ScriptPubKey { - asm: String::from(""), - hex: script.clone(), - req_sigs: None, - script_type: ScriptType::NullData, - addresses: None, - }; + let script_pubkey = htlc_params.compute_address().script_pubkey(); - let provided_bitcoin_amount = 0.5; + let provided_bitcoin_amount = BitcoinQuantity::from_bitcoin(0.5); - let transaction_output = TransactionOutput { - value: provided_bitcoin_amount, - n: 1, - script_pub_key, + let transaction_output = TxOut { + value: provided_bitcoin_amount.satoshi(), + script_pubkey, }; - let transaction = VerboseRawTransaction { - txid: Sha256dHash::from_data(b"a"), - hash: String::from(""), - size: 0, - vsize: 0, + let transaction = Transaction { version: 1, - locktime: 42, - vin: Vec::new(), - vout: vec![transaction_output], - hex: SerializedRawTransaction(String::from("")), - blockhash: Sha256dHash::from_data(b"blockhash"), - confirmations: 0, - time: 0, - blocktime: 0, + lock_time: 42, + input: vec![], + output: vec![transaction_output], }; let result = BitcoinQuantity::is_contained_in_transaction(&htlc_params, transaction.into()); let expected_error = ValidationError::UnexpectedAsset { - found: BitcoinQuantity::from_bitcoin(provided_bitcoin_amount), - expected: BitcoinQuantity::from_bitcoin(bitcoin_amount), + found: provided_bitcoin_amount, + expected: bitcoin_amount, }; assert_that(&result).is_err_containing(expected_error) diff --git a/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs index 7be38ed621..fdd1dab297 100644 --- a/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs @@ -1,4 +1,3 @@ -use bitcoin_rpc_client::rpc::{SerializedRawTransaction, VerboseRawTransaction}; use bitcoin_support::{BitcoinQuantity, Blocks, OutPoint, Sha256dHash}; use comit_client::SwapReject; use swap_protocols::{ @@ -186,22 +185,12 @@ fn source_refunded() { vout: 0, }))), source_htlc_refunded_target_htlc_funded: Some(Box::new(future::ok(Either::A( - VerboseRawTransaction { - txid: Sha256dHash::from_data(b"refunded"), - hash: String::from(""), - size: 0, - vsize: 0, + bitcoin_support::Transaction { version: 1, - locktime: 42, - vin: Vec::new(), - vout: Vec::new(), - hex: SerializedRawTransaction(String::from("")), - blockhash: Sha256dHash::from_data(b"blockhash"), - confirmations: 0, - time: 0, - blocktime: 0, + lock_time: 42, + input: vec![], + output: vec![], } - .into(), )))), ..Default::default() } @@ -267,22 +256,12 @@ fn bob_transition_source_refunded() { vout: 0, }))), source_htlc_refunded_target_htlc_funded: Some(Box::new(future::ok(Either::A( - VerboseRawTransaction { - txid: Sha256dHash::from_data(b"refunded"), - hash: String::from(""), - size: 0, - vsize: 0, + bitcoin_support::Transaction { version: 1, - locktime: 42, - vin: Vec::new(), - vout: Vec::new(), - hex: SerializedRawTransaction(String::from("")), - blockhash: Sha256dHash::from_data(b"blockhash"), - confirmations: 0, - time: 0, - blocktime: 0, + lock_time: 42, + input: vec![], + output: vec![], } - .into(), )))), ..Default::default() } From cc1e5fd8e297c79b2e055f0985656e7eb84d8fd3 Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Mon, 19 Nov 2018 14:31:20 +1100 Subject: [PATCH 07/10] s/source_htlc_location/htlc_location/ --- .../comit_node/src/swap_protocols/rfc003/events/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/comit_node/src/swap_protocols/rfc003/events/mod.rs b/application/comit_node/src/swap_protocols/rfc003/events/mod.rs index bd8caa783a..cb0cf72f47 100644 --- a/application/comit_node/src/swap_protocols/rfc003/events/mod.rs +++ b/application/comit_node/src/swap_protocols/rfc003/events/mod.rs @@ -96,7 +96,7 @@ where { fn new_htlc_redeemed_query( htlc_params: &HtlcParams, - source_htlc_location: &L::HtlcLocation, + htlc_location: &L::HtlcLocation, ) -> Self; } @@ -106,6 +106,6 @@ where { fn new_htlc_refunded_query( htlc_params: &HtlcParams, - source_htlc_location: &L::HtlcLocation, + htlc_location: &L::HtlcLocation, ) -> Self; } From 33ef1c7062cfb9a976f91f1e40bf4e5fa2e39725 Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Mon, 19 Nov 2018 15:41:23 +1100 Subject: [PATCH 08/10] Move ExtractSecret trait to Ledger ..And delete LedgerExtractSecret --- .../swap_protocols/rfc003/alice/handler.rs | 6 ++--- .../rfc003/bitcoin/extract_secret.rs | 20 ++++++++++------ .../rfc003/ethereum/extract_secret.rs | 22 +++++++++++------- .../src/swap_protocols/rfc003/ledger.rs | 23 ++++--------------- .../src/swap_protocols/rfc003/mod.rs | 4 ++-- .../src/swap_protocols/rfc003/secret.rs | 4 ---- .../swap_protocols/rfc003/state_machine.rs | 7 ++---- 7 files changed, 37 insertions(+), 49 deletions(-) diff --git a/application/comit_node/src/swap_protocols/rfc003/alice/handler.rs b/application/comit_node/src/swap_protocols/rfc003/alice/handler.rs index 55d4c2c8fc..f7c84635d0 100644 --- a/application/comit_node/src/swap_protocols/rfc003/alice/handler.rs +++ b/application/comit_node/src/swap_protocols/rfc003/alice/handler.rs @@ -18,7 +18,7 @@ use swap_protocols::{ roles::Alice, state_machine::{Start, SwapStates}, state_store::StateStore, - ExtractSecret, Ledger, Secret, + Ledger, Secret, }, }; use swaps::{alice_events, common::SwapId}; @@ -126,9 +126,7 @@ fn spawn_state_machine>, state_store: &S, -) where - TL::Transaction: ExtractSecret, -{ +) { let state = SwapStates::Start(start_state); // TODO: spawn state machine from state here diff --git a/application/comit_node/src/swap_protocols/rfc003/bitcoin/extract_secret.rs b/application/comit_node/src/swap_protocols/rfc003/bitcoin/extract_secret.rs index 51f463e327..61e2c50d00 100644 --- a/application/comit_node/src/swap_protocols/rfc003/bitcoin/extract_secret.rs +++ b/application/comit_node/src/swap_protocols/rfc003/bitcoin/extract_secret.rs @@ -1,9 +1,15 @@ use bitcoin_support::Transaction; -use swap_protocols::rfc003::secret::{ExtractSecret, Secret, SecretHash}; +use swap_protocols::{ + ledger::Bitcoin, + rfc003::{ + secret::{Secret, SecretHash}, + ExtractSecret, + }, +}; -impl ExtractSecret for Transaction { - fn extract_secret(&self, secret_hash: &SecretHash) -> Option { - self.input.iter().find_map(|txin| { +impl ExtractSecret for Bitcoin { + fn extract_secret(transaction: &Transaction, secret_hash: &SecretHash) -> Option { + transaction.input.iter().find_map(|txin| { txin.witness .iter() .find_map(|script_item| match Secret::from_vec(&script_item) { @@ -50,7 +56,7 @@ mod test { let secret = Secret::from(*b"This is our favourite passphrase"); let transaction = setup(&secret); - assert_that!(transaction.extract_secret(&secret.hash())) + assert_that!(Bitcoin::extract_secret(&transaction, &secret.hash())) .is_some() .is_equal_to(&secret); } @@ -65,7 +71,7 @@ mod test { bfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbf", ) .unwrap(); - assert_that!(transaction.extract_secret(&secret_hash)).is_none(); + assert_that!(Bitcoin::extract_secret(&transaction, &secret_hash)).is_none(); } #[test] @@ -77,7 +83,7 @@ mod test { .unwrap(); let secret = Secret::from_vec(&hex_secret).unwrap(); - assert_that!(transaction.extract_secret(&secret.hash())) + assert_that!(Bitcoin::extract_secret(&transaction, &secret.hash())) .is_some() .is_equal_to(&secret); } diff --git a/application/comit_node/src/swap_protocols/rfc003/ethereum/extract_secret.rs b/application/comit_node/src/swap_protocols/rfc003/ethereum/extract_secret.rs index 3c8d77d997..b340d4d77c 100644 --- a/application/comit_node/src/swap_protocols/rfc003/ethereum/extract_secret.rs +++ b/application/comit_node/src/swap_protocols/rfc003/ethereum/extract_secret.rs @@ -1,12 +1,18 @@ use ethereum_support::Transaction; -use swap_protocols::rfc003::secret::{ExtractSecret, Secret, SecretHash}; +use swap_protocols::{ + ledger::Ethereum, + rfc003::{ + secret::{Secret, SecretHash}, + ExtractSecret, + }, +}; -impl ExtractSecret for Transaction { - fn extract_secret(&self, secret_hash: &SecretHash) -> Option { - let data = &self.input.0; +impl ExtractSecret for Ethereum { + fn extract_secret(transaction: &Transaction, secret_hash: &SecretHash) -> Option { + let data = &transaction.input.0; info!( "Attempting to extract secret for {:?} from transaction {:?}", - secret_hash, self.hash + secret_hash, transaction.hash ); match Secret::from_vec(&data) { Ok(secret) => match secret.hash() == *secret_hash { @@ -14,7 +20,7 @@ impl ExtractSecret for Transaction { false => { error!( "Input ({:?}) in transaction {:?} is NOT the pre-image to {:?}", - data, self.hash, secret_hash + data, transaction.hash, secret_hash ); None } @@ -57,7 +63,7 @@ mod test { let secret = Secret::from(*b"This is our favourite passphrase"); let transaction = setup(&secret); - assert_that!(transaction.extract_secret(&secret.hash())) + assert_that!(Ethereum::extract_secret(&transaction, &secret.hash())) .is_some() .is_equal_to(&secret); } @@ -72,6 +78,6 @@ mod test { bfbfbfbfbfbfbfbfbfbfbfbfbfbfbfbf", ) .unwrap(); - assert_that!(transaction.extract_secret(&secret_hash)).is_none(); + assert_that!(Ethereum::extract_secret(&transaction, &secret_hash)).is_none(); } } diff --git a/application/comit_node/src/swap_protocols/rfc003/ledger.rs b/application/comit_node/src/swap_protocols/rfc003/ledger.rs index b2b0da7a42..dedec236a4 100644 --- a/application/comit_node/src/swap_protocols/rfc003/ledger.rs +++ b/application/comit_node/src/swap_protocols/rfc003/ledger.rs @@ -2,10 +2,10 @@ use serde::{de::DeserializeOwned, Serialize}; use std::fmt::Debug; use swap_protocols::{ self, - rfc003::secret::{ExtractSecret, Secret, SecretHash}, + rfc003::secret::{Secret, SecretHash}, }; -pub trait Ledger: LedgerExtractSecret { +pub trait Ledger: swap_protocols::Ledger + ExtractSecret { type LockDuration: PartialEq + Debug + Clone @@ -23,21 +23,6 @@ pub trait Ledger: LedgerExtractSecret { + Into<::Identity>; } -pub trait LedgerExtractSecret: swap_protocols::ledger::Ledger { - fn extract_secret_from_transaction( - txn: &Self::Transaction, - secret_hash: &SecretHash, - ) -> Option; -} - -impl LedgerExtractSecret for L -where - L::Transaction: ExtractSecret, -{ - fn extract_secret_from_transaction( - txn: &Self::Transaction, - secret_hash: &SecretHash, - ) -> Option { - txn.extract_secret(secret_hash) - } +pub trait ExtractSecret: swap_protocols::ledger::Ledger { + fn extract_secret(txn: &Self::Transaction, secret_hash: &SecretHash) -> Option; } diff --git a/application/comit_node/src/swap_protocols/rfc003/mod.rs b/application/comit_node/src/swap_protocols/rfc003/mod.rs index 4078377d35..c10f5520cb 100644 --- a/application/comit_node/src/swap_protocols/rfc003/mod.rs +++ b/application/comit_node/src/swap_protocols/rfc003/mod.rs @@ -26,9 +26,9 @@ mod state_machine_test; pub use self::{ error::Error, - ledger::{Ledger, LedgerExtractSecret}, + ledger::{ExtractSecret, Ledger}, messages::*, outcome::SwapOutcome, save_state::SaveState, - secret::{ExtractSecret, RandomnessSource, Secret, SecretFromErr, SecretHash}, + secret::{RandomnessSource, Secret, SecretFromErr, SecretHash}, }; diff --git a/application/comit_node/src/swap_protocols/rfc003/secret.rs b/application/comit_node/src/swap_protocols/rfc003/secret.rs index b520ef3dc1..8973d1e847 100644 --- a/application/comit_node/src/swap_protocols/rfc003/secret.rs +++ b/application/comit_node/src/swap_protocols/rfc003/secret.rs @@ -207,10 +207,6 @@ impl RandomnessSource for ThreadRng { } } -pub trait ExtractSecret { - fn extract_secret(&self, secret_hash: &SecretHash) -> Option; -} - #[cfg(test)] mod tests { use super::*; diff --git a/application/comit_node/src/swap_protocols/rfc003/state_machine.rs b/application/comit_node/src/swap_protocols/rfc003/state_machine.rs index 41ab42abc2..28f8b22def 100755 --- a/application/comit_node/src/swap_protocols/rfc003/state_machine.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_machine.rs @@ -1,7 +1,7 @@ use futures::{future::Either, Async}; use state_machine_future::{RentToOwn, StateMachineFuture}; use std::sync::Arc; -use swap_protocols::rfc003::LedgerExtractSecret; +use swap_protocols::rfc003::ExtractSecret; use swap_protocols::{ self, @@ -278,10 +278,7 @@ impl PollSwap for Swap { let secret_hash = state.swap.secret.clone().into(); match redeemed_or_refunded { Either::A(target_redeemed_tx) => { - match R::TargetLedger::extract_secret_from_transaction( - &target_redeemed_tx, - &secret_hash, - ) { + match R::TargetLedger::extract_secret(&target_redeemed_tx, &secret_hash) { Some(secret) => transition_save!( context.state_repo, SourceFundedTargetRedeemed { From f0f80cbd9da0b0425a55dc48d2ba941e5a0da434 Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Mon, 19 Nov 2018 16:17:09 +1100 Subject: [PATCH 09/10] Put LedgerEvents functions on the trait And rename ResponseEvent to CommunicationEvents --- .../src/swap_protocols/rfc003/events/lqs.rs | 71 ++++--------------- .../src/swap_protocols/rfc003/events/mod.rs | 46 ++++-------- .../swap_protocols/rfc003/events/response.rs | 4 +- .../src/swap_protocols/rfc003/roles.rs | 6 +- .../swap_protocols/rfc003/state_machine.rs | 4 +- .../rfc003/state_machine_test.rs | 27 ++----- 6 files changed, 43 insertions(+), 115 deletions(-) diff --git a/application/comit_node/src/swap_protocols/rfc003/events/lqs.rs b/application/comit_node/src/swap_protocols/rfc003/events/lqs.rs index 34eedb47ce..621214c936 100644 --- a/application/comit_node/src/swap_protocols/rfc003/events/lqs.rs +++ b/application/comit_node/src/swap_protocols/rfc003/events/lqs.rs @@ -5,10 +5,8 @@ use swap_protocols::{ rfc003::{ self, events::{ - Funded, HtlcFunded, LedgerEvents, NewHtlcFundedQuery, NewHtlcRedeemedQuery, - NewHtlcRefundedQuery, RedeemedOrRefunded, SourceHtlcRedeemedOrRefunded, - SourceHtlcRefundedTargetHtlcFunded, SourceRefundedOrTargetFunded, - TargetHtlcRedeemedOrRefunded, + Funded, LedgerEvents, NewHtlcFundedQuery, NewHtlcRedeemedQuery, NewHtlcRefundedQuery, + RedeemedOrRefunded, SourceRefundedOrTargetFunded, }, is_contained_in_transaction::IsContainedInTransaction, state_machine::HtlcParams, @@ -31,15 +29,23 @@ pub struct LqsEvents { target_htlc_redeemed_or_refunded: Option>>, } -impl HtlcFunded for LqsEvents +impl LedgerEvents + for LqsEvents where SL: Ledger, TL: Ledger, SA: Asset + IsContainedInTransaction, - SLQuery: Query + NewHtlcFundedQuery, - TLQuery: Query, + TA: Asset + IsContainedInTransaction, + SLQuery: Query + + NewHtlcRefundedQuery + + NewHtlcFundedQuery + + NewHtlcRedeemedQuery, + TLQuery: Query + + NewHtlcRefundedQuery + + NewHtlcFundedQuery + + NewHtlcRedeemedQuery, { - fn htlc_funded<'s>(&'s mut self, htlc_params: HtlcParams) -> &'s mut Funded { + fn source_htlc_funded(&mut self, htlc_params: HtlcParams) -> &mut Funded { let source_ledger_first_match = self.source_ledger_first_match.clone(); let query = SLQuery::new_htlc_funded_query(&htlc_params); @@ -60,18 +66,7 @@ where Box::new(funded_future) }) } -} -impl SourceHtlcRefundedTargetHtlcFunded - for LqsEvents -where - SL: Ledger, - TL: Ledger, - SA: Asset, - TA: Asset + IsContainedInTransaction, - SLQuery: Query + NewHtlcRefundedQuery, - TLQuery: Query + NewHtlcFundedQuery, -{ fn source_htlc_refunded_target_htlc_funded( &mut self, source_htlc_params: HtlcParams, @@ -122,17 +117,7 @@ where ) }) } -} -impl TargetHtlcRedeemedOrRefunded - for LqsEvents -where - SL: Ledger, - TL: Ledger, - TA: Asset, - TLQuery: Query + NewHtlcRefundedQuery + NewHtlcRedeemedQuery, - SLQuery: Query, -{ fn target_htlc_redeemed_or_refunded( &mut self, target_htlc_params: HtlcParams, @@ -176,17 +161,7 @@ where ) }) } -} -impl SourceHtlcRedeemedOrRefunded - for LqsEvents -where - SL: Ledger, - TL: Ledger, - SA: Asset, - SLQuery: Query + NewHtlcRefundedQuery + NewHtlcRedeemedQuery, - TLQuery: Query, -{ fn source_htlc_redeemed_or_refunded( &mut self, source_htlc_params: HtlcParams, @@ -231,21 +206,3 @@ where }) } } - -impl LedgerEvents - for LqsEvents -where - SL: Ledger, - TL: Ledger, - SA: Asset + IsContainedInTransaction, - TA: Asset + IsContainedInTransaction, - SLQuery: Query - + NewHtlcFundedQuery - + NewHtlcRefundedQuery - + NewHtlcRedeemedQuery, - TLQuery: Query - + NewHtlcFundedQuery - + NewHtlcRefundedQuery - + NewHtlcRedeemedQuery, -{ -} diff --git a/application/comit_node/src/swap_protocols/rfc003/events/mod.rs b/application/comit_node/src/swap_protocols/rfc003/events/mod.rs index cb0cf72f47..6fa6982c84 100644 --- a/application/comit_node/src/swap_protocols/rfc003/events/mod.rs +++ b/application/comit_node/src/swap_protocols/rfc003/events/mod.rs @@ -37,50 +37,34 @@ pub type SourceRefundedOrTargetFunded = Future>; pub type RedeemedOrRefunded = Future>; -pub trait LedgerEvents: - HtlcFunded - + SourceHtlcRefundedTargetHtlcFunded - + SourceHtlcRedeemedOrRefunded - + TargetHtlcRedeemedOrRefunded -{ -} - -pub trait ResponseEvent { - fn request_responded( - &mut self, - request: &Request, - ) -> &mut ResponseFuture; -} - -pub trait HtlcFunded: Send { - fn htlc_funded(&mut self, htlc_params: HtlcParams) -> &mut Funded; -} +pub trait LedgerEvents: Send { + fn source_htlc_funded(&mut self, htlc_params: HtlcParams) -> &mut Funded; -pub trait SourceHtlcRefundedTargetHtlcFunded: - Send -{ fn source_htlc_refunded_target_htlc_funded( &mut self, source_htlc_params: HtlcParams, target_htlc_params: HtlcParams, source_htlc_location: &SL::HtlcLocation, ) -> &mut SourceRefundedOrTargetFunded; -} -pub trait SourceHtlcRedeemedOrRefunded: Send { fn source_htlc_redeemed_or_refunded( &mut self, - source_htlc_params: HtlcParams, - htlc_location: &L::HtlcLocation, - ) -> &mut RedeemedOrRefunded; -} + source_htlc_params: HtlcParams, + htlc_location: &SL::HtlcLocation, + ) -> &mut RedeemedOrRefunded; -pub trait TargetHtlcRedeemedOrRefunded: Send { fn target_htlc_redeemed_or_refunded( &mut self, - target_htlc_params: HtlcParams, - htlc_location: &L::HtlcLocation, - ) -> &mut RedeemedOrRefunded; + target_htlc_params: HtlcParams, + htlc_location: &TL::HtlcLocation, + ) -> &mut RedeemedOrRefunded; +} + +pub trait CommunicationEvents { + fn request_responded( + &mut self, + request: &Request, + ) -> &mut ResponseFuture; } pub trait NewHtlcFundedQuery: Send + Sync diff --git a/application/comit_node/src/swap_protocols/rfc003/events/response.rs b/application/comit_node/src/swap_protocols/rfc003/events/response.rs index 86837bab72..a3f49465d3 100644 --- a/application/comit_node/src/swap_protocols/rfc003/events/response.rs +++ b/application/comit_node/src/swap_protocols/rfc003/events/response.rs @@ -7,7 +7,7 @@ use swap_protocols::{ asset::Asset, rfc003::{ self, - events::{ResponseEvent, ResponseFuture, StateMachineResponseFuture}, + events::{CommunicationEvents, ResponseFuture, StateMachineResponseFuture}, ledger::Ledger, messages::Request, }, @@ -21,7 +21,7 @@ struct AliceComitClient { } impl - ResponseEvent> for AliceComitClient + CommunicationEvents> for AliceComitClient { fn request_responded( &mut self, diff --git a/application/comit_node/src/swap_protocols/rfc003/roles.rs b/application/comit_node/src/swap_protocols/rfc003/roles.rs index 8973b0a483..d20d234606 100644 --- a/application/comit_node/src/swap_protocols/rfc003/roles.rs +++ b/application/comit_node/src/swap_protocols/rfc003/roles.rs @@ -84,7 +84,7 @@ pub mod test { use swap_protocols::{ ledger::{Bitcoin, Ethereum}, rfc003::{ - events::{ResponseEvent, ResponseFuture}, + events::{CommunicationEvents, ResponseFuture}, messages::Request, }, }; @@ -109,11 +109,11 @@ pub mod test { } #[allow(missing_debug_implementations)] - pub struct FakeResponseEvent { + pub struct FakeCommunicationEvents { pub response: Option>>, } - impl ResponseEvent for FakeResponseEvent { + impl CommunicationEvents for FakeCommunicationEvents { fn request_responded( &mut self, _request: &Request, diff --git a/application/comit_node/src/swap_protocols/rfc003/state_machine.rs b/application/comit_node/src/swap_protocols/rfc003/state_machine.rs index 28f8b22def..ef5d6cc4b9 100755 --- a/application/comit_node/src/swap_protocols/rfc003/state_machine.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_machine.rs @@ -108,7 +108,7 @@ pub struct Context { pub ledger_events: Box>, pub state_repo: Arc>, - pub response_event: Box + Send>, + pub response_event: Box + Send>, } #[derive(StateMachineFuture)] @@ -218,7 +218,7 @@ impl PollSwap for Swap { ) -> Result>, rfc003::Error> { let source_htlc_location = try_ready!(context .ledger_events - .htlc_funded(state.swap.source_htlc_params()) + .source_htlc_funded(state.swap.source_htlc_params()) .poll()); let state = state.take(); diff --git a/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs index fdd1dab297..ae6487d19f 100644 --- a/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs +++ b/application/comit_node/src/swap_protocols/rfc003/state_machine_test.rs @@ -4,11 +4,8 @@ use swap_protocols::{ ledger::{Bitcoin, Ethereum}, rfc003::{ ethereum::Seconds, - events::{ - self, HtlcFunded, LedgerEvents, SourceHtlcRedeemedOrRefunded, - SourceHtlcRefundedTargetHtlcFunded, TargetHtlcRedeemedOrRefunded, - }, - roles::test::{Alisha, Bobisha, FakeResponseEvent}, + events::{self, LedgerEvents}, + roles::test::{Alisha, Bobisha, FakeCommunicationEvents}, state_machine::*, Secret, }, @@ -30,18 +27,14 @@ struct FakeLedgerEvents { Option>>, } -impl HtlcFunded for FakeLedgerEvents { - fn htlc_funded( +impl LedgerEvents for FakeLedgerEvents { + fn source_htlc_funded( &mut self, _htlc_params: HtlcParams, ) -> &mut events::Funded { self.source_htlc_funded.as_mut().unwrap() } -} -impl SourceHtlcRefundedTargetHtlcFunded - for FakeLedgerEvents -{ fn source_htlc_refunded_target_htlc_funded( &mut self, _source_htlc_params: HtlcParams, @@ -52,9 +45,7 @@ impl SourceHtlcRefundedTargetHtlcFunded for FakeLedgerEvents { fn target_htlc_redeemed_or_refunded( &mut self, _target_htlc_params: HtlcParams, @@ -62,9 +53,7 @@ impl TargetHtlcRedeemedOrRefunded for FakeLedgerEvents ) -> &mut events::RedeemedOrRefunded { unimplemented!() } -} -impl SourceHtlcRedeemedOrRefunded for FakeLedgerEvents { fn source_htlc_redeemed_or_refunded( &mut self, _source_htlc_params: HtlcParams, @@ -74,8 +63,6 @@ impl SourceHtlcRedeemedOrRefunded for FakeLedgerEvents } } -impl LedgerEvents for FakeLedgerEvents {} - fn gen_start_state() -> Start { Start { source_ledger_refund_identity: secp256k1_support::KeyPair::from_secret_key_slice( @@ -145,7 +132,7 @@ fn when_swap_is_rejected_go_to_final_reject() { let (state_machine, states) = init!( Alisha, - FakeResponseEvent:: { + FakeCommunicationEvents:: { response: Some(Box::new(future::ok(Err(SwapReject::Rejected)))), }, start.clone().into(), @@ -175,7 +162,7 @@ fn source_refunded() { let (state_machine, states) = init!( Alisha, - FakeResponseEvent:: { + FakeCommunicationEvents:: { response: Some(Box::new(future::ok(Ok(bob_response.clone())))), }, start.clone().into(), @@ -246,7 +233,7 @@ fn bob_transition_source_refunded() { let (state_machine, states) = init!( Bobisha, - FakeResponseEvent:: { + FakeCommunicationEvents:: { response: Some(Box::new(future::ok(Ok(response.clone())))) }, start.clone().into(), From bd22a72776150b18c1be4d0c0c32a94ea3056957 Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Mon, 19 Nov 2018 16:28:12 +1100 Subject: [PATCH 10/10] Restore erroneously deleted test --- .../rfc003/actions/bob/btc_eth.rs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/application/comit_node/src/swap_protocols/rfc003/actions/bob/btc_eth.rs b/application/comit_node/src/swap_protocols/rfc003/actions/bob/btc_eth.rs index dcb55a2e64..ea9b73dd3a 100644 --- a/application/comit_node/src/swap_protocols/rfc003/actions/bob/btc_eth.rs +++ b/application/comit_node/src/swap_protocols/rfc003/actions/bob/btc_eth.rs @@ -77,3 +77,39 @@ impl StateActions for SwapStates { + source_ledger_refund_identity: bitcoin_support::PubkeyHash::from_hex( + "875638cac0b0ae9f826575e190f2788918c354c2", + ) + .unwrap(), + target_ledger_success_identity: "8457037fcd80a8650c4692d7fcfc1d0a96b92867" + .parse() + .unwrap(), + source_ledger: Bitcoin::regtest(), + target_ledger: Ethereum::default(), + source_asset: BitcoinQuantity::from_bitcoin(1.0), + target_asset: EtherQuantity::from_eth(10.0), + source_ledger_lock_duration: bitcoin_support::Blocks::from(144), + secret: Secret::from(*b"hello world, you are beautiful!!").hash(), + }); + + let actions = swap_state.actions(); + + assert_eq!( + actions, + vec![Action::Accept(Accept), Action::Decline(Decline)] + ); + } + +}