diff --git a/src/crypto.rs b/src/crypto.rs index ce4b46b1..7c0c4466 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -11,6 +11,7 @@ use thiserror::Error; use tiny_keccak::{Hasher, Keccak}; use crate::consensus::{self, CanonicalBytes, Decodable, Encodable}; +use crate::role::Accordant; /// List of cryptographic errors that can be encountered in cryptographic operations such as /// signatures, proofs, key derivation, or commitments. @@ -19,6 +20,9 @@ pub enum Error { /// The key identifier is not supported and the key cannot be derived. #[error("The key identifier is not supported and the key cannot be derived")] UnsupportedKey, + /// The key or key identifier does not exists or is missing. + #[error("The key or key identifier does not exists or is missing")] + MissingKey, /// The signature does not pass the validation tests. #[error("The signature does not pass the validation")] InvalidSignature, @@ -239,6 +243,17 @@ impl Decodable for SharedKeyId { } } +pub struct AccordantKeys { + pub spend_key: A::PublicKey, + pub extra_accordant_keys: Vec>, + pub shared_keys: Vec>, +} + +pub struct SwapAccordantKeys { + pub alice: AccordantKeys, + pub bob: AccordantKeys, +} + fixed_hash::construct_fixed_hash!( /// Result of a keccak256 commitment. #[cfg_attr( diff --git a/src/monero.rs b/src/monero.rs index c5f190a5..4c11adad 100644 --- a/src/monero.rs +++ b/src/monero.rs @@ -1,9 +1,9 @@ //! Implementation of the Monero blockchain as an accordant blockchain in a swap. This //! implementation should work in pair with any other arbitrating implementation, like Bitcoin. -use crate::blockchain::{self, Asset}; +use crate::blockchain::{self, Asset, Network}; use crate::consensus::{self, CanonicalBytes}; -use crate::crypto::{Keys, SharedKeyId, SharedSecretKeys}; +use crate::crypto::{self, AccordantKeys, Keys, SharedKeyId, SharedSecretKeys, SwapAccordantKeys}; use crate::role::Accordant; use monero::util::key::{PrivateKey, PublicKey}; @@ -22,7 +22,52 @@ pub const SHARED_VIEW_KEY_ID: u16 = 0x01; #[derive(Clone, Debug, Copy, PartialEq, Eq)] pub struct Monero; -impl Accordant for Monero {} +impl Accordant for Monero { + fn derive_lock_address( + network: Network, + keys: SwapAccordantKeys, + ) -> Result { + let SwapAccordantKeys { + alice: + AccordantKeys { + spend_key: alice_spend_key, + shared_keys: alice_shared_keys, + .. + }, + bob: + AccordantKeys { + spend_key: bob_spend_key, + shared_keys: bob_shared_keys, + .. + }, + } = keys; + + let alice_tagged_view_secretkey = alice_shared_keys + .iter() + .find(|tagged_key| *tagged_key.tag() == SharedKeyId::new(SHARED_VIEW_KEY_ID)) + .ok_or(crypto::Error::MissingKey)?; + let bob_tagged_view_secretkey = bob_shared_keys + .iter() + .find(|tagged_key| *tagged_key.tag() == SharedKeyId::new(SHARED_VIEW_KEY_ID)) + .ok_or(crypto::Error::MissingKey)?; + + let public_spend = alice_spend_key + bob_spend_key; + let secret_view = alice_tagged_view_secretkey.elem() + bob_tagged_view_secretkey.elem(); + let public_view = PublicKey::from_private_key(&secret_view); + + Ok(Address::standard(network.into(), public_spend, public_view)) + } +} + +impl From for monero::Network { + fn from(network: Network) -> Self { + match network { + Network::Mainnet => monero::Network::Mainnet, + Network::Testnet => monero::Network::Stagenet, + Network::Local => monero::Network::Testnet, + } + } +} impl std::str::FromStr for Monero { type Err = crate::consensus::Error; diff --git a/src/protocol_message.rs b/src/protocol_message.rs index dba65937..48c95202 100644 --- a/src/protocol_message.rs +++ b/src/protocol_message.rs @@ -318,9 +318,6 @@ where type Strategy = AsStrict; } -// TODO: Add more common data to reveal, e.g. help to ensure that both node uses the same value for -// fee - /// Reveals the parameters commited by the [`CommitAliceParameters`] protocol message. #[derive(Clone, Debug, Display)] #[display(Debug)] diff --git a/src/role.rs b/src/role.rs index b1609390..7da91b3f 100644 --- a/src/role.rs +++ b/src/role.rs @@ -6,7 +6,9 @@ use std::fmt::Debug; use std::io; use std::str::FromStr; -use crate::blockchain::{Address, Asset, Fee, FeePriority, Onchain, Timelock, Transactions}; +use crate::blockchain::{ + Address, Asset, Fee, FeePriority, Network, Onchain, Timelock, Transactions, +}; use crate::bundle::{ AliceParameters, BobParameters, CoreArbitratingTransactions, CosignedArbitratingCancel, FullySignedBuy, FullySignedPunish, FullySignedRefund, SignedAdaptorBuy, SignedAdaptorRefund, @@ -14,8 +16,8 @@ use crate::bundle::{ }; use crate::consensus::{self, Decodable, Encodable}; use crate::crypto::{ - AccordantKeyId, ArbitratingKeyId, KeyGenerator, Keys, SharedSecretKeys, Sign, Signatures, - TaggedElement, TaggedExtraKeys, TaggedSharedKeys, + self, AccordantKeyId, ArbitratingKeyId, KeyGenerator, Keys, SharedSecretKeys, Sign, Signatures, + SwapAccordantKeys, TaggedElement, TaggedExtraKeys, TaggedSharedKeys, }; use crate::negotiation::PublicOffer; use crate::script::{DataLock, DataPunishableLock, DoubleKeys, ScriptPath}; @@ -1329,5 +1331,11 @@ pub trait Arbitrating: } /// An accordant is the blockchain which does not need transaction inside the protocol nor -/// timelocks, it is the blockchain with the less requirements for an atomic swap. -pub trait Accordant: Asset + Address + Keys + SharedSecretKeys + Clone + Eq {} +/// timelocks: it is the blockchain with fewer requirements for an atomic swap. +pub trait Accordant: Asset + Address + Keys + SharedSecretKeys + Clone + Eq { + /// Derive the lock address for the accordant blockchain. + fn derive_lock_address( + network: Network, + keys: SwapAccordantKeys, + ) -> Result; +} diff --git a/tests/wallet.rs b/tests/wallet.rs index f4abe5b2..bf18587c 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -1,14 +1,15 @@ use bitcoin::hashes::{sha256d, Hash}; use rand::prelude::*; use std::convert::TryInto; +use std::str::FromStr; -use farcaster_core::consensus::CanonicalBytes; use farcaster_core::crypto::{ AccordantKeyId, ArbitratingKeyId, GenerateKey, GenerateSharedKey, ProveCrossGroupDleq, SharedKeyId, Sign, }; use farcaster_core::monero::SHARED_VIEW_KEY_ID; use farcaster_core::swap::btcxmr::*; +use farcaster_core::{consensus::CanonicalBytes, crypto::SwapAccordantKeys}; #[test] fn create_key_manager_from_seed() { @@ -182,3 +183,60 @@ fn key_manager_can_recover_secret() { secret.reverse(); assert_eq!(secret, recovered_secret.as_canonical_bytes()); } + +#[test] +fn can_create_accordant_address() { + use farcaster_core::crypto::{AccordantKeys, TaggedElement}; + use farcaster_core::monero::Monero; + use farcaster_core::role::Accordant; + use monero::{Address, Network, PrivateKey, PublicKey}; + + let swap_index = 0; + let mut alice_key_manager = KeyManager::new([1u8; 32], swap_index).unwrap(); + let mut bob_key_manager = KeyManager::new([2u8; 32], swap_index).unwrap(); + + let alice_spend_pubkey = alice_key_manager.get_pubkey(AccordantKeyId::Spend).unwrap(); + let bob_spend_pubkey = bob_key_manager.get_pubkey(AccordantKeyId::Spend).unwrap(); + + let alice_view_secretkey: PrivateKey = alice_key_manager + .get_shared_key(SharedKeyId::new(SHARED_VIEW_KEY_ID)) + .unwrap(); + let bob_view_secretkey: PrivateKey = bob_key_manager + .get_shared_key(SharedKeyId::new(SHARED_VIEW_KEY_ID)) + .unwrap(); + + let public_spend = alice_spend_pubkey + bob_spend_pubkey; + let secret_view = alice_view_secretkey + bob_view_secretkey; + let public_view = PublicKey::from_private_key(&secret_view); + + let accordant_address = Address::standard(Network::Testnet, public_spend, public_view); + let addr = "9srAu5mbgRwjoUiobvJ6zXB2JL7MZsPRPTgzhVjFdZJb6afRPaeN1ND4e4MWz55Q2JM3bQLTWmMgyjPZZHLa4X587UgdkNy"; + assert_eq!(Address::from_str(addr), Ok(accordant_address)); + + // redo process like manual above, but test against result from Monero's derive_lock_address implementation + let lock_address = Monero::derive_lock_address( + farcaster_core::blockchain::Network::Local, + SwapAccordantKeys { + alice: AccordantKeys { + spend_key: alice_spend_pubkey, + shared_keys: vec![TaggedElement::new( + SharedKeyId::new(SHARED_VIEW_KEY_ID), + alice_view_secretkey, + )], + extra_accordant_keys: vec![], + }, + bob: AccordantKeys { + spend_key: bob_spend_pubkey, + shared_keys: vec![TaggedElement::new( + SharedKeyId::new(SHARED_VIEW_KEY_ID), + bob_view_secretkey, + )], + extra_accordant_keys: vec![], + }, + }, + ); + assert_eq!( + lock_address.expect("derivation of lock address should work since manual does"), + accordant_address + ); +}