farcaster_core 0.6.4

Farcaster project core library, blockchain atomic swaps.
Documentation
// Copyright 2021-2022 Farcaster Devs
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA

//! Protocol execution and messages exchanged between peers. Execution steps of a swap are carried
//! by [`Alice`] and [`Bob`] structures. Each contain the list of methods needed to proceed a swap.

// For this file we allow having complex types
#![allow(clippy::type_complexity)]

use std::io;

use crate::blockchain::{Fee, FeePriority, FeeStrategy, Transactions};
use crate::consensus::{self, CanonicalBytes, Decodable, Encodable};
use crate::crypto::{
    self, AccordantKeyId, ArbitratingKeyId, Commit, DeriveKeys, EncSign, KeyGenerator,
    RecoverSecret, SharedKeyId, Sign, TaggedElement, TaggedElements, TaggedExtraKeys,
    TaggedSharedKeys,
};
use crate::protocol::message::{
    BuyProcedureSignature, CommitAliceParameters, CommitBobParameters, CoreArbitratingSetup,
    RevealAliceParameters, RevealBobParameters,
};
use crate::script::{DataLock, DataPunishableLock, ScriptPath, SwapRoleKeys};
use crate::swap::SwapId;
use crate::trade::Deal;
use crate::transaction::{
    Buyable, Cancelable, Chainable, Fundable, Lockable, Punishable, Refundable, Transaction,
    Witnessable,
};
use crate::{Error, Res};

pub mod message;

struct ValidatedCoreTransactions<Px, Ti, Pk> {
    lock: Px,
    cancel: Px,
    refund: Px,
    punish_lock: DataPunishableLock<Ti, Pk>,
}

/// Container for the three main transactions used as the arbitrating engine on-chain. The `lock`,
/// the lock `cancel`, and the cancel `refund`.
#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
pub struct CoreArbitratingTransactions<Px> {
    /// Partial transaction raw type representing the lock.
    pub lock: Px,
    /// Partial transaction raw type representing the cancel.
    pub cancel: Px,
    /// Partial transaction raw type representing the refund.
    pub refund: Px,
}

impl<Px> CoreArbitratingTransactions<Px> {
    pub fn into_arbitrating_setup<Sig, U>(
        self,
        swap_id: U,
        cancel_sig: Sig,
    ) -> CoreArbitratingSetup<Px, Sig>
    where
        U: Into<SwapId>,
    {
        CoreArbitratingSetup {
            swap_id: swap_id.into(),
            lock: self.lock,
            cancel: self.cancel,
            refund: self.refund,
            cancel_sig,
        }
    }
}

/// Container for the set of parameters needed to build or verify some parameters on the
/// [`CoreArbitratingTransactions`].
#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
pub struct ArbitratingParameters<Amt, Ti, F> {
    pub arbitrating_amount: Amt,
    pub cancel_timelock: Ti,
    pub punish_timelock: Ti,
    pub fee_strategy: FeeStrategy<F>,
}

/// A pair of signatures, one regular and one encrypted.
#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
pub struct TxSignatures<Sig> {
    pub sig: Sig,
    pub adapted_sig: Sig,
}

/// The partial `punish` transaction with its signature.
#[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)]
pub struct FullySignedPunish<Px, Sig> {
    pub punish: Px,
    pub punish_sig: Sig,
}

/// One has to produce a set of parameters at the start of a swap and receive a mostly equivalent
/// set from counter-party during the reveal procedure. Container for all the parameters a
/// participant needs to execture a swap.
///
/// The proof is optional because one receives the proof after receiving the parameters in the
/// receal process, the option allows to create the struct at the first step and add the swap if
/// needed later.
///
/// Timelocks and fee strategy are only present in the `local` set of parameters and not part of
/// the reveal process, thus they are optional too.
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr> {
    pub buy: Pk,
    pub cancel: Pk,
    pub refund: Pk,
    pub punish: Option<Pk>,
    pub adaptor: Pk,
    pub extra_arbitrating_keys: Vec<TaggedElement<u16, Pk>>,
    pub arbitrating_shared_keys: Vec<TaggedElement<SharedKeyId, Rk>>,
    pub spend: Qk,
    pub extra_accordant_keys: Vec<TaggedElement<u16, Qk>>,
    pub accordant_shared_keys: Vec<TaggedElement<SharedKeyId, Sk>>,
    pub proof: Option<Pr>,
    pub destination_address: Addr,
    pub cancel_timelock: Option<Ti>,
    pub punish_timelock: Option<Ti>,
    pub fee_strategy: Option<FeeStrategy<F>>,
}

impl<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr> Encodable for Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>
where
    Pk: CanonicalBytes,
    Qk: CanonicalBytes,
    Rk: CanonicalBytes,
    Sk: CanonicalBytes,
    Addr: CanonicalBytes,
    Ti: CanonicalBytes,
    F: CanonicalBytes,
    Pr: CanonicalBytes,
{
    fn consensus_encode<W: io::Write>(&self, writer: &mut W) -> Result<usize, io::Error> {
        let mut len = self.buy.as_canonical_bytes().consensus_encode(writer)?;
        len += self.cancel.as_canonical_bytes().consensus_encode(writer)?;
        len += self.refund.as_canonical_bytes().consensus_encode(writer)?;
        len += self.punish.as_canonical_bytes().consensus_encode(writer)?;
        len += self.adaptor.as_canonical_bytes().consensus_encode(writer)?;
        len += self.extra_arbitrating_keys.consensus_encode(writer)?;
        len += self.arbitrating_shared_keys.consensus_encode(writer)?;
        len += self.spend.as_canonical_bytes().consensus_encode(writer)?;
        len += self.extra_accordant_keys.consensus_encode(writer)?;
        len += self.accordant_shared_keys.consensus_encode(writer)?;
        len += self.proof.as_canonical_bytes().consensus_encode(writer)?;
        len += self
            .destination_address
            .as_canonical_bytes()
            .consensus_encode(writer)?;
        len += self
            .cancel_timelock
            .as_canonical_bytes()
            .consensus_encode(writer)?;
        len += self
            .punish_timelock
            .as_canonical_bytes()
            .consensus_encode(writer)?;
        Ok(len
            + self
                .fee_strategy
                .as_canonical_bytes()
                .consensus_encode(writer)?)
    }
}

impl<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr> Decodable for Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>
where
    Pk: CanonicalBytes,
    Qk: CanonicalBytes,
    Rk: CanonicalBytes,
    Sk: CanonicalBytes,
    Addr: CanonicalBytes,
    Ti: CanonicalBytes,
    F: CanonicalBytes,
    Pr: CanonicalBytes,
{
    fn consensus_decode<D: io::Read>(d: &mut D) -> Result<Self, consensus::Error> {
        Ok(Parameters {
            buy: Pk::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            cancel: Pk::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            refund: Pk::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            punish: Option::<Pk>::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            adaptor: Pk::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            extra_arbitrating_keys: Decodable::consensus_decode(d)?,
            arbitrating_shared_keys: Decodable::consensus_decode(d)?,
            spend: Qk::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            extra_accordant_keys: Decodable::consensus_decode(d)?,
            accordant_shared_keys: Decodable::consensus_decode(d)?,
            proof: Option::<Pr>::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            destination_address: Addr::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            cancel_timelock: Option::<Ti>::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            punish_timelock: Option::<Ti>::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?,
            fee_strategy: Option::<FeeStrategy<F>>::from_canonical_bytes(
                unwrap_vec_ref!(d).as_ref(),
            )?,
        })
    }
}

impl_strict_encoding!(Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>, Pk: CanonicalBytes, Qk: CanonicalBytes, Rk: CanonicalBytes, Sk: CanonicalBytes, Addr: CanonicalBytes, Ti: CanonicalBytes, F: CanonicalBytes, Pr: CanonicalBytes);

