mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-24 23:19:34 -05:00
Merge branch 'master' into monero-unlocked-balance
This commit is contained in:
commit
55e7d3f425
20
.github/workflows/build-release-binaries.yml
vendored
20
.github/workflows/build-release-binaries.yml
vendored
@ -50,17 +50,21 @@ jobs:
|
||||
ref: ${{ github.event.release.target_commitish }}
|
||||
token: ${{ secrets.BOTTY_GITHUB_TOKEN }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2.0.2
|
||||
- uses: Swatinem/rust-cache@v2.2.0
|
||||
|
||||
- name: Install compiler for armhf arch
|
||||
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-arm-linux-gnueabihf
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
override: true
|
||||
|
||||
- name: Build ${{ matrix.target }} ${{ matrix.bin }} release binary
|
||||
|
||||
run: cargo build --target=${{ matrix.target }} --release --package swap --bin ${{ matrix.bin }}
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --target=${{ matrix.target }} --release --package swap --bin ${{ matrix.bin }}
|
||||
use-cross: true
|
||||
|
||||
- name: Smoke test the binary
|
||||
if: matrix.target != 'armv7-unknown-linux-gnueabihf' # armv7-unknown-linux-gnueabihf is only cross-compiled, no smoke test
|
||||
|
68
.github/workflows/ci.yml
vendored
68
.github/workflows/ci.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3.1.0
|
||||
|
||||
- uses: Swatinem/rust-cache@v2.0.2
|
||||
- uses: Swatinem/rust-cache@v2.2.0
|
||||
|
||||
- name: Check formatting
|
||||
uses: dprint/check@v2.1
|
||||
@ -26,6 +26,36 @@ jobs:
|
||||
- name: Run clippy with all features enabled
|
||||
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
|
||||
bdk_test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3.1.0
|
||||
|
||||
- uses: Swatinem/rust-cache@v2.0.2
|
||||
|
||||
- name: Build swap
|
||||
run: cargo build --bin swap
|
||||
|
||||
- name: Run BDK regression script
|
||||
run: ./swap/tests/bdk.sh
|
||||
|
||||
sqlx_test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3.1.0
|
||||
|
||||
- uses: Swatinem/rust-cache@v2.0.2
|
||||
|
||||
- name: Install sqlx-cli
|
||||
run: cargo install sqlx-cli
|
||||
|
||||
- name: Run sqlite_dev_setup.sh script
|
||||
run: |
|
||||
cd swap
|
||||
./sqlite_dev_setup.sh
|
||||
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
@ -43,26 +73,38 @@ jobs:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3.1.0
|
||||
|
||||
- uses: Swatinem/rust-cache@v2.0.2
|
||||
- uses: Swatinem/rust-cache@v2.2.0
|
||||
|
||||
- name: Install compiler for armhf arch
|
||||
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-arm-linux-gnueabihf
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
override: true
|
||||
|
||||
- name: Build binary
|
||||
run: |
|
||||
cargo build -p swap --target ${{ matrix.target }}
|
||||
if: matrix.target != 'armv7-unknown-linux-gnueabihf'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: -p swap --target ${{ matrix.target }}
|
||||
|
||||
- name: Build binary (armv7)
|
||||
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: -p swap --target ${{ matrix.target }}
|
||||
use-cross: true
|
||||
|
||||
- name: Upload swap binary
|
||||
uses: actions/upload-artifact@v2-preview
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: swap-${{ matrix.target }}
|
||||
path: target/${{ matrix.target }}/debug/swap
|
||||
|
||||
- name: Upload asb binary
|
||||
uses: actions/upload-artifact@v2-preview
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: asb-${{ matrix.target }}
|
||||
path: target/${{ matrix.target }}/debug/asb
|
||||
@ -76,7 +118,7 @@ jobs:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3.1.0
|
||||
|
||||
- uses: Swatinem/rust-cache@v2.0.2
|
||||
- uses: Swatinem/rust-cache@v2.2.0
|
||||
|
||||
- name: Build tests
|
||||
run: cargo build --tests --workspace --all-features
|
||||
@ -112,7 +154,7 @@ jobs:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3.1.0
|
||||
|
||||
- uses: Swatinem/rust-cache@v2.0.2
|
||||
- uses: Swatinem/rust-cache@v2.2.0
|
||||
|
||||
- name: Run test ${{ matrix.test_name }}
|
||||
run: cargo test --package swap --all-features --test ${{ matrix.test_name }} -- --nocapture
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -160,3 +160,4 @@ flycheck_*.el
|
||||
|
||||
*.log
|
||||
.vscode
|
||||
swap/tempdb
|
||||
|
672
Cargo.lock
generated
672
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
||||
status = [
|
||||
"static_analysis",
|
||||
"bdk_test",
|
||||
"sqlx_test",
|
||||
"build (x86_64-unknown-linux-gnu, ubuntu-latest)",
|
||||
"build (armv7-unknown-linux-gnueabihf, ubuntu-latest)",
|
||||
"build (x86_64-apple-darwin, macos-latest)",
|
||||
|
@ -15,9 +15,9 @@ async-trait = "0.1"
|
||||
atty = "0.2"
|
||||
backoff = { version = "0.4", features = [ "tokio" ] }
|
||||
base64 = "0.13"
|
||||
bdk = "0.23"
|
||||
bdk = "0.24"
|
||||
big-bytes = "1"
|
||||
bitcoin = { version = "0.28", features = [ "rand", "use-serde" ] }
|
||||
bitcoin = { version = "0.29", features = [ "rand", "serde" ] }
|
||||
bmrng = "0.5"
|
||||
comfy-table = "6.1"
|
||||
config = { version = "0.11", default-features = false, features = [ "toml" ] }
|
||||
@ -48,7 +48,7 @@ serde_json = "1"
|
||||
serde_with = { version = "1", features = [ "macros" ] }
|
||||
sha2 = "0.10"
|
||||
sigma_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "ed25519", "serde", "secp256k1", "alloc" ] }
|
||||
sqlx = { version = "0.5", features = [ "sqlite", "runtime-tokio-rustls", "offline" ] }
|
||||
sqlx = { version = "0.6", features = [ "sqlite", "runtime-tokio-rustls", "offline" ] }
|
||||
structopt = "0.3"
|
||||
strum = { version = "0.24", features = [ "derive" ] }
|
||||
thiserror = "1"
|
||||
@ -74,7 +74,7 @@ tokio-tar = "0.3"
|
||||
zip = "0.5"
|
||||
|
||||
[dev-dependencies]
|
||||
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs", rev = "bff9a64b5cd75dec2ce807cbfade7b98c2e1e0d4" } # waiting on crates.io release
|
||||
bitcoin-harness = "0.2.2"
|
||||
get-port = "3"
|
||||
hyper = "0.14"
|
||||
monero-harness = { path = "../monero-harness" }
|
||||
|
16
swap/sqlite_dev_setup.sh
Normal file → Executable file
16
swap/sqlite_dev_setup.sh
Normal file → Executable file
@ -1,6 +1,14 @@
|
||||
# crated temporary DB
|
||||
# run the migration scripts to create the tables
|
||||
# prepare the sqlx-data.json rust mappings
|
||||
#!/bin/bash
|
||||
|
||||
# run this script from the swap dir
|
||||
# make sure you have sqlx-cli installed: cargo install sqlx-cli
|
||||
|
||||
# this script creates a temporary sqlite database
|
||||
# then runs the migration scripts to create the tables (migrations folder)
|
||||
# then it prepares the offline sqlx-data.json rust mappings
|
||||
DATABASE_URL=sqlite:tempdb cargo sqlx database create
|
||||
DATABASE_URL=sqlite:tempdb cargo sqlx migrate run
|
||||
DATABASE_URL=sqlite:./swap/tempdb cargo sqlx prepare -- --bin swap
|
||||
# needs the absolute path here
|
||||
# https://github.com/launchbadge/sqlx/issues/1399
|
||||
DB_PATH=$(readlink -f tempdb)
|
||||
DATABASE_URL="sqlite:$DB_PATH" cargo sqlx prepare -- --bin swap
|
||||
|
@ -1,7 +1,6 @@
|
||||
{
|
||||
"db": "SQLite",
|
||||
"081c729a0f1ad6e4ff3e13d6702c946bc4d37d50f40670b4f51d2efcce595aa6": {
|
||||
"query": "\n SELECT peer_id\n FROM peers\n WHERE swap_id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@ -10,26 +9,26 @@
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT peer_id\n FROM peers\n WHERE swap_id = ?\n "
|
||||
},
|
||||
"0ab84c094964968e96a3f2bf590d9ae92227d057386921e0e57165b887de3c75": {
|
||||
"query": "\n insert into peer_addresses (\n peer_id,\n address\n ) values (?, ?);\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"query": "\n insert into peer_addresses (\n peer_id,\n address\n ) values (?, ?);\n "
|
||||
},
|
||||
"1ec38c85e7679b2eb42b3df75d9098772ce44fdb8db3012d3c2410d828b74157": {
|
||||
"query": "\n SELECT swap_id, state\n FROM (\n SELECT max(id), swap_id, state\n FROM swap_states\n GROUP BY swap_id\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@ -43,37 +42,37 @@
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT swap_id, state\n FROM (\n SELECT max(id), swap_id, state\n FROM swap_states\n GROUP BY swap_id\n )\n "
|
||||
},
|
||||
"2a356078a41b321234adf2aa385b501749f907f7c422945a8bdda2b6274f5225": {
|
||||
"query": "\n insert into peers (\n swap_id,\n peer_id\n ) values (?, ?);\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"query": "\n insert into peers (\n swap_id,\n peer_id\n ) values (?, ?);\n "
|
||||
},
|
||||
"50a5764546f69c118fa0b64120da50f51073d36257d49768de99ff863e3511e0": {
|
||||
"query": "\n insert into monero_addresses (\n swap_id,\n address\n ) values (?, ?);\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"query": "\n insert into monero_addresses (\n swap_id,\n address\n ) values (?, ?);\n "
|
||||
},
|
||||
"88f761a4f7a0429cad1df0b1bebb1c0a27b2a45656549b23076d7542cfa21ecf": {
|
||||
"query": "\n SELECT state\n FROM swap_states\n WHERE swap_id = ?\n ORDER BY id desc\n LIMIT 1;\n\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@ -82,16 +81,16 @@
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT state\n FROM swap_states\n WHERE swap_id = ?\n ORDER BY id desc\n LIMIT 1;\n\n "
|
||||
},
|
||||
"a0eb85d04ee3842c52291dad4d225941d1141af735922fcbc665868997fce304": {
|
||||
"query": "\n SELECT address\n FROM peer_addresses\n WHERE peer_id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@ -100,26 +99,26 @@
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT address\n FROM peer_addresses\n WHERE peer_id = ?\n "
|
||||
},
|
||||
"b703032b4ddc627a1124817477e7a8e5014bdc694c36a14053ef3bb2fc0c69b0": {
|
||||
"query": "\n insert into swap_states (\n swap_id,\n entered_at,\n state\n ) values (?, ?, ?);\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"query": "\n insert into swap_states (\n swap_id,\n entered_at,\n state\n ) values (?, ?, ?);\n "
|
||||
},
|
||||
"ce270dd4a4b9615695a79864240c5401e2122077365e5e5a19408c068c7f9454": {
|
||||
"query": "\n SELECT address\n FROM monero_addresses\n WHERE swap_id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@ -128,12 +127,13 @@
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT address\n FROM monero_addresses\n WHERE swap_id = ?\n "
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ impl Rate {
|
||||
///
|
||||
/// This applies the spread to the market asking price.
|
||||
pub fn ask(&self) -> Result<bitcoin::Amount> {
|
||||
let sats = self.ask.as_sat();
|
||||
let sats = self.ask.to_sat();
|
||||
let sats = Decimal::from(sats);
|
||||
|
||||
let additional_sats = sats * self.ask_spread;
|
||||
@ -51,13 +51,13 @@ impl Rate {
|
||||
// quote (btc) = rate * base (xmr)
|
||||
// base = quote / rate
|
||||
|
||||
let quote_in_sats = quote.as_sat();
|
||||
let quote_in_sats = quote.to_sat();
|
||||
let quote_in_btc = Decimal::from(quote_in_sats)
|
||||
.checked_div(Decimal::from(bitcoin::Amount::ONE_BTC.as_sat()))
|
||||
.checked_div(Decimal::from(bitcoin::Amount::ONE_BTC.to_sat()))
|
||||
.context("Division overflow")?;
|
||||
|
||||
let rate_in_btc = Decimal::from(rate.as_sat())
|
||||
.checked_div(Decimal::from(bitcoin::Amount::ONE_BTC.as_sat()))
|
||||
let rate_in_btc = Decimal::from(rate.to_sat())
|
||||
.checked_div(Decimal::from(bitcoin::Amount::ONE_BTC.to_sat()))
|
||||
.context("Division overflow")?;
|
||||
|
||||
let base_in_xmr = quote_in_btc
|
||||
@ -105,7 +105,7 @@ mod tests {
|
||||
|
||||
let amount = rate.ask().unwrap();
|
||||
|
||||
assert_eq!(amount.as_sat(), 102);
|
||||
assert_eq!(amount.to_sat(), 102);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -320,11 +320,10 @@ async fn init_bitcoin_wallet(
|
||||
env_config: swap::env::Config,
|
||||
) -> Result<bitcoin::Wallet> {
|
||||
tracing::debug!("Opening Bitcoin wallet");
|
||||
let wallet_dir = config.data.dir.join("wallet");
|
||||
|
||||
let data_dir = &config.data.dir;
|
||||
let wallet = bitcoin::Wallet::new(
|
||||
config.bitcoin.electrum_rpc_url.clone(),
|
||||
&wallet_dir,
|
||||
data_dir,
|
||||
seed.derive_extended_private_key(env_config.bitcoin_network)?,
|
||||
env_config,
|
||||
config.bitcoin.target_block,
|
||||
|
@ -523,12 +523,12 @@ async fn init_bitcoin_wallet(
|
||||
env_config: Config,
|
||||
bitcoin_target_block: usize,
|
||||
) -> Result<bitcoin::Wallet> {
|
||||
let wallet_dir = data_dir.join("wallet");
|
||||
let xprivkey = seed.derive_extended_private_key(env_config.bitcoin_network)?;
|
||||
|
||||
let wallet = bitcoin::Wallet::new(
|
||||
electrum_rpc_url.clone(),
|
||||
&wallet_dir,
|
||||
seed.derive_extended_private_key(env_config.bitcoin_network)?,
|
||||
data_dir,
|
||||
xprivkey,
|
||||
env_config,
|
||||
bitcoin_target_block,
|
||||
)
|
||||
|
@ -373,8 +373,8 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn calculate_transaction_weights() {
|
||||
let alice_wallet = WalletBuilder::new(Amount::ONE_BTC.as_sat()).build();
|
||||
let bob_wallet = WalletBuilder::new(Amount::ONE_BTC.as_sat()).build();
|
||||
let alice_wallet = WalletBuilder::new(Amount::ONE_BTC.to_sat()).build();
|
||||
let bob_wallet = WalletBuilder::new(Amount::ONE_BTC.to_sat()).build();
|
||||
let spending_fee = Amount::from_sat(1_000);
|
||||
let btc_amount = Amount::from_sat(500_000);
|
||||
let xmr_amount = crate::monero::Amount::from_piconero(10000);
|
||||
|
@ -4,9 +4,11 @@ use crate::bitcoin::{
|
||||
build_shared_output_descriptor, Address, Amount, BlockHeight, PublicKey, Transaction, TxLock,
|
||||
};
|
||||
use ::bitcoin::util::sighash::SighashCache;
|
||||
use ::bitcoin::{EcdsaSighashType, OutPoint, Script, Sighash, TxIn, TxOut, Txid};
|
||||
use ::bitcoin::{
|
||||
EcdsaSighashType, OutPoint, PackedLockTime, Script, Sequence, Sighash, TxIn, TxOut, Txid,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use bdk::miniscript::{Descriptor, DescriptorTrait};
|
||||
use bdk::miniscript::Descriptor;
|
||||
use ecdsa_fun::Signature;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
@ -109,18 +111,18 @@ impl TxCancel {
|
||||
let tx_in = TxIn {
|
||||
previous_output: tx_lock.as_outpoint(),
|
||||
script_sig: Default::default(),
|
||||
sequence: cancel_timelock.0,
|
||||
sequence: Sequence(cancel_timelock.0),
|
||||
witness: Default::default(),
|
||||
};
|
||||
|
||||
let tx_out = TxOut {
|
||||
value: tx_lock.lock_amount().as_sat() - spending_fee.as_sat(),
|
||||
value: tx_lock.lock_amount().to_sat() - spending_fee.to_sat(),
|
||||
script_pubkey: cancel_output_descriptor.script_pubkey(),
|
||||
};
|
||||
|
||||
let transaction = Transaction {
|
||||
version: 2,
|
||||
lock_time: 0,
|
||||
lock_time: PackedLockTime(0),
|
||||
input: vec![tx_in],
|
||||
output: vec![tx_out],
|
||||
};
|
||||
@ -129,7 +131,7 @@ impl TxCancel {
|
||||
.segwit_signature_hash(
|
||||
0, // Only one input: lock_input (lock transaction)
|
||||
&tx_lock.output_descriptor.script_code().expect("scriptcode"),
|
||||
tx_lock.lock_amount().as_sat(),
|
||||
tx_lock.lock_amount().to_sat(),
|
||||
EcdsaSighashType::All,
|
||||
)
|
||||
.expect("sighash");
|
||||
@ -235,21 +237,22 @@ impl TxCancel {
|
||||
) -> Transaction {
|
||||
let previous_output = self.as_outpoint();
|
||||
|
||||
let sequence = Sequence(sequence.map(|seq| seq.0).unwrap_or(0xFFFF_FFFF));
|
||||
let tx_in = TxIn {
|
||||
previous_output,
|
||||
script_sig: Default::default(),
|
||||
sequence: sequence.map(|seq| seq.0).unwrap_or(0xFFFF_FFFF),
|
||||
sequence,
|
||||
witness: Default::default(),
|
||||
};
|
||||
|
||||
let tx_out = TxOut {
|
||||
value: self.amount().as_sat() - spending_fee.as_sat(),
|
||||
value: self.amount().to_sat() - spending_fee.to_sat(),
|
||||
script_pubkey: spend_address.script_pubkey(),
|
||||
};
|
||||
|
||||
Transaction {
|
||||
version: 2,
|
||||
lock_time: 0,
|
||||
lock_time: PackedLockTime(0),
|
||||
input: vec![tx_in],
|
||||
output: vec![tx_out],
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ use ::bitcoin::util::psbt::PartiallySignedTransaction;
|
||||
use ::bitcoin::{OutPoint, TxIn, TxOut, Txid};
|
||||
use anyhow::{bail, Result};
|
||||
use bdk::database::BatchDatabase;
|
||||
use bdk::miniscript::{Descriptor, DescriptorTrait};
|
||||
use bitcoin::Script;
|
||||
use bdk::miniscript::Descriptor;
|
||||
use bitcoin::{PackedLockTime, Script, Sequence};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const SCRIPT_SIZE: usize = 34;
|
||||
@ -58,14 +58,14 @@ impl TxLock {
|
||||
btc: Amount,
|
||||
) -> Result<Self> {
|
||||
let shared_output_candidate = match psbt.unsigned_tx.output.as_slice() {
|
||||
[shared_output_candidate, _] if shared_output_candidate.value == btc.as_sat() => {
|
||||
[shared_output_candidate, _] if shared_output_candidate.value == btc.to_sat() => {
|
||||
shared_output_candidate
|
||||
}
|
||||
[_, shared_output_candidate] if shared_output_candidate.value == btc.as_sat() => {
|
||||
[_, shared_output_candidate] if shared_output_candidate.value == btc.to_sat() => {
|
||||
shared_output_candidate
|
||||
}
|
||||
// A single output is possible if Bob funds without any change necessary
|
||||
[shared_output_candidate] if shared_output_candidate.value == btc.as_sat() => {
|
||||
[shared_output_candidate] if shared_output_candidate.value == btc.to_sat() => {
|
||||
shared_output_candidate
|
||||
}
|
||||
[_, _] => {
|
||||
@ -140,14 +140,15 @@ impl TxLock {
|
||||
) -> Transaction {
|
||||
let previous_output = self.as_outpoint();
|
||||
|
||||
let sequence = Sequence(sequence.unwrap_or(0xFFFF_FFFF));
|
||||
let tx_in = TxIn {
|
||||
previous_output,
|
||||
script_sig: Default::default(),
|
||||
sequence: sequence.unwrap_or(0xFFFF_FFFF),
|
||||
sequence,
|
||||
witness: Default::default(),
|
||||
};
|
||||
|
||||
let fee = spending_fee.as_sat();
|
||||
let fee = spending_fee.to_sat();
|
||||
let tx_out = TxOut {
|
||||
value: self.inner.clone().extract_tx().output[self.lock_output_vout()].value - fee,
|
||||
script_pubkey: spend_address.script_pubkey(),
|
||||
@ -157,7 +158,7 @@ impl TxLock {
|
||||
|
||||
Transaction {
|
||||
version: 2,
|
||||
lock_time: 0,
|
||||
lock_time: PackedLockTime(0),
|
||||
input: vec![tx_in],
|
||||
output: vec![tx_out],
|
||||
}
|
||||
@ -207,7 +208,7 @@ mod tests {
|
||||
let (A, B) = alice_and_bob();
|
||||
let fees = 300;
|
||||
let agreed_amount = Amount::from_sat(10000);
|
||||
let amount = agreed_amount.as_sat() + fees;
|
||||
let amount = agreed_amount.to_sat() + fees;
|
||||
let wallet = WalletBuilder::new(amount).build();
|
||||
|
||||
let psbt = bob_make_psbt(A, B, &wallet, agreed_amount).await;
|
||||
|
@ -4,7 +4,7 @@ use ::bitcoin::util::sighash::SighashCache;
|
||||
use ::bitcoin::{EcdsaSighashType, Sighash};
|
||||
use anyhow::{Context, Result};
|
||||
use bdk::bitcoin::Script;
|
||||
use bdk::miniscript::{Descriptor, DescriptorTrait};
|
||||
use bdk::miniscript::Descriptor;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -32,7 +32,7 @@ impl TxPunish {
|
||||
.output_descriptor
|
||||
.script_code()
|
||||
.expect("scriptcode"),
|
||||
tx_cancel.amount().as_sat(),
|
||||
tx_cancel.amount().to_sat(),
|
||||
EcdsaSighashType::All,
|
||||
)
|
||||
.expect("sighash");
|
||||
|
@ -5,7 +5,7 @@ use crate::bitcoin::{
|
||||
};
|
||||
use ::bitcoin::{Sighash, Txid};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use bdk::miniscript::{Descriptor, DescriptorTrait};
|
||||
use bdk::miniscript::Descriptor;
|
||||
use bitcoin::secp256k1::ecdsa;
|
||||
use bitcoin::util::sighash::SighashCache;
|
||||
use bitcoin::{EcdsaSighashType, Script};
|
||||
@ -34,7 +34,7 @@ impl TxRedeem {
|
||||
.segwit_signature_hash(
|
||||
0, // Only one input: lock_input (lock transaction)
|
||||
&tx_lock.output_descriptor.script_code().expect("scriptcode"),
|
||||
tx_lock.lock_amount().as_sat(),
|
||||
tx_lock.lock_amount().to_sat(),
|
||||
EcdsaSighashType::All,
|
||||
)
|
||||
.expect("sighash");
|
||||
|
@ -8,7 +8,7 @@ use ::bitcoin::secp256k1::ecdsa;
|
||||
use ::bitcoin::util::sighash::SighashCache;
|
||||
use ::bitcoin::{EcdsaSighashType, Script, Sighash, Txid};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use bdk::miniscript::{Descriptor, DescriptorTrait};
|
||||
use bdk::miniscript::Descriptor;
|
||||
use ecdsa_fun::Signature;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -31,7 +31,7 @@ impl TxRefund {
|
||||
.output_descriptor
|
||||
.script_code()
|
||||
.expect("scriptcode"),
|
||||
tx_cancel.amount().as_sat(),
|
||||
tx_cancel.amount().to_sat(),
|
||||
EcdsaSighashType::All,
|
||||
)
|
||||
.expect("sighash");
|
||||
|
@ -6,12 +6,12 @@ use ::bitcoin::Txid;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use bdk::blockchain::{Blockchain, ElectrumBlockchain, GetTx};
|
||||
use bdk::database::BatchDatabase;
|
||||
use bdk::descriptor::Segwitv0;
|
||||
use bdk::electrum_client::{ElectrumApi, GetHistoryRes};
|
||||
use bdk::keys::DerivableKey;
|
||||
use bdk::sled::Tree;
|
||||
use bdk::wallet::export::FullyNodedExport;
|
||||
use bdk::wallet::AddressIndex;
|
||||
use bdk::{FeeRate, KeychainKind, SignOptions, SyncOptions};
|
||||
use bitcoin::util::bip32::ExtendedPrivKey;
|
||||
use bitcoin::{Network, Script};
|
||||
use reqwest::Url;
|
||||
use rust_decimal::prelude::*;
|
||||
@ -33,7 +33,10 @@ const MAX_RELATIVE_TX_FEE: Decimal = dec!(0.03);
|
||||
const MAX_ABSOLUTE_TX_FEE: Decimal = dec!(100_000);
|
||||
const DUST_AMOUNT: u64 = 546;
|
||||
|
||||
pub struct Wallet<D = bdk::sled::Tree, C = Client> {
|
||||
const WALLET: &str = "wallet";
|
||||
const WALLET_OLD: &str = "wallet-old";
|
||||
|
||||
pub struct Wallet<D = Tree, C = Client> {
|
||||
client: Arc<Mutex<C>>,
|
||||
wallet: Arc<Mutex<bdk::Wallet<D>>>,
|
||||
finality_confirmations: u32,
|
||||
@ -44,19 +47,28 @@ pub struct Wallet<D = bdk::sled::Tree, C = Client> {
|
||||
impl Wallet {
|
||||
pub async fn new(
|
||||
electrum_rpc_url: Url,
|
||||
wallet_dir: &Path,
|
||||
key: impl DerivableKey<Segwitv0> + Clone,
|
||||
data_dir: impl AsRef<Path>,
|
||||
xprivkey: ExtendedPrivKey,
|
||||
env_config: env::Config,
|
||||
target_block: usize,
|
||||
) -> Result<Self> {
|
||||
let db = bdk::sled::open(wallet_dir)?.open_tree(SLED_TREE_NAME)?;
|
||||
let data_dir = data_dir.as_ref();
|
||||
let wallet_dir = data_dir.join(WALLET);
|
||||
let database = bdk::sled::open(&wallet_dir)?.open_tree(SLED_TREE_NAME)?;
|
||||
let network = env_config.bitcoin_network;
|
||||
|
||||
let wallet = bdk::Wallet::new(
|
||||
bdk::template::Bip84(key.clone(), KeychainKind::External),
|
||||
Some(bdk::template::Bip84(key, KeychainKind::Internal)),
|
||||
env_config.bitcoin_network,
|
||||
db,
|
||||
)?;
|
||||
let wallet = match bdk::Wallet::new(
|
||||
bdk::template::Bip84(xprivkey, KeychainKind::External),
|
||||
Some(bdk::template::Bip84(xprivkey, KeychainKind::Internal)),
|
||||
network,
|
||||
database,
|
||||
) {
|
||||
Ok(w) => w,
|
||||
Err(e) if matches!(e, bdk::Error::ChecksumMismatch) => {
|
||||
Self::migrate(data_dir, xprivkey, network)?
|
||||
}
|
||||
err => err?,
|
||||
};
|
||||
|
||||
let client = Client::new(electrum_rpc_url, env_config.bitcoin_sync_interval())?;
|
||||
|
||||
@ -71,6 +83,32 @@ impl Wallet {
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new database for the wallet and rename the old one.
|
||||
/// This is necessary when getting a ChecksumMismatch from a wallet
|
||||
/// created with an older version of BDK. Only affected Testnet wallets.
|
||||
// https://github.com/comit-network/xmr-btc-swap/issues/1182
|
||||
fn migrate(
|
||||
data_dir: &Path,
|
||||
xprivkey: ExtendedPrivKey,
|
||||
network: bitcoin::Network,
|
||||
) -> Result<bdk::Wallet<Tree>> {
|
||||
let from = data_dir.join(WALLET);
|
||||
let to = data_dir.join(WALLET_OLD);
|
||||
std::fs::rename(from, to)?;
|
||||
|
||||
let wallet_dir = data_dir.join(WALLET);
|
||||
let database = bdk::sled::open(&wallet_dir)?.open_tree(SLED_TREE_NAME)?;
|
||||
|
||||
let wallet = bdk::Wallet::new(
|
||||
bdk::template::Bip84(xprivkey, KeychainKind::External),
|
||||
Some(bdk::template::Bip84(xprivkey, KeychainKind::Internal)),
|
||||
network,
|
||||
database,
|
||||
)?;
|
||||
|
||||
Ok(wallet)
|
||||
}
|
||||
|
||||
/// Broadcast the given transaction to the network and emit a log statement
|
||||
/// if done so successfully.
|
||||
///
|
||||
@ -346,7 +384,7 @@ where
|
||||
let script = address.script_pubkey();
|
||||
|
||||
let mut tx_builder = wallet.build_tx();
|
||||
tx_builder.add_recipient(script.clone(), amount.as_sat());
|
||||
tx_builder.add_recipient(script.clone(), amount.to_sat());
|
||||
tx_builder.fee_rate(fee_rate);
|
||||
let (psbt, _details) = tx_builder.finish()?;
|
||||
let mut psbt: PartiallySignedTransaction = psbt;
|
||||
@ -392,7 +430,7 @@ where
|
||||
return Ok(Amount::ZERO);
|
||||
}
|
||||
let client = self.client.lock().await;
|
||||
let min_relay_fee = client.min_relay_fee()?.as_sat();
|
||||
let min_relay_fee = client.min_relay_fee()?.to_sat();
|
||||
|
||||
if balance.get_total() < min_relay_fee {
|
||||
return Ok(Amount::ZERO);
|
||||
@ -443,18 +481,18 @@ fn estimate_fee(
|
||||
fee_rate: FeeRate,
|
||||
min_relay_fee: Amount,
|
||||
) -> Result<Amount> {
|
||||
if transfer_amount.as_sat() <= 546 {
|
||||
if transfer_amount.to_sat() <= 546 {
|
||||
bail!("Amounts needs to be greater than Bitcoin dust amount.")
|
||||
}
|
||||
let fee_rate_svb = fee_rate.as_sat_per_vb();
|
||||
if fee_rate_svb <= 0.0 {
|
||||
bail!("Fee rate needs to be > 0")
|
||||
}
|
||||
if fee_rate_svb > 100_000_000.0 || min_relay_fee.as_sat() > 100_000_000 {
|
||||
if fee_rate_svb > 100_000_000.0 || min_relay_fee.to_sat() > 100_000_000 {
|
||||
bail!("A fee_rate or min_relay_fee of > 1BTC does not make sense")
|
||||
}
|
||||
|
||||
let min_relay_fee = if min_relay_fee.as_sat() == 0 {
|
||||
let min_relay_fee = if min_relay_fee.to_sat() == 0 {
|
||||
// if min_relay_fee is 0 we don't fail, we just set it to 1 satoshi;
|
||||
Amount::ONE_SAT
|
||||
} else {
|
||||
@ -474,9 +512,9 @@ fn estimate_fee(
|
||||
"Estimated fee for transaction",
|
||||
);
|
||||
|
||||
let transfer_amount = Decimal::from(transfer_amount.as_sat());
|
||||
let transfer_amount = Decimal::from(transfer_amount.to_sat());
|
||||
let max_allowed_fee = transfer_amount * MAX_RELATIVE_TX_FEE;
|
||||
let min_relay_fee = Decimal::from(min_relay_fee.as_sat());
|
||||
let min_relay_fee = Decimal::from(min_relay_fee.to_sat());
|
||||
|
||||
let recommended_fee = if sats_per_vbyte < min_relay_fee {
|
||||
tracing::warn!(
|
||||
@ -932,6 +970,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::bitcoin::{PublicKey, TxLock};
|
||||
use crate::tracing_ext::capture_logs;
|
||||
use bitcoin::hashes::Hash;
|
||||
use proptest::prelude::*;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
|
||||
@ -1031,7 +1070,7 @@ mod tests {
|
||||
|
||||
// weight / 4.0 * sat_per_vb would be greater than 3% hence we take total
|
||||
// max allowed fee.
|
||||
assert_eq!(is_fee.as_sat(), MAX_ABSOLUTE_TX_FEE.to_u64().unwrap());
|
||||
assert_eq!(is_fee.to_sat(), MAX_ABSOLUTE_TX_FEE.to_u64().unwrap());
|
||||
}
|
||||
|
||||
proptest! {
|
||||
@ -1067,7 +1106,7 @@ mod tests {
|
||||
let is_fee = estimate_fee(weight, amount, fee_rate, relay_fee).unwrap();
|
||||
|
||||
// weight / 4 * 1_000 is always lower than MAX_ABSOLUTE_TX_FEE
|
||||
assert!(is_fee.as_sat() < MAX_ABSOLUTE_TX_FEE.to_u64().unwrap());
|
||||
assert!(is_fee.to_sat() < MAX_ABSOLUTE_TX_FEE.to_u64().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1086,7 +1125,7 @@ mod tests {
|
||||
let is_fee = estimate_fee(weight, amount, fee_rate, relay_fee).unwrap();
|
||||
|
||||
// weight / 4 * 1_000 is always higher than MAX_ABSOLUTE_TX_FEE
|
||||
assert!(is_fee.as_sat() >= MAX_ABSOLUTE_TX_FEE.to_u64().unwrap());
|
||||
assert!(is_fee.to_sat() >= MAX_ABSOLUTE_TX_FEE.to_u64().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1142,7 +1181,7 @@ mod tests {
|
||||
let wallet = WalletBuilder::new(10_000).build();
|
||||
let amount = wallet.max_giveable(TxLock::script_size()).await.unwrap();
|
||||
|
||||
assert!(amount.as_sat() > 0);
|
||||
assert!(amount.to_sat() > 0);
|
||||
}
|
||||
|
||||
/// This test ensures that the relevant script output of the transaction
|
||||
@ -1212,7 +1251,8 @@ mod tests {
|
||||
fn printing_status_change_doesnt_spam_on_same_status() {
|
||||
let writer = capture_logs(LevelFilter::DEBUG);
|
||||
|
||||
let tx = Txid::default();
|
||||
let inner = bitcoin::hashes::sha256d::Hash::all_zeros();
|
||||
let tx = Txid::from_hash(inner);
|
||||
let mut old = None;
|
||||
old = Some(print_status_change(tx, old, ScriptStatus::Unseen));
|
||||
old = Some(print_status_change(tx, old, ScriptStatus::InMempool));
|
||||
|
@ -338,6 +338,7 @@ struct RawArguments {
|
||||
testnet: bool,
|
||||
|
||||
#[structopt(
|
||||
short,
|
||||
long = "--data-base-dir",
|
||||
help = "The base data directory to be used for mainnet / testnet specific data like database, wallets etc"
|
||||
)]
|
||||
|
@ -9,19 +9,18 @@ pub enum Version {
|
||||
}
|
||||
|
||||
/// Check the latest release from GitHub API.
|
||||
pub async fn check_latest_version(current: &str) -> anyhow::Result<Version> {
|
||||
pub async fn check_latest_version(current_version: &str) -> anyhow::Result<Version> {
|
||||
let response = reqwest::get(LATEST_RELEASE_URL).await?;
|
||||
let e = "Failed to get latest release.";
|
||||
let url = response.url();
|
||||
let segments = url.path_segments().ok_or_else(|| anyhow!(e))?;
|
||||
let latest = segments.last().ok_or_else(|| anyhow!(e))?;
|
||||
let download_url = response.url();
|
||||
let segments = download_url.path_segments().ok_or_else(|| anyhow!(e))?;
|
||||
let latest_version = segments.last().ok_or_else(|| anyhow!(e))?;
|
||||
|
||||
let result = if is_latest_version(current, latest) {
|
||||
let result = if is_latest_version(current_version, latest_version) {
|
||||
Version::Current
|
||||
} else {
|
||||
println!(
|
||||
"You are not on the latest version: {} is available. \n{}",
|
||||
latest, url
|
||||
tracing::warn!(%current_version, %latest_version, %download_url,
|
||||
"You are not on the latest version",
|
||||
);
|
||||
Version::Available
|
||||
};
|
||||
|
@ -110,7 +110,7 @@ impl Amount {
|
||||
}
|
||||
|
||||
// safely convert the BTC/XMR rate to sat/pico
|
||||
let ask_sats = Decimal::from(ask_price.as_sat());
|
||||
let ask_sats = Decimal::from(ask_price.to_sat());
|
||||
let pico_per_xmr = Decimal::from(PICONERO_OFFSET);
|
||||
let ask_sats_per_pico = ask_sats / pico_per_xmr;
|
||||
|
||||
|
@ -20,8 +20,8 @@ compile_error!("unsupported operating system");
|
||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-x64-v0.18.1.2.tar.bz2";
|
||||
|
||||
#[cfg(all(target_os = "macos", target_arch = "arm"))]
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-armv8-v0.18.1.2.tar.bz2";
|
||||
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-mac-armv8-v0.18.0.0.tar.bz2";
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-linux-x64-v0.18.1.2.tar.bz2";
|
||||
|
27
swap/tests/bdk.sh
Executable file
27
swap/tests/bdk.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
VERSION=0.11.1
|
||||
|
||||
mkdir bdk
|
||||
stat ./target/debug/swap || exit 1
|
||||
cp ./target/debug/swap bdk/swap-current
|
||||
pushd bdk
|
||||
|
||||
echo "download swap $VERSION"
|
||||
curl -L "https://github.com/comit-network/xmr-btc-swap/releases/download/${VERSION}/swap_${VERSION}_Linux_x86_64.tar" | tar xv
|
||||
|
||||
echo "create testnet wallet with $VERSION"
|
||||
./swap --testnet --data-base-dir . --debug balance || exit 1
|
||||
echo "check testnet wallet with this version"
|
||||
./swap-current --testnet --data-base-dir . --debug balance || exit 1
|
||||
|
||||
echo "create mainnet wallet with $VERSION"
|
||||
./swap --version || exit 1
|
||||
./swap --data-base-dir . --debug balance || exit 1
|
||||
echo "check mainnet wallet with this version"
|
||||
./swap-current --version || exit 1
|
||||
./swap-current --data-base-dir . --debug balance || exit 1
|
||||
|
||||
exit 0
|
@ -56,7 +56,7 @@ where
|
||||
monero.init_miner().await.unwrap();
|
||||
|
||||
let btc_amount = bitcoin::Amount::from_sat(1_000_000);
|
||||
let xmr_amount = monero::Amount::from_monero(btc_amount.as_btc() / FixedRate::RATE).unwrap();
|
||||
let xmr_amount = monero::Amount::from_monero(btc_amount.to_btc() / FixedRate::RATE).unwrap();
|
||||
|
||||
let alice_starting_balances =
|
||||
StartingBalances::new(bitcoin::Amount::ZERO, xmr_amount, Some(10));
|
||||
|
Loading…
Reference in New Issue
Block a user