Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:

- uses: Swatinem/rust-cache@v1.4.0

- run: cargo test --features serde --verbose
- run: cargo test --verbose

rpc-test:
name: Integration tests
Expand Down
11 changes: 5 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ rust-version = "1.56.1"
rpc = []
experimental = ["ecdsa_fun", "secp256kfun", "rand", "sha2", "rand_chacha", "bincode"]
taproot = []
serde = ["serde_crate", "bitcoin/use-serde", "monero/serde_support", "inet2_addr/serde"]

default = ["experimental", "taproot"]

Expand All @@ -36,9 +35,9 @@ bitcoin_hashes = { version = "0.10" }
bitvec = { version = "1.0" }
fixed-hash = { version = "0.7", default-features = false }
hex = "0.4"
inet2_addr = { version = "0.6", default-features = false, features = ["tor", "strict_encoding"] }
inet2_addr = { version = "0.6", default-features = false, features = ["tor", "strict_encoding", "serde"] }
lightning_encoding = "0.6"
serde_crate = { package = "serde", version = "1", features = ["derive"], optional = true }
serde = { version = "1", features = ["derive"] }
strict_encoding = "1.8"
strict_encoding_derive = "1.7"
thiserror = "1"
Expand All @@ -47,7 +46,7 @@ tiny-keccak = { version = "2", features = ["keccak"] }
# crypto libs

bincode = { version = "1", optional = true }
curve25519-dalek = "3"
curve25519-dalek = { version = "3", features = ["serde"] }
ecdsa_fun = { git = "https://github.com/farcaster-project/secp256kfun.git", branch = "secp256k1/0.22", default-features = false, features = ["all"], optional = true }
rand = { version = "0.8.4", optional = true }
rand_alt = { package = "rand", version = "0.7.3", features = ["std"] }
Expand All @@ -57,8 +56,8 @@ sha2 = { version = "0.9", optional = true }
sha3 = "0.10"

# blockchain specific
bitcoin = "0.28.0"
monero = { version = "0.16" }
bitcoin = { version = "0.28", features = ["use-serde"] }
monero = { version = "0.17", features = ["serde"] }

[dev-dependencies]
bitcoincore-rpc = "0.15"
Expand Down
31 changes: 15 additions & 16 deletions src/bitcoin/fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use std::str::FromStr;
/// An amount of Bitcoin (internally in satoshis) representing the number of satoshis per virtual
/// byte a transaction must use for its fee. A [`FeeStrategy`] can use one of more of this type
/// depending of its complexity (fixed, range, etc).
#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Display)]
#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq, Display)]
#[display(display_sats_per_vbyte)]
pub struct SatPerVByte(Amount);

Expand Down Expand Up @@ -117,22 +117,24 @@ fn get_available_input_sat(tx: &PartiallySignedTransaction) -> Result<Amount, Fe
))
}