/// Transform a vector of tagged elements `K` into a vector of tagged commitments.
/// [`CanonicalBytes`] are used for computing the commitment of each elements.
pub fn commit_to_vec<T: Clone + Eq, K: CanonicalBytes, C: Clone + Eq>(
    wallet: &impl Commit<C>,
    keys: &[TaggedElement<T, K>],
) -> TaggedElements<T, C> {
    keys.iter()
        .map(|tagged_key| {
            TaggedElement::new(
                tagged_key.tag().clone(),
                wallet.commit_to(tagged_key.elem().as_canonical_bytes()),
            )
        })
        .collect()
}

/// Verifies a vector of tagged commitments against a vector of revealed tagged elements. Fails if
/// a tag is not found in the commitments or doesn't match the commitment.  [`CanonicalBytes`] are
/// used for computing the commitment of each elements.
pub fn verify_vec_of_commitments<T: Eq, K: CanonicalBytes, C: Clone + Eq>(
    wallet: &impl Commit<C>,
    keys: Vec<TaggedElement<T, K>>,
    commitments: &[TaggedElement<T, C>],
) -> Result<(), Error> {
    keys.into_iter()
        .map(|tagged_key| {
            commitments
                .iter()
                .find(|tagged_commitment| tagged_commitment.tag() == tagged_key.tag())
                .map(|tagged_commitment| {
                    wallet
                        .validate(
                            tagged_key.elem().as_canonical_bytes(),
                            tagged_commitment.elem().clone(),
                        )
                        .map_err(Error::Crypto)
                })
                .ok_or(Error::Crypto(crypto::Error::InvalidCommitment))
        })
        .collect::<Result<Vec<_>, _>>()
        .map(|_| ())
}

impl<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr> Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>
where
    Pk: Clone + CanonicalBytes,
    Qk: CanonicalBytes,
    Rk: CanonicalBytes,
    Sk: CanonicalBytes,
{
    /// Generates protocol message that commits to Alice's parameters.
    pub fn commit_alice<C: Clone + Eq, U: Into<SwapId>>(
        &self,
        swap_id: U,
        wallet: &impl Commit<C>,
    ) -> CommitAliceParameters<C> {
        CommitAliceParameters {
            swap_id: swap_id.into(),
            buy: wallet.commit_to(self.buy.as_canonical_bytes()),
            cancel: wallet.commit_to(self.cancel.as_canonical_bytes()),
            refund: wallet.commit_to(self.refund.as_canonical_bytes()),
            punish: wallet.commit_to(
                self.punish
                    .clone()
                    .expect("Alice has punish")
                    .as_canonical_bytes(),
            ),
            adaptor: wallet.commit_to(self.adaptor.as_canonical_bytes()),
            extra_arbitrating_keys: commit_to_vec(wallet, &self.extra_arbitrating_keys),
            arbitrating_shared_keys: commit_to_vec(wallet, &self.arbitrating_shared_keys),
            spend: wallet.commit_to(self.spend.as_canonical_bytes()),
            extra_accordant_keys: commit_to_vec(wallet, &self.extra_accordant_keys),
            accordant_shared_keys: commit_to_vec(wallet, &self.accordant_shared_keys),
        }
    }

    /// Create the reveal protocol message based on the set of parameters.
    pub fn reveal_alice<U: Into<SwapId>>(
        self,
        swap_id: U,
    ) -> RevealAliceParameters<Pk, Qk, Rk, Sk, Addr, Pr> {
        RevealAliceParameters {
            swap_id: swap_id.into(),
            buy: self.buy,
            cancel: self.cancel,
            refund: self.refund,
            punish: self.punish.expect("Alice has punish"),
            adaptor: self.adaptor,
            extra_arbitrating_keys: self.extra_arbitrating_keys,
            arbitrating_shared_keys: self.arbitrating_shared_keys,
            spend: self.spend,
            extra_accordant_keys: self.extra_accordant_keys,
            accordant_shared_keys: self.accordant_shared_keys,
            address: self.destination_address,
            proof: self.proof.expect("Generated parameters contains proof"),
        }
    }

    /// Generates protocol message that commits to Bob's parameters.
    pub fn commit_bob<C: Clone + Eq, U: Into<SwapId>>(
        &self,
        swap_id: U,
        wallet: &impl Commit<C>,
    ) -> CommitBobParameters<C> {
        CommitBobParameters {
            swap_id: swap_id.into(),
            buy: wallet.commit_to(self.buy.as_canonical_bytes()),
            cancel: wallet.commit_to(self.cancel.as_canonical_bytes()),
            refund: wallet.commit_to(self.refund.as_canonical_bytes()),
            adaptor: wallet.commit_to(self.adaptor.as_canonical_bytes()),
            extra_arbitrating_keys: commit_to_vec(wallet, &self.extra_arbitrating_keys),
            arbitrating_shared_keys: commit_to_vec(wallet, &self.arbitrating_shared_keys),
            spend: wallet.commit_to(self.spend.as_canonical_bytes()),
            extra_accordant_keys: commit_to_vec(wallet, &self.extra_accordant_keys),
            accordant_shared_keys: commit_to_vec(wallet, &self.accordant_shared_keys),
        }
    }

    /// Create the reveal protocol message based on the set of parameters.
    pub fn reveal_bob<U: Into<SwapId>>(
        self,
        swap_id: U,
    ) -> RevealBobParameters<Pk, Qk, Rk, Sk, Addr, Pr> {
        RevealBobParameters {
            swap_id: swap_id.into(),
            buy: self.buy,
            cancel: self.cancel,
            refund: self.refund,
            adaptor: self.adaptor,
            extra_arbitrating_keys: self.extra_arbitrating_keys,
            arbitrating_shared_keys: self.arbitrating_shared_keys,
            spend: self.spend,
            extra_accordant_keys: self.extra_accordant_keys,
            accordant_shared_keys: self.accordant_shared_keys,
            address: self.destination_address,
            proof: self.proof.expect("Generated parameters contains proof"),
        }
    }
}

/// Alice, a [`SwapRole`], starts with accordant blockchain assets and exchange them for
/// arbitrating blockchain assets.
///
/// [`SwapRole`]: crate::role::SwapRole
#[derive(Debug, Clone)]
pub struct Alice<Addr, Ar, Ac> {
    /// The **arbitrating** blockchain to use during the swap
    pub arbitrating: Ar,
    /// The **accordant** blockchain to use during the swap
    pub accordant: Ac,
    /// An arbitrating address where, if successfully executed, the funds exchanged will be sent to
    pub destination_address: Addr,
    /// The fee politic to apply during the swap fee calculation
    pub fee_politic: FeePriority,
}

impl<Addr, Ar, Ac> Encodable for Alice<Addr, Ar, Ac>
where
    Ar: Encodable,
    Ac: Encodable,
    Addr: CanonicalBytes,
{
    fn consensus_encode<W: io::Write>(&self, writer: &mut W) -> Result<usize, io::Error> {
        let mut len = self.fee_politic.consensus_encode(writer)?;
        len += self.arbitrating.consensus_encode(writer)?;
        len += self.accordant.consensus_encode(writer)?;
        Ok(len
            + self
                .destination_address
                .as_canonical_bytes()
                .consensus_encode(writer)?)
    }
}

impl<Addr, Ar, Ac> Decodable for Alice<Addr, Ar, Ac>
where
    Ar: Decodable,
    Ac: Decodable,
    Addr: CanonicalBytes,
{
    fn consensus_decode<D: io::Read>(d: &mut D) -> Result<Self, consensus::Error> {
        let fee_politic = FeePriority::consensus_decode(d)?;
        let arbitrating = Decodable::consensus_decode(d)?;
        let accordant = Decodable::consensus_decode(d)?;
        let destination_address = Addr::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?;
        Ok(Alice {
            arbitrating,
            accordant,
            destination_address,
            fee_politic,
        })
    }
}

