Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage accordant address #142

Merged
merged 4 commits into from
Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand Down Expand Up @@ -239,6 +243,17 @@ impl Decodable for SharedKeyId {
}
}

pub struct AccordantKeys<A: Accordant> {
pub spend_key: A::PublicKey,
pub extra_accordant_keys: Vec<TaggedElement<u16, A::PublicKey>>,
pub shared_keys: Vec<TaggedElement<SharedKeyId, A::SharedSecretKey>>,
}

pub struct SwapAccordantKeys<A: Accordant> {
pub alice: AccordantKeys<A>,
pub bob: AccordantKeys<A>,
}

fixed_hash::construct_fixed_hash!(
/// Result of a keccak256 commitment.
#[cfg_attr(
Expand Down
51 changes: 48 additions & 3 deletions src/monero.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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<Self>,
) -> Result<Address, crypto::Error> {
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<Network> 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;
Expand Down
3 changes: 0 additions & 3 deletions src/protocol_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
18 changes: 13 additions & 5 deletions src/role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ 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,
SignedArbitratingLock,
};
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};
Expand Down Expand Up @@ -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<Self>,
) -> Result<Self::Address, crypto::Error>;
}
60 changes: 59 additions & 1 deletion tests/wallet.rs
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -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
);
}