impl<S: Strategy> Fee for Bitcoin<S> {
impl Fee for PartiallySignedTransaction {
type FeeUnit = SatPerVByte;

type Amount = Amount;

/// Calculates and sets the fees on the given transaction and return the fees set
fn set_fee(
tx: &mut PartiallySignedTransaction,
&mut self,
strategy: &FeeStrategy<SatPerVByte>,
politic: FeePriority,
) -> Result<Amount, FeeStrategyError> {
if tx.unsigned_tx.output.len() != 1 {
) -> Result<Self::Amount, FeeStrategyError> {
if self.unsigned_tx.output.len() != 1 {
return Err(FeeStrategyError::new(
transaction::Error::MultiUTXOUnsuported,
));
}

let input_sum = get_available_input_sat(tx)?;
let input_sum = get_available_input_sat(self)?;

// FIXME This does not account for witnesses
// currently the fees are wrong
Expand All @@ -142,7 +144,7 @@ impl<S: Strategy> Fee for Bitcoin<S> {
// times four. For transactions with a witness, this is the non-witness
// consensus-serialized size multiplied by three plus the with-witness consensus-serialized
// size.
let weight = tx.unsigned_tx.weight() as u64;
let weight = self.unsigned_tx.weight() as u64;

// Compute the fee amount to set in total
let fee_amount = match strategy {
Expand All @@ -155,7 +157,7 @@ impl<S: Strategy> Fee for Bitcoin<S> {
.ok_or(FeeStrategyError::AmountOfFeeTooHigh)?;

// Apply the fee on the first output
tx.unsigned_tx.output[0].value = input_sum
self.unsigned_tx.output[0].value = input_sum
.checked_sub(fee_amount)
.ok_or(FeeStrategyError::NotEnoughAssets)?
.as_sat();
Expand All @@ -165,22 +167,19 @@ impl<S: Strategy> Fee for Bitcoin<S> {
}

/// Validates that the fees for the given transaction are set accordingly to the strategy
fn validate_fee(
tx: &PartiallySignedTransaction,
strategy: &FeeStrategy<SatPerVByte>,
) -> Result<bool, FeeStrategyError> {
if tx.unsigned_tx.output.len() != 1 {
fn validate_fee(&self, strategy: &FeeStrategy<SatPerVByte>) -> Result<bool, FeeStrategyError> {
if self.unsigned_tx.output.len() != 1 {
return Err(FeeStrategyError::new(
transaction::Error::MultiUTXOUnsuported,
));
}

let input_sum = get_available_input_sat(tx)?.as_sat();
let output_sum = tx.unsigned_tx.output[0].value;
let input_sum = get_available_input_sat(self)?.as_sat();
let output_sum = self.unsigned_tx.output[0].value;
let fee = input_sum
.checked_sub(output_sum)
.ok_or(FeeStrategyError::AmountOfFeeTooHigh)?;
let weight = tx.unsigned_tx.weight() as u64;
let weight = self.unsigned_tx.weight() as u64;

let effective_sat_per_vbyte = SatPerVByte::from_sat(
weight
Expand Down
69 changes: 31 additions & 38 deletions src/bitcoin/segwitv0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ use crate::bitcoin::transaction::TxInRef;
use crate::bitcoin::transaction::{MetadataOutput, Tx};
use crate::bitcoin::{Bitcoin, BitcoinSegwitV0, Btc, Strategy};

use crate::bitcoin::timelock::CSVTimelock;
use crate::blockchain::Transactions;
use crate::consensus::{self, CanonicalBytes};
use crate::crypto::{Keys, SharedKeyId, SharedSecretKeys, Signatures};
use crate::role::{Arbitrating, SwapRole};
use crate::crypto::{DeriveKeys, SharedKeyId, Signatures};
use crate::role::SwapRole;
use crate::script::{DataLock, DataPunishableLock, DoubleKeys, ScriptPath};

use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::script::{Builder, Instruction, Script};
use bitcoin::blockdata::transaction::EcdsaSighashType;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::secp256k1::{ecdsa::Signature, Message, PublicKey, Secp256k1, SecretKey, Signing};
use bitcoin::util::psbt::PartiallySignedTransaction;
use bitcoin::util::sighash::SighashCache;

use ecdsa_fun::adaptor::EncryptedSignature;
Expand Down Expand Up @@ -86,20 +88,20 @@ pub struct CoopLock {
}

impl CoopLock {
pub fn script(data: DataLock<BitcoinSegwitV0>) -> Script {
pub fn script(data: DataLock<CSVTimelock, PublicKey>) -> Script {
let DataLock {
success: DoubleKeys { alice, bob },
..
} = data;
Builder::new()
.push_key(&bitcoin::util::key::PublicKey::new(*alice))
.push_key(&bitcoin::util::key::PublicKey::new(alice))
.push_opcode(opcodes::all::OP_CHECKSIGVERIFY)
.push_key(&bitcoin::util::key::PublicKey::new(*bob))
.push_key(&bitcoin::util::key::PublicKey::new(bob))
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}

pub fn v0_p2wsh(data: DataLock<BitcoinSegwitV0>) -> Script {
pub fn v0_p2wsh(data: DataLock<CSVTimelock, PublicKey>) -> Script {
Self::script(data).to_v0_p2wsh()
}

Expand Down Expand Up @@ -177,29 +179,29 @@ pub struct PunishLock {
}

impl PunishLock {
pub fn script(data: DataPunishableLock<BitcoinSegwitV0>) -> Script {
pub fn script(data: DataPunishableLock<CSVTimelock, PublicKey>) -> Script {
let DataPunishableLock {
timelock,
success: DoubleKeys { alice, bob },
failure,
} = data;
Builder::new()
.push_opcode(opcodes::all::OP_IF)
.push_key(&bitcoin::util::key::PublicKey::new(*alice))
.push_key(&bitcoin::util::key::PublicKey::new(alice))
.push_opcode(opcodes::all::OP_CHECKSIGVERIFY)
.push_key(&bitcoin::util::key::PublicKey::new(*bob))
.push_key(&bitcoin::util::key::PublicKey::new(bob))
.push_opcode(opcodes::all::OP_CHECKSIG)
.push_opcode(opcodes::all::OP_ELSE)
.push_int(timelock.as_u32().into())
.push_opcode(opcodes::all::OP_CSV)
.push_opcode(opcodes::all::OP_DROP)
.push_key(&bitcoin::util::key::PublicKey::new(*failure))
.push_key(&bitcoin::util::key::PublicKey::new(failure))
.push_opcode(opcodes::all::OP_CHECKSIG)
.push_opcode(opcodes::all::OP_ENDIF)
.into_script()
}

pub fn v0_p2wsh(data: DataPunishableLock<BitcoinSegwitV0>) -> Script {
pub fn v0_p2wsh(data: DataPunishableLock<CSVTimelock, PublicKey>) -> Script {
Self::script(data).to_v0_p2wsh()
}

Expand Down Expand Up @@ -336,7 +338,7 @@ impl PunishLock {
}
}

impl Arbitrating for Bitcoin<SegwitV0> {}
//impl Arbitrating for Bitcoin<SegwitV0> {}

impl TryFrom<Btc> for Bitcoin<SegwitV0> {
type Error = consensus::Error;
Expand All @@ -350,7 +352,15 @@ impl TryFrom<Btc> for Bitcoin<SegwitV0> {
}

impl Transactions for Bitcoin<SegwitV0> {
type Metadata = MetadataOutput;
type Addr = bitcoin::Address;
type Amt = bitcoin::Amount;
type Tx = bitcoin::Transaction;
type Px = PartiallySignedTransaction;
type Out = MetadataOutput;
type Ti = CSVTimelock;
type Ms = Sha256dHash;
type Pk = PublicKey;
type Si = Signature;

type Funding = Funding;
type Lock = Tx<Lock>;
Expand All @@ -360,14 +370,19 @@ impl Transactions for Bitcoin<SegwitV0> {
type Punish = Tx<Punish>;
}

impl Keys for Bitcoin<SegwitV0> {
type SecretKey = SecretKey;
impl DeriveKeys for Bitcoin<SegwitV0> {
type PublicKey = PublicKey;
type PrivateKey = SecretKey;

fn extra_keys() -> Vec<u16> {
fn extra_public_keys() -> Vec<u16> {
// No extra key
vec![]
}

fn extra_shared_private_keys() -> Vec<SharedKeyId> {
// No shared key in Bitcoin, transparent ledger
vec![]
}
}

impl CanonicalBytes for SecretKey {
Expand All @@ -383,28 +398,6 @@ impl CanonicalBytes for SecretKey {
}
}

impl CanonicalBytes for PublicKey {
fn as_canonical_bytes(&self) -> Vec<u8> {
self.serialize().as_ref().into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
PublicKey::from_slice(bytes).map_err(consensus::Error::new)
}
}

impl SharedSecretKeys for Bitcoin<SegwitV0> {
type SharedSecretKey = SecretKey;

fn shared_keys() -> Vec<SharedKeyId> {
// No shared key in Bitcoin, transparent ledger
vec![]
}
}

impl Signatures for Bitcoin<SegwitV0> {
type Message = Sha256dHash;
type Signature = Signature;
Expand Down
33 changes: 30 additions & 3 deletions src/bitcoin/segwitv0/buy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ use std::marker::PhantomData;
use bitcoin::blockdata::transaction::{TxIn, TxOut};
use bitcoin::blockdata::witness::Witness;
use bitcoin::secp256k1::ecdsa::Signature;
use bitcoin::secp256k1::PublicKey;
use bitcoin::util::ecdsa::EcdsaSig;
use bitcoin::util::psbt::PartiallySignedTransaction;
use bitcoin::Address;
use bitcoin::Amount;
use bitcoin::Transaction;

use crate::role::SwapRole;
use crate::script;
use crate::transaction::{Buyable, Error as FError, Lockable};

use crate::bitcoin::segwitv0::Sha256dHash;
use crate::bitcoin::segwitv0::{CoopLock, SegwitV0};
use crate::bitcoin::timelock::CSVTimelock;
use crate::bitcoin::transaction::{Error, MetadataOutput, SubTransaction, Tx};
use crate::bitcoin::Bitcoin;

Expand Down Expand Up @@ -53,10 +58,32 @@ impl SubTransaction for Buy {
}
}

impl Buyable<Bitcoin<SegwitV0>, MetadataOutput> for Tx<Buy> {
impl
Buyable<
Address,
Transaction,
PartiallySignedTransaction,
MetadataOutput,
Amount,
CSVTimelock,
Sha256dHash,
PublicKey,
Signature,
> for Tx<Buy>
{
fn initialize(
prev: &impl Lockable<Bitcoin<SegwitV0>, MetadataOutput>,
_lock: script::DataLock<Bitcoin<SegwitV0>>,
prev: &impl Lockable<
Address,
Transaction,
PartiallySignedTransaction,
MetadataOutput,
Amount,
CSVTimelock,
Sha256dHash,
PublicKey,
Signature,
>,
_lock: script::DataLock<CSVTimelock, PublicKey>,
destination_target: Address,
) -> Result<Self, FError> {
let output_metadata = prev.get_consumable_output()?;
Expand Down
Loading