impl<Addr, Ar, Ac> Alice<Addr, Ar, Ac> {
    /// Create a new role for Alice with her local parameters.
    pub fn new(
        arbitrating: Ar,
        accordant: Ac,
        destination_address: Addr,
        fee_politic: FeePriority,
    ) -> Self {
        Self {
            arbitrating,
            accordant,
            destination_address,
            fee_politic,
        }
    }
}

impl<Addr, Ar, Ac> Alice<Addr, Ar, Ac>
where
    Addr: Clone,
{
    /// Generate Alice's parameters for the protocol execution based on a key generator.
    ///
    /// # Safety
    ///
    /// All data passed to the function are considered trusted and does not require extra
    /// validation. Thus we assume the deal has been validated upfront.
    ///
    pub fn generate_parameters<Kg, Amt, Bmt, Ti, F, Pk, Qk, Rk, Sk, Pr>(
        &self,
        key_gen: &mut Kg,
        deal: &Deal<Amt, Bmt, Ti, F>,
    ) -> Res<Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>>
    where
        Ar: DeriveKeys<PublicKey = Pk, PrivateKey = Rk>,
        Ac: DeriveKeys<PublicKey = Qk, PrivateKey = Sk>,
        Ti: Copy,
        F: Copy,
        Kg: KeyGenerator<Pk, Qk, Rk, Sk, Pr>,
    {
        let extra_arbitrating_keys: Res<TaggedExtraKeys<Pk>> = Ar::extra_public_keys()
            .into_iter()
            .map(|tag| {
                let key = key_gen.get_pubkey(ArbitratingKeyId::Extra(tag))?;
                Ok(TaggedElement::new(tag, key))
            })
            .collect();

        let arbitrating_shared_keys: Res<TaggedSharedKeys<Rk>> = Ar::extra_shared_private_keys()
            .into_iter()
            .map(|tag| {
                let key = key_gen.get_shared_key(tag)?;
                Ok(TaggedElement::new(tag, key))
            })
            .collect();

        let extra_accordant_keys: Res<TaggedExtraKeys<Qk>> = Ac::extra_public_keys()
            .into_iter()
            .map(|tag| {
                let key = key_gen.get_pubkey(AccordantKeyId::Extra(tag))?;
                Ok(TaggedElement::new(tag, key))
            })
            .collect();

        let accordant_shared_keys: Res<TaggedSharedKeys<Sk>> = Ac::extra_shared_private_keys()
            .into_iter()
            .map(|tag| {
                let key = key_gen.get_shared_key(tag)?;
                Ok(TaggedElement::new(tag, key))
            })
            .collect();

        let (spend, adaptor, proof) = key_gen.generate_proof()?;

        Ok(Parameters {
            buy: key_gen.get_pubkey(ArbitratingKeyId::Buy)?,
            cancel: key_gen.get_pubkey(ArbitratingKeyId::Cancel)?,
            refund: key_gen.get_pubkey(ArbitratingKeyId::Refund)?,
            punish: Some(key_gen.get_pubkey(ArbitratingKeyId::Punish)?),
            adaptor,
            extra_arbitrating_keys: extra_arbitrating_keys?,
            arbitrating_shared_keys: arbitrating_shared_keys?,
            spend,
            extra_accordant_keys: extra_accordant_keys?,
            accordant_shared_keys: accordant_shared_keys?,
            proof: Some(proof),
            destination_address: self.destination_address.clone(),
            cancel_timelock: Some(deal.parameters.cancel_timelock),
            punish_timelock: Some(deal.parameters.punish_timelock),
            fee_strategy: Some(deal.parameters.fee_strategy),
        })
    }

    /// Generates the witness on the refund transaction and encrypts it.
    ///
    /// # Safety
    ///
    /// Bob's parameters (i.e. counter-party parameters) are created and validated with the
    /// protocol messages that commit and reveal the values.
    ///
    /// **This function assumes that the commit/reveal scheme has been validated and assumes that
    /// all cryptographic proof needed for securing the system have passed the validation.**
    ///
    /// [`CoreArbitratingTransactions`] protocol message is created by Bob and requries extra
    /// validation. **This validation is performed in this function!**
    ///
    /// _Previously verified data_:
    ///  * `bob_parameters`: Bob's parameters (i.e. counter-party parameters)
    ///  * `arb_params`: The parameters used to verify core
    ///
    /// _Trusted data_:
    ///  * `wallet`: Alice's own wallet, used to perform cryptographic operations
    ///  * `alice_parameters`: Alice's own parameters
    ///
    /// _Verified data_:
    ///  * `core`: Core arbitrating transactions
    ///
    /// # Execution
    ///
    ///  * Parse and validate the [`Lockable`], [`Cancelable`], [`Refundable`] partial transactions
    ///  in [`CoreArbitratingTransactions`]
    ///  * Retrieve Bob's(counter-party) adaptor public key from Bob's [`Parameters`]
    ///  * Retrieve Alice's own refund public key from Alice's [`Parameters`]
    ///  * Generate the witness data and adaptor-sign(encrypt) it
    ///
    /// Returns the adaptor(encrypted) signature.
    ///
    pub fn sign_adaptor_refund<Amt, Px, Pk, Qk, Rk, Sk, Ti, F, Pr, S, Ms, Si, EncSig>(
        &self,
        wallet: &mut S,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        core: &CoreArbitratingTransactions<Px>,
        arb_params: ArbitratingParameters<Amt, Ti, F>,
    ) -> Res<EncSig>
    where
        S: EncSign<Pk, Ms, Si, EncSig>,
        Ar: Transactions<Addr = Addr, Amt = Amt, Ti = Ti, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone + Fee<FeeUnit = F>,
        Pk: Copy,
        Ti: Copy,
        Amt: Copy + PartialEq,
    {
        // Verifies the core arbitrating transactions.
        let ValidatedCoreTransactions { refund, .. } =
            self.validate_core(alice_parameters, bob_parameters, core, arb_params)?;

        // Generate the witness message to sign and adaptor sign with the refund key and the
        // counter-party adaptor.
        let adaptor = &bob_parameters.adaptor;
        let refund = <Ar::Refund>::from_partial(refund);
        let msg = refund.generate_witness_message(ScriptPath::Success)?;
        wallet
            .encrypt_sign(ArbitratingKeyId::Refund, adaptor, msg)
            .map_err(Into::into)
    }

    /// Generates the witness on the cancel transaction and signs it.
    ///
    /// # Safety
    ///
    /// [`CoreArbitratingTransactions`] protocol message is created by Bob and requries extra
    /// validation. **This validation is performed in this function!**
    ///
    /// _Previously verified data_:
    ///  * `bob_parameters`: Bob's parameters (i.e. counter-party parameters)
    ///  * `arb_params`: The parameters used to verify core
    ///
    /// _Trusted data_:
    ///  * `wallet`: Alice's own wallet, used to perform cryptographic operations
    ///  * `alice_parameters`: Alice's own parameters
    ///
    /// _Verified data_:
    ///  * `core`: Core arbitrating transactions
    ///
    /// # Execution
    ///
    ///  * Parse and validate the [`Lockable`], [`Cancelable`], [`Refundable`] partial transactions
    ///  in [`CoreArbitratingTransactions`]
    ///  * Retreive Alice's cancel public key from her own parameters
    ///  * Generate the witness data and sign it
    ///
    /// Returns the witness signature.
    ///
    pub fn cosign_arbitrating_cancel<Amt, Px, Pk, Qk, Rk, Sk, Ti, F, Pr, S, Ms, Si>(
        &self,
        wallet: &mut S,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        core: &CoreArbitratingTransactions<Px>,
        arb_params: ArbitratingParameters<Amt, Ti, F>,
    ) -> Res<Si>
    where
        S: Sign<Pk, Ms, Si>,
        Ar: Transactions<Addr = Addr, Amt = Amt, Ti = Ti, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone + Fee<FeeUnit = F>,
        Pk: Copy,
        Ti: Copy,
        Amt: Copy + PartialEq,
    {
        // Verifies the core arbitrating transactions.
        let ValidatedCoreTransactions { cancel, .. } =
            self.validate_core(alice_parameters, bob_parameters, core, arb_params)?;

        // Generate the witness message to sign and sign with the cancel key.
        let cancel = <Ar::Cancel>::from_partial(cancel);
        let msg = cancel.generate_witness_message(ScriptPath::Failure)?;
        wallet
            .sign(ArbitratingKeyId::Cancel, msg)
            .map_err(Into::into)
    }

    /// Validates the adaptor buy witness based on Alice & Bob parameters and the buy arbitrating
    /// transactions.
    ///
    /// # Safety
    ///
    /// Bob's parameters (i.e. counter-party parameters) are created and validated with the
    /// protocol messages that commit and reveal the values.
    ///
    /// **This function assumes that the commit/reveal scheme has been validated and assumes that
    /// all cryptographic proof needed for securing the system have passed the validation.**
    ///
    /// [`CoreArbitratingTransactions`] protocol message is created by Bob and requries extra
    /// validation. **This validation is performed in this function!**
    ///
    /// _Previously verified data_:
    ///  * `bob_parameters`: Bob's parameters (i.e. counter-party parameters)
    ///  * `arb_params`: The parameters used to verify core
    ///
    /// _Trusted data_:
    ///  * `wallet`: Alice's own wallet, used to perform cryptographic operations
    ///  * `alice_parameters`: Alice's own parameters
    ///
    /// _Verified data_:
    ///  * `core`: Core arbitrating transactions
    ///  * `adaptor_buy`: The adaptor witness this function verifies
    ///
    /// # Execution
    ///
    ///  * Parse and verify the [`Buyable`] partial transaction and the adaptor witness in
    ///  [`BuyProcedureSignature`] with the public keys from the parameters
    ///
    /// Return `Ok(())` if all tests succeed.
    ///
    pub fn validate_adaptor_buy<Amt, Px, Pk, Qk, Rk, Sk, Ti, F, Pr, S, Ms, Si, EncSig>(
        &self,
        wallet: &mut S,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        core: &CoreArbitratingTransactions<Px>,
        arb_params: ArbitratingParameters<Amt, Ti, F>,
        adaptor_buy: &BuyProcedureSignature<Px, EncSig>,
    ) -> Res<()>
    where
        S: EncSign<Pk, Ms, Si, EncSig>,
        Ar: Transactions<Addr = Addr, Amt = Amt, Ti = Ti, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone + Fee<FeeUnit = F>,
        Pk: Copy,
        Ti: Copy,
        F: Copy,
        Amt: Copy + PartialEq,
    {
        // Verifies the core arbitrating transactions.
        let ValidatedCoreTransactions { lock, .. } =
            self.validate_core(alice_parameters, bob_parameters, core, arb_params)?;
        let lock = <Ar::Lock>::from_partial(lock);

        let fee_strategy = &arb_params.fee_strategy;

        // Extract the partial transaction from the adaptor buy, this operation should not error if
        // well formed.
        let partial_buy = adaptor_buy.buy.clone();

        // Initialize the buy transaction based on the extracted partial transaction format.
        let buy = <Ar::Buy>::from_partial(partial_buy);

        buy.is_build_on_top_of(&lock)?;
        buy.verify_template(self.destination_address.clone())?;
        buy.as_partial().validate_fee(fee_strategy)?;

        // Verify the adaptor buy witness
        let msg = buy.generate_witness_message(ScriptPath::Success)?;
        wallet.verify_encrypted_signature(
            &bob_parameters.buy,
            &alice_parameters.adaptor,
            msg,
            &adaptor_buy.buy_adaptor_sig,
        )?;

        Ok(())
    }

    /// Sign the arbitrating buy transaction and adapt(encrypt) the counter-party adaptor witness
    /// with the private adaptor key.
    ///
    /// # Safety
    ///
    /// This function **MUST NOT** be run if [`validate_adaptor_buy`] is not successful.
    ///
    /// [`BuyProcedureSignature`] protocol message is created by Bob and must be verified to be a
    /// valid encrypted signature and a valid transaction.
    ///
    /// **This function assumes that the adaptor signature has been validated and assumes that all
    /// cryptographic proof needed for securing the system have passed the validation.**
    ///
    /// _Previously verified data_:
    ///  * `adaptor_buy`: Verified by [`validate_adaptor_buy`]
    ///  * `arb_params`: The parameters used to verify core
    ///
    /// _Trusted data_:
    ///  * `wallet`: Alice's own wallet, used to perform cryptographic operations
    ///  * `alice_parameters`: Alice's own parameters
    ///  * `bob_parameters`: Bob's parameters
    ///
    /// _Verified data_:
    ///  * `core`: Core arbitrating transactions
    ///
    /// # Execution
    ///
    ///  * Parse the [`Buyable`] partial transaction in [`BuyProcedureSignature`]
    ///  * Retreive the buy public key from the paramters
    ///  * Generate the buy witness data and sign it
    ///  * Retreive the adaptor public key from the parameters
    ///  * Adapt(encrypt) the signature
    ///
    /// Returns the signatures inside a [`TxSignatures`].
    ///
    /// [`validate_adaptor_buy`]: Alice::validate_adaptor_buy
    ///
    pub fn fully_sign_buy<Amt, Px, Pk, Qk, Rk, Sk, Ti, F, Pr, S, Ms, Si, EncSig>(
        &self,
        wallet: &mut S,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        core: &CoreArbitratingTransactions<Px>,
        arb_params: ArbitratingParameters<Amt, Ti, F>,
        adaptor_buy: &BuyProcedureSignature<Px, EncSig>,
    ) -> Res<TxSignatures<Si>>
    where
        S: Sign<Pk, Ms, Si> + EncSign<Pk, Ms, Si, EncSig>,
        Ar: Transactions<Addr = Addr, Amt = Amt, Ti = Ti, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone + Fee<FeeUnit = F>,
        Pk: Copy,
        Ti: Copy,
        F: Copy,
        Amt: Copy + PartialEq,
        EncSig: Clone,
    {
        // Verifies the core arbitrating transactions.
        let ValidatedCoreTransactions { lock, .. } =
            self.validate_core(alice_parameters, bob_parameters, core, arb_params)?;
        let lock = <Ar::Lock>::from_partial(lock);

        let fee_strategy = &arb_params.fee_strategy;

        // Extract the partial transaction from the adaptor buy protocol message, this operation
        // should not error if the message is well formed.
        let partial_buy = adaptor_buy.buy.clone();

        // Initialize the buy transaction based on the extracted partial transaction format.
        let buy = <Ar::Buy>::from_partial(partial_buy);

        buy.is_build_on_top_of(&lock)?;
        buy.verify_template(self.destination_address.clone())?;
        buy.as_partial().validate_fee(fee_strategy)?;

        // Generate the witness message to sign and sign with the buy key.
        let msg = buy.generate_witness_message(ScriptPath::Success)?;
        let sig = wallet.sign(ArbitratingKeyId::Buy, msg)?;

        // Retreive the adaptor public key and the counter-party adaptor witness.
        let adapted_sig =
            wallet.decrypt_signature(AccordantKeyId::Spend, adaptor_buy.buy_adaptor_sig.clone())?;

        Ok(TxSignatures { sig, adapted_sig })
    }

    /// Create and sign the arbitrating punish transaction.
    ///
    /// # Safety
    ///
    /// [`CoreArbitratingTransactions`] protocol message is created by Bob and requries extra
    /// validation.
    ///
    /// This transaction does not require the same validation of Bob's parameters because the
    /// adaptor is not used and no private key is revealed during the process. Alice's should
    /// always be able to produce the punish transaction if the contract on cancel has been
    /// correctly validated.
    ///
    /// _Previously verified data_:
    ///  * `bob_parameters`: Bob's parameters
    ///  * `core`: The core arbitrating transactions
    ///  * `arb_params`: The core arbitrating parameter used in verification
    ///
    /// _Trusted data_:
    ///  * `wallet`: Alice's own wallet, used to perform cryptographic operations
    ///  * `alice_parameters`: Alice's own parameters
    ///
    /// Returns the signatures inside [`FullySignedPunish`].
    ///
    /// [`validate_adaptor_buy`]: Alice::validate_adaptor_buy
    ///
    pub fn fully_sign_punish<Amt, Px, Pk, Qk, Rk, Sk, Ti, F, Pr, S, Ms, Si>(
        &self,
        wallet: &mut S,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        core: &CoreArbitratingTransactions<Px>,
        arb_params: ArbitratingParameters<Amt, Ti, F>,
    ) -> Res<FullySignedPunish<Px, Si>>
    where
        S: Sign<Pk, Ms, Si>,
        Ar: Transactions<Addr = Addr, Amt = Amt, Ti = Ti, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone + Fee<FeeUnit = F>,
        Pk: Copy,
        Ti: Copy,
        F: Copy,
        Amt: Copy + PartialEq,
    {
        // Verifies the core arbitrating transactions.
        let ValidatedCoreTransactions {
            cancel,
            punish_lock,
            ..
        } = self.validate_core(alice_parameters, bob_parameters, core, arb_params)?;
        let cancel = <Ar::Cancel>::from_partial(cancel);

        let fee_strategy = &arb_params.fee_strategy;

        // Initialize the punish transaction based on the cancel transaction.
        let mut punish =
            <Ar::Punish>::initialize(&cancel, punish_lock, self.destination_address.clone())?;

        // Set the fees according to the strategy in the deal and the local politic.
        punish
            .as_partial_mut()
            .set_fee(fee_strategy, self.fee_politic)?;

        // Generate the witness message to sign and sign with the punish key.
        let msg = punish.generate_witness_message(ScriptPath::Failure)?;
        let punish_sig = wallet.sign(ArbitratingKeyId::Punish, msg)?;

        Ok(FullySignedPunish {
            punish: punish.to_partial(),
            punish_sig,
        })
    }

    /// Given the Bob's parameters, the refund transaction and the encrypted signature for the
    /// refund transaction, return the secret key used to encrypt the signature.
    ///
    /// This method is used if the refund occurs to allow Alice to unlock her funds.
    pub fn recover_accordant_key<Amt, Tx, Px, Pk, Qk, Rk, Sk, Ti, F, Pr, S, Si, EncSig>(
        &self,
        wallet: &mut S,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        refund_adaptor_sig: EncSig,
        refund_tx: Tx,
    ) -> Rk
    where
        S: RecoverSecret<Pk, Rk, Si, EncSig>,
        Ar: Transactions<Addr = Addr, Amt = Amt, Ti = Ti, Pk = Pk, Si = Si, Px = Px, Tx = Tx>,
    {
        let encryption_key = &bob_parameters.adaptor;
        let signature = <Ar::Refund>::extract_witness(refund_tx);
        wallet.recover_secret_key(refund_adaptor_sig, encryption_key, signature)
    }

    // Internal method to parse and validate the core arbitratring transactions received by Alice
    // from Bob.
    //
    // Each transaction is parsed from the protocol message and initialized from its partial
    // transaction format. After initialization validation tests are performed to ensure:
    //
    //  * the transaction template is valid (transaction is well formed, contract and keys are used
    //  correctly)
    //  * the target amount from the deal is correct (for the lock transaction)
    //  * the fee strategy validation passes
    //
    fn validate_core<Amt, Pk, Qk, Rk, Sk, Ti, F, Pr, Ms, Si, Px>(
        &self,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        core: &CoreArbitratingTransactions<Px>,
        arb_params: ArbitratingParameters<Amt, Ti, F>,
    ) -> Res<ValidatedCoreTransactions<Px, Ti, Pk>>
    where
        Ar: Transactions<Addr = Addr, Amt = Amt, Ti = Ti, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone + Fee<FeeUnit = F>,
        Amt: PartialEq + Copy,
        Pk: Copy,
        Ti: Copy,
    {
        // Extract the partial transaction from the core arbitrating message, this operation should
        // not error if the message is well formed.
        let partial_lock = core.lock.clone();

        // Initialize the lock transaction based on the extracted partial transaction format.
        let lock = <Ar::Lock>::from_partial(partial_lock);

        // Get the four keys, Alice and Bob for Buy and Cancel. The keys are needed, along with the
        // timelock for the cancel, to create the cancelable on-chain contract on the arbitrating
        // blockchain.
        let alice_buy = alice_parameters.buy;
        let bob_buy = bob_parameters.buy;
        let alice_cancel = alice_parameters.cancel;
        let bob_cancel = bob_parameters.cancel;

        // Create the data structure that represents an on-chain cancelable contract for the
        // arbitrating blockchain.
        let data_lock = DataLock {
            timelock: arb_params.cancel_timelock,
            success: SwapRoleKeys::new(alice_buy, bob_buy),
            failure: SwapRoleKeys::new(alice_cancel, bob_cancel),
        };

        // Verify the lock transaction template.
        lock.verify_template(data_lock)?;
        // The target amount is dictated from the deal.
        let target_amount = arb_params.arbitrating_amount;
        // Verify the target amount
        lock.verify_target_amount(target_amount)?;
        // Validate that the transaction follows the strategy.
        let fee_strategy = &arb_params.fee_strategy;
        lock.as_partial().validate_fee(fee_strategy)?;

        // Get the three keys, Alice and Bob for refund and Alice's punish key. The keys are
        // needed, along with the timelock for the punish, to create the punishable on-chain
        // contract on the arbitrating blockchain.
        let alice_refund = alice_parameters.refund;
        let bob_refund = bob_parameters.refund;
        let alice_punish = alice_parameters
            .punish
            .expect("Alice has a punish transaction");

        // Create the data structure that represents an on-chain punishable contract for the
        // arbitrating blockchain.
        let punish_lock = DataPunishableLock {
            timelock: arb_params.punish_timelock,
            success: SwapRoleKeys::new(alice_refund, bob_refund),
            failure: alice_punish,
        };

        // Extract the partial transaction from the core arbitrating protocol message, this
        // operation should not error if the message is well formed.
        let partial_cancel = core.cancel.clone();

        // Initialize the lock transaction based on the extracted partial transaction format.
        let cancel = <Ar::Cancel>::from_partial(partial_cancel);
        // Check that the cancel transaction is build on top of the lock.
        cancel.is_build_on_top_of(&lock)?;
        cancel.verify_template(data_lock, punish_lock)?;
        // Validate the fee strategy
        cancel.as_partial().validate_fee(fee_strategy)?;

        // Extract the partial transaction from the core arbitrating protocol message, this
        // operation should not error if the message is well formed.
        let partial_refund = core.refund.clone();

        // Initialize the refund transaction based on the extracted partial transaction format.
        let refund = <Ar::Refund>::from_partial(partial_refund);
        // Check that the refund transaction is build on top of the cancel transaction.
        refund.is_build_on_top_of(&cancel)?;
        let refund_address = bob_parameters.destination_address.clone();
        refund.verify_template(refund_address)?;
        // Validate the fee strategy
        refund.as_partial().validate_fee(fee_strategy)?;

        Ok(ValidatedCoreTransactions {
            lock: lock.to_partial(),
            cancel: cancel.to_partial(),
            refund: refund.to_partial(),
            punish_lock,
        })
    }
}

/// Bob, a [`SwapRole`], starts with arbitrating blockchain assets and exchange them for accordant
/// blockchain assets.
///
/// [`SwapRole`]: crate::role::SwapRole
#[derive(Debug, Clone)]
pub struct Bob<Addr, Ar, Ac> {
    /// The **arbitrating** blockchain to use during the swap
    pub arbitrating: Ar,
    /// The **accordant** blockchain to use during the swap
    pub accordant: Ac,
    /// An arbitrating address where, if unsuccessfully executed, the funds exchanged will be sent
    /// back to
    pub refund_address: Addr,
    /// The fee politic to apply during the swap fee calculation
    pub fee_politic: FeePriority,
}

impl<Addr, Ar, Ac> Encodable for Bob<Addr, Ar, Ac>
where
    Ar: Encodable,
    Ac: Encodable,
    Addr: CanonicalBytes,
{
    fn consensus_encode<W: io::Write>(&self, writer: &mut W) -> Result<usize, io::Error> {
        let mut len = self.fee_politic.consensus_encode(writer)?;
        len += self.arbitrating.consensus_encode(writer)?;
        len += self.accordant.consensus_encode(writer)?;
        Ok(len
            + self
                .refund_address
                .as_canonical_bytes()
                .consensus_encode(writer)?)
    }
}

impl<Addr, Ar, Ac> Decodable for Bob<Addr, Ar, Ac>
where
    Ar: Decodable,
    Ac: Decodable,
    Addr: CanonicalBytes,
{
    fn consensus_decode<D: io::Read>(d: &mut D) -> Result<Self, consensus::Error> {
        let fee_politic = FeePriority::consensus_decode(d)?;
        let arbitrating = Decodable::consensus_decode(d)?;
        let accordant = Decodable::consensus_decode(d)?;
        let refund_address = Addr::from_canonical_bytes(unwrap_vec_ref!(d).as_ref())?;
        Ok(Bob {
            arbitrating,
            accordant,
            refund_address,
            fee_politic,
        })
    }
}

impl<Addr, Ar, Ac> Bob<Addr, Ar, Ac> {
    /// Create a new [`Bob`] role with the local parameters.
    pub fn new(
        arbitrating: Ar,
        accordant: Ac,
        refund_address: Addr,
        fee_politic: FeePriority,
    ) -> Self {
        Self {
            arbitrating,
            accordant,
            refund_address,
            fee_politic,
        }
    }
}

impl<Addr, Ar, Ac> Bob<Addr, Ar, Ac>
where
    Addr: Clone,
{
    /// Generate Bob’s parameters for the protocol execution based on a key generator.
    ///
    /// # Safety
    ///
    /// All the data passed to the function are considered trusted and does not require extra
    /// validation. The deal is assumend to be validated by user upfront.
    ///
    pub fn generate_parameters<Amt, Bmt, Pk, Qk, Rk, Sk, Ti, F, Pr, Kg>(
        &self,
        key_gen: &mut Kg,
        deal: &Deal<Amt, Bmt, Ti, F>,
    ) -> Res<Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>>
    where
        Ar: DeriveKeys<PublicKey = Pk, PrivateKey = Rk>,
        Ac: DeriveKeys<PublicKey = Qk, PrivateKey = Sk>,
        Ti: Copy,
        F: Clone,
        Kg: KeyGenerator<Pk, Qk, Rk, Sk, Pr>,
    {
        let extra_arbitrating_keys: Res<TaggedExtraKeys<Pk>> = Ar::extra_public_keys()
            .into_iter()
            .map(|tag| {
                let key = key_gen.get_pubkey(ArbitratingKeyId::Extra(tag))?;
                Ok(TaggedElement::new(tag, key))
            })
            .collect();

        let arbitrating_shared_keys: Res<TaggedSharedKeys<Rk>> = Ar::extra_shared_private_keys()
            .into_iter()
            .map(|tag| {
                let key = key_gen.get_shared_key(tag)?;
                Ok(TaggedElement::new(tag, key))
            })
            .collect();

        let extra_accordant_keys: Res<TaggedExtraKeys<Qk>> = Ac::extra_public_keys()
            .into_iter()
            .map(|tag| {
                let key = key_gen.get_pubkey(AccordantKeyId::Extra(tag))?;
                Ok(TaggedElement::new(tag, key))
            })
            .collect();

        let accordant_shared_keys: Res<TaggedSharedKeys<Sk>> = Ac::extra_shared_private_keys()
            .into_iter()
            .map(|tag| {
                let key = key_gen.get_shared_key(tag)?;
                Ok(TaggedElement::new(tag, key))
            })
            .collect();

        let (spend, adaptor, proof) = key_gen.generate_proof()?;

        Ok(Parameters {
            buy: key_gen.get_pubkey(ArbitratingKeyId::Buy)?,
            cancel: key_gen.get_pubkey(ArbitratingKeyId::Cancel)?,
            refund: key_gen.get_pubkey(ArbitratingKeyId::Refund)?,
            punish: None,
            adaptor,
            extra_arbitrating_keys: extra_arbitrating_keys?,
            arbitrating_shared_keys: arbitrating_shared_keys?,
            spend,
            extra_accordant_keys: extra_accordant_keys?,
            accordant_shared_keys: accordant_shared_keys?,
            proof: Some(proof),
            destination_address: self.refund_address.clone(),
            cancel_timelock: Some(deal.parameters.cancel_timelock),
            punish_timelock: Some(deal.parameters.punish_timelock),
            fee_strategy: Some(deal.parameters.fee_strategy.clone()),
        })
    }

    /// Initialize the core arbitrating transactions composed of: [`Lockable`], [`Cancelable`], and
    /// [`Refundable`] transactions.
    ///
    /// # Safety
    ///
    /// Alice's [`Parameters`] (i.e. counter-party parameters) are created and validated with the
    /// protocol messages that commit and reveal the values.
    ///
    /// **This function assumes that the commit/reveal scheme has been validated and assumes that
    /// all cryptographic proof needed for securing the system have passed the validation.**
    ///
    /// _Previously verified data_:
    ///  * `alice_parameters`: Alice's parameters
    ///  * `arb_params`: The core arbitrating parameter used in verification
    ///
    /// _Trusted data_:
    ///  * `bob_parameters`: Bob's own parameters
    ///  * `funding`: Bob's own funding transaction
    ///
    /// # Execution
    ///
    /// The parameters to create the three transactions are:
    ///  * Alice's public keys present in Alice's [`Parameters`]
    ///  * Bob's public keys present in Bob's [`Parameters`]
    ///  * The [`Fundable`] transaction
    ///  * The [`FeeStrategy`] and the [`FeePriority`]
    ///
    /// The lock transaction is initialized by passing the [`DataLock`] structure, then the cancel
    /// transaction is initialized based on the lock transaction with the [`DataPunishableLock`]
    /// structure, then the punish is initialized based on the cancel transaction.
    ///
    /// # Transaction Fee
    ///
    /// The fee on each transactions are set according to the [`FeeStrategy`] specified in the deal
    /// and the [`FeePriority`] in `self`.
    ///
    /// [`FeeStrategy`]: crate::blockchain::FeeStrategy
    ///
    pub fn core_arbitrating_transactions<Amt, Tx, Px, Pk, Qk, Rk, Sk, Ti, F, Pr, Out>(
        &self,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        funding: impl Fundable<Tx, Out, Addr, Pk>,
        arb_params: ArbitratingParameters<Amt, Ti, F>,
    ) -> Res<CoreArbitratingTransactions<Px>>
    where
        Ar: Transactions<Addr = Addr, Amt = Amt, Tx = Tx, Out = Out, Ti = Ti, Pk = Pk, Px = Px>,
        Px: Fee<FeeUnit = F>,
        Out: Eq,
        Pk: Copy,
        Amt: Copy,
        Ti: Copy,
    {
        // Initialize the fundable transaction to build the lockable transaction on top of it.
        //
        // The fundable transaction `funding` contains all the logic to build on top of a
        // externally created transaction seen on-chain asyncronously by a syncer when broadcasted
        // by the external wallet.

        // Get the four keys, Alice and Bob for Buy and Cancel. The keys are needed, along with the
        // timelock for the cancel, to create the cancelable on-chain contract on the arbitrating
        // blockchain.
        let alice_buy = alice_parameters.buy;
        let bob_buy = bob_parameters.buy;
        let alice_cancel = alice_parameters.cancel;
        let bob_cancel = bob_parameters.cancel;

        // Create the data structure that represents an on-chain cancelable contract for the
        // arbitrating blockchain.
        let cancel_lock = DataLock {
            timelock: arb_params.cancel_timelock,
            success: SwapRoleKeys::new(alice_buy, bob_buy),
            failure: SwapRoleKeys::new(alice_cancel, bob_cancel),
        };

        // The target amount is dictated from the deal.
        let target_amount = arb_params.arbitrating_amount;

        // Initialize the lockable transaction based on the fundable structure. The lockable
        // transaction prepare the on-chain contract for a buy or a cancel. The amount of available
        // assets is defined as the target by the deal.
        let lock = <Ar::Lock>::initialize(&funding, cancel_lock, target_amount)?;

        // Ensure that the transaction contains enough assets to pass the fee validation latter.
        let fee_strategy = &arb_params.fee_strategy;
        lock.as_partial().validate_fee(fee_strategy)?;

        // Get the three keys, Alice and Bob for refund and Alice's punish key. The keys are
        // needed, along with the timelock for the punish, to create the punishable on-chain
        // contract on the arbitrating blockchain.
        let alice_refund = alice_parameters.refund;
        let bob_refund = bob_parameters.refund;
        let alice_punish = alice_parameters.punish.expect("Alice has punish key");

        // Create the data structure that represents an on-chain punishable contract for the
        // arbitrating blockchain.
        let punish_lock = DataPunishableLock {
            timelock: arb_params.punish_timelock,
            success: SwapRoleKeys::new(alice_refund, bob_refund),
            failure: alice_punish,
        };

        // Initialize the cancel transaction for the lock transaction, removing the funds from the
        // buy and moving them into a punisable on-chain contract.
        let mut cancel = <Ar::Cancel>::initialize(&lock, cancel_lock, punish_lock)?;

        // Set the fees according to the strategy in the deal and the local politic.
        cancel
            .as_partial_mut()
            .set_fee(fee_strategy, self.fee_politic)?;

        // Initialize the refund transaction for the cancel transaction, moving the funds out of
        // the punishable lock to Bob's refund address.
        let mut refund = <Ar::Refund>::initialize(&cancel, self.refund_address.clone())?;

        // Set the fees according to the strategy in the deal and the local politic.
        refund
            .as_partial_mut()
            .set_fee(fee_strategy, self.fee_politic)?;

        Ok(CoreArbitratingTransactions {
            lock: lock.to_partial(),
            cancel: cancel.to_partial(),
            refund: refund.to_partial(),
        })
    }

    /// Co-sign the arbitrating [`Cancelable`] transaction.
    ///
    /// # Safety
    ///
    /// All the data passed to [`cosign_arbitrating_cancel`] are considered trusted.
    ///
    /// The signature is created by Bob and does not require any extra validation.
    ///
    /// # Execution
    ///
    ///  * Parse the [`Cancelable`] partial transaction in [`CoreArbitratingTransactions`]
    ///  * Retreive the cancel public key from the paramters
    ///  * Generate the witness data and sign it
    ///
    /// Returns the signature.
    ///
    /// [`cosign_arbitrating_cancel`]: Bob::cosign_arbitrating_cancel
    ///
    pub fn cosign_arbitrating_cancel<S, Px, Si, Pk, Ms>(
        &self,
        wallet: &mut S,
        core: &CoreArbitratingTransactions<Px>,
    ) -> Res<Si>
    where
        S: Sign<Pk, Ms, Si>,
        Ar: Transactions<Addr = Addr, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone,
    {
        // Extract the partial transaction from the core arbitrating protocol message, this
        // operation should not error if the message is well formed.
        let partial_cancel = core.cancel.clone();

        // Initialize the cancel transaction based on the partial transaction format.
        let cancel = <Ar::Cancel>::from_partial(partial_cancel);

        // Generate the witness message to sign and sign with the cancel key.
        let msg = cancel.generate_witness_message(ScriptPath::Failure)?;
        wallet
            .sign(ArbitratingKeyId::Cancel, msg)
            .map_err(Into::into)
    }

    /// Validates the adaptor refund witness based on the parameters and the core arbitrating
    /// transactions.
    ///
    /// # Safety
    ///
    /// Alice's [`Parameters`] (i.e. counter-party parameters) are created and validated with the
    /// protocol messages that commit and reveal the values.
    ///
    /// **This function assumes that the commit/reveal scheme has been validated and assumes that
    /// all cryptographic proof needed for securing the system have passed the validation.**
    ///
    /// [`CoreArbitratingTransactions`] protocol message is created by Bob and does not require any
    /// extra validation.
    ///
    /// _Previously verified data_:
    ///  * `alice_parameters`: Alice's parameters
    ///
    /// _Trusted data_:
    ///  * `bob_parameters`: Bob's own parameters
    ///  * `core`: Core arbitrating transactions
    ///
    /// _Verified data_:
    ///  * `refund_adaptor_sig`: The adaptor witness to verify
    ///
    /// # Execution
    ///
    ///  * Parse the [`Refundable`] partial transaction in [`CoreArbitratingTransactions`]
    ///  * Verify the adaptor witness with the public keys from the parameters
    ///
    /// Return `Ok(())` if all tests succeed.
    ///
    pub fn validate_adaptor_refund<Amt, Px, Pk, Qk, Rk, Sk, Ti, F, Pr, S, Ms, Si, EncSig>(
        &self,
        wallet: &mut S,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        core: &CoreArbitratingTransactions<Px>,
        refund_adaptor_sig: &EncSig,
    ) -> Res<()>
    where
        S: EncSign<Pk, Ms, Si, EncSig>,
        Ar: Transactions<Addr = Addr, Amt = Amt, Ti = Ti, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone,
    {
        // Extract the partial transaction from the core arbitrating protocol message, this
        // operation should not error if the message is well formed.
        let partial_refund = core.refund.clone();

        // Initialize the refund transaction based on the partial transaction format.
        let refund = <Ar::Refund>::from_partial(partial_refund);

        // Verify the adaptor refund witness
        let msg = refund.generate_witness_message(ScriptPath::Success)?;
        wallet.verify_encrypted_signature(
            &alice_parameters.refund,
            &bob_parameters.adaptor,
            msg,
            refund_adaptor_sig,
        )?;

        Ok(())
    }

    /// Creates the [`Buyable`] transaction and generate the adaptor witness
    ///
    /// # Safety
    ///
    /// This function **MUST NOT** be run if [`validate_adaptor_refund`] is not successful.
    ///
    /// This function **MUST NOT** be run if the accordant assets are not confirmed on-chain.
    ///
    /// Alice's [`Parameters`] (i.e. counter-party parameters) are created and validated with the
    /// protocol messages that commit and reveal the values.
    ///
    /// **This function assumes that the commit/reveal scheme has been validated and assumes that
    /// all cryptographic proof needed for securing the system have passed the validation.**
    ///
    /// [`CoreArbitratingTransactions`] protocol message is created by Bob and does not require any
    /// extra validation.
    ///
    /// _Previously verified data_:
    ///  * `alice_parameters`: Alice's parameters
    ///  * `arb_params`: The parameters used to verify core
    ///
    /// _Trusted data_:
    ///  * `wallet`: Bob's own wallet, used to perform cryptographic operations
    ///  * `bob_parameters`: Bob's own parameters
    ///  * `core`: Core arbitrating transactions
    ///
    /// # Execution
    ///
    ///  * Parse the [`Lockable`] partial transaction in [`CoreArbitratingTransactions`]
    ///  * Generate the [`DataLock`] structure from Alice and Bob parameters and the deal
    ///  * Retrieve Alice's adaptor public key from Alice's [`Parameters`]
    ///  * Retreive the buy public key from the paramters
    ///  * Generate the adaptor witness data and sign it
    ///
    /// Returns the partial transaction and the signature inside the [`BuyProcedureSignature`]
    /// protocol message.
    ///
    /// [`sign_adaptor_buy`]: Bob::sign_adaptor_buy
    /// [`validate_adaptor_refund`]: Bob::validate_adaptor_refund
    ///
    pub fn sign_adaptor_buy<Amt, Px, Pk, Qk, Rk, Sk, Ti, F, Pr, S, Ms, Si, EncSig, U>(
        &self,
        swap_id: U,
        wallet: &mut S,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        bob_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        core: &CoreArbitratingTransactions<Px>,
        arb_params: ArbitratingParameters<Amt, Ti, F>,
    ) -> Res<BuyProcedureSignature<Px, EncSig>>
    where
        S: EncSign<Pk, Ms, Si, EncSig>,
        Ar: Transactions<Addr = Addr, Amt = Amt, Ti = Ti, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone + Fee<FeeUnit = F>,
        Pk: Copy,
        Ti: Copy,
        U: Into<SwapId>,
    {
        // Extract the partial transaction from the core arbitrating protocol message, this
        // operation should not error if the message is well formed.
        let partial_lock = core.lock.clone();

        // Initialize the lock transaction based on the partial transaction format.
        let lock = <Ar::Lock>::from_partial(partial_lock);

        // Get the four keys, Alice and Bob for Buy and Cancel. The keys are needed, along with the
        // timelock for the cancel, to create the cancelable on-chain contract on the arbitrating
        // blockchain.
        let alice_buy = alice_parameters.buy;
        let bob_buy = bob_parameters.buy;
        let alice_cancel = alice_parameters.cancel;
        let bob_cancel = bob_parameters.cancel;

        // Create the data structure that represents an on-chain cancelable contract for the
        // arbitrating blockchain.
        let cancel_lock = DataLock {
            timelock: arb_params.cancel_timelock,
            success: SwapRoleKeys::new(alice_buy, bob_buy),
            failure: SwapRoleKeys::new(alice_cancel, bob_cancel),
        };

        // Initialize the buy transaction based on the lock and the data lock. The buy transaction
        // consumes the success path of the lock and send the funds into Alice's destination
        // address.
        let mut buy = <Ar::Buy>::initialize(
            &lock,
            cancel_lock,
            alice_parameters.destination_address.clone(),
        )?;

        // Set the fees according to the strategy in the deal and the local politic.
        let fee_strategy = &arb_params.fee_strategy;
        buy.as_partial_mut()
            .set_fee(fee_strategy, self.fee_politic)?;

        // Generate the witness message to sign and adaptor sign with the buy key and the
        // counter-party adaptor.
        let adaptor = &alice_parameters.adaptor;
        let msg = buy.generate_witness_message(ScriptPath::Success)?;
        let sig = wallet.encrypt_sign(ArbitratingKeyId::Buy, adaptor, msg)?;

        Ok(BuyProcedureSignature {
            swap_id: swap_id.into(),
            buy: buy.to_partial(),
            buy_adaptor_sig: sig,
        })
    }

    /// Sign the arbitrating [`Lockable`] transaction and return the signature.
    ///
    /// # Safety
    ///
    /// This function **MUST NOT** be run if [`validate_adaptor_refund`] is not successful.
    ///
    /// All the data passed to [`sign_arbitrating_lock`] are considered trusted.
    ///
    /// [`CoreArbitratingTransactions`] protocol message is created by Bob and does not require any
    /// extra validation.
    ///
    /// # Execution
    ///
    ///  * Parse the [`Lockable`] partial transaction in [`CoreArbitratingTransactions`]
    ///  * Retreive the funding public key from the paramters
    ///  * Generate the witness data and sign it
    ///
    /// Returns the signature.
    ///
    /// [`sign_arbitrating_lock`]: Bob::sign_arbitrating_lock
    /// [`validate_adaptor_refund`]: Bob::validate_adaptor_refund
    ///
    pub fn sign_arbitrating_lock<S, Px, Si, Pk, Ms>(
        &self,
        wallet: &mut S,
        core: &CoreArbitratingTransactions<Px>,
    ) -> Res<Si>
    where
        S: Sign<Pk, Ms, Si>,
        Ar: Transactions<Addr = Addr, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        Px: Clone,
    {
        // Extract the partial transaction from the core arbitrating protocol message, this
        // operation should not error if the message is well formed.
        let partial_lock = core.lock.clone();

        // Initialize the lock transaction based on the partial transaction format.
        let lock = <Ar::Lock>::from_partial(partial_lock);

        // Generate the witness message to sign and sign with the fund key.
        let msg = lock.generate_witness_message(ScriptPath::Success)?;
        wallet.sign(ArbitratingKeyId::Lock, msg).map_err(Into::into)
    }

    /// Sign the arbitrating [`Refundable`] transaction and adapt the counter-party adaptor witness
    /// with the private adaptor key.
    ///
    /// # Safety
    ///
    /// This function **MUST NOT** be run if [`validate_adaptor_refund`] is not successful.
    ///
    /// The encrypted signature is created by Alice and must be verified to be a valid.
    ///
    /// **This function assumes that the adaptor signature has been validated and assumes that all
    /// cryptographic proof needed for securing the system have passed the validation.**
    ///
    /// [`CoreArbitratingTransactions`] protocol message is created by Bob and does not require any
    /// extra validation.
    ///
    /// # Execution
    ///
    ///  * Parse the [`Refundable`] partial transaction in [`CoreArbitratingTransactions`]
    ///  * Retreive the refund public key from the paramters
    ///  * Generate the refund witness data
    ///  * Retreive the adaptor public key from the pamaters
    ///  * Adapt the signature
    ///
    /// Returns the signatures inside a [`TxSignatures`] structure.
    ///
    /// [`validate_adaptor_refund`]: Bob::validate_adaptor_refund
    ///
    pub fn fully_sign_refund<S, Px, Si, Pk, Ms, EncSig>(
        &self,
        wallet: &mut S,
        core: &CoreArbitratingTransactions<Px>,
        signed_adaptor_refund: &EncSig,
    ) -> Res<TxSignatures<Si>>
    where
        S: Sign<Pk, Ms, Si> + EncSign<Pk, Ms, Si, EncSig>,
        Ar: Transactions<Addr = Addr, Ms = Ms, Pk = Pk, Si = Si, Px = Px>,
        EncSig: Clone,
        Px: Clone,
    {
        // Extract the partial transaction from the core arbitrating protocol message, this
        // operation should not error if the message is well formed.
        let partial_refund = core.refund.clone();

        // Initialize the refund transaction based on the partial transaction format.
        let refund = <Ar::Refund>::from_partial(partial_refund);

        // Generate the witness message to sign and sign with the refund key.
        let msg = refund.generate_witness_message(ScriptPath::Success)?;
        let sig = wallet.sign(ArbitratingKeyId::Refund, msg)?;

        let adapted_sig =
            wallet.decrypt_signature(AccordantKeyId::Spend, signed_adaptor_refund.clone())?;

        Ok(TxSignatures { sig, adapted_sig })
    }

    /// This function allows to recover the secret key used to encrypt the buy signature, allowing
    /// Bob to recover Alice's secret and transfer ownership of funds.
    pub fn recover_accordant_key<S, Tx, Px, Si, Pk, Qk, Rk, Sk, Ti, F, Pr, EncSig>(
        &self,
        wallet: &mut S,
        alice_parameters: &Parameters<Pk, Qk, Rk, Sk, Addr, Ti, F, Pr>,
        buy_adaptor_sig: EncSig,
        buy_tx: Tx,
    ) -> Rk
    where
        S: RecoverSecret<Pk, Rk, Si, EncSig>,
        Ar: Transactions<Addr = Addr, Tx = Tx, Px = Px, Pk = Pk, Si = Si>,
    {
        let encryption_key = &alice_parameters.adaptor;
        let signature = <Ar::Buy>::extract_witness(buy_tx);
        wallet.recover_secret_key(buy_adaptor_sig, encryption_key, signature)
    }
}