From 359605d4fc4b34d7137fd16127f0da591461c10e Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Fri, 30 Jul 2021 14:33:40 +1000 Subject: [PATCH 1/7] Loan due date using current block height --- extension/src/components/OpenLoans.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extension/src/components/OpenLoans.tsx b/extension/src/components/OpenLoans.tsx index 5a3aeea5..fbc14d49 100644 --- a/extension/src/components/OpenLoans.tsx +++ b/extension/src/components/OpenLoans.tsx @@ -20,7 +20,7 @@ import { LoanDetails } from "../models"; import Btc from "./bitcoin.svg"; import Usdt from "./tether.svg"; -const error = Debug("openloans:error"); +const debug = Debug("openloans:error"); interface OpenLoansProps { openLoans: LoanDetails[] | undefined; @@ -53,7 +53,7 @@ function OpenLoan({ loanDetails, onRepayed, index }: OpenLoanProps) { await repayLoan(loanDetails.txid); onRepayed(); }, - onReject: (e) => error("Failed to repay loan %s: %s", loanDetails.txid, e), + onReject: (e) => debug("Failed to repay loan %s: %s", loanDetails.txid, e), }); const blockHeightHook = useAsync({ From 06701f852298e28bbba69d5d83ed44e36301775a Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 2 Aug 2021 12:03:47 +1000 Subject: [PATCH 2/7] Upgrade to baru 0.2 --- Cargo.lock | 72 ++++++++++++++++--- bobtimus/Cargo.toml | 4 +- bobtimus/src/elements_rpc.rs | 14 ++-- bobtimus/src/lib.rs | 31 +++++--- coin_selection/Cargo.toml | 2 +- coin_selection/src/lib.rs | 15 ++-- extension/wallet/Cargo.toml | 7 +- extension/wallet/src/lib.rs | 2 +- extension/wallet/src/wallet.rs | 8 +-- extension/wallet/src/wallet/extract_loan.rs | 10 +-- .../src/wallet/make_create_swap_payload.rs | 3 +- .../wallet/src/wallet/make_loan_request.rs | 2 +- 12 files changed, 117 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2b76d50..27464184 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,17 +123,20 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "baru" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a925d16d7755d1f745724754b5812644fc6bcf9f2936de18c9710d707c2f87" +checksum = "6a23513ccaf1216e056a58cbf696e14c21c4b0236da951747c9ede655e7c502a" dependencies = [ "anyhow", "bitcoin_hashes 0.9.6", + "conquer-once", "elements", + "elements-miniscript", "env_logger", "hex", "hmac 0.10.1", "log", + "rand 0.6.5", "rust_decimal", "secp256k1", "secp256k1-zkp", @@ -156,10 +159,10 @@ checksum = "daeccaea73c9fc27e218e2a4402070707fb8354afd30fecd4a1c9a0bea8b79c4" dependencies = [ "async-trait", "bdk-macros", - "bitcoin", + "bitcoin 0.26.0", "js-sys", "log", - "miniscript", + "miniscript 5.1.0", "rand 0.7.3", "serde 1.0.126", "serde_json 1.0.64", @@ -183,6 +186,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" +[[package]] +name = "bech32" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" + [[package]] name = "bip32" version = "0.2.1" @@ -205,12 +214,24 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec5f88a446d66e7474a3b8fa2e348320b574463fb78d799d90ba68f79f48e0e" dependencies = [ - "bech32", + "bech32 0.7.3", "bitcoin_hashes 0.9.6", "secp256k1", "serde 1.0.126", ] +[[package]] +name = "bitcoin" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a427b27dae305157520d86673f2393b3eb08d880609abfcffc6e3c3c820e764" +dependencies = [ + "bech32 0.8.1", + "bitcoin_hashes 0.10.0", + "secp256k1", + "serde 1.0.126", +] + [[package]] name = "bitcoin_hashes" version = "0.7.6" @@ -226,6 +247,15 @@ dependencies = [ "serde 1.0.126", ] +[[package]] +name = "bitcoin_hashes" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006cc91e1a1d99819bc5b8214be3555c1f0611b169f527a1fdc54ed1f2b745b0" +dependencies = [ + "serde 1.0.126", +] + [[package]] name = "bitflags" version = "1.2.1" @@ -526,12 +556,12 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "elements" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52bc8e918eb82346053fc6dcac7281066778164bced4cfa40e4b2b2e4b219c8b" +checksum = "aa776445bd0ef9b30eab2e7c09cacc3a545e0502c178fdca9c4c6d80f738d519" dependencies = [ - "bitcoin", - "bitcoin_hashes 0.9.6", + "bitcoin 0.27.0", + "bitcoin_hashes 0.10.0", "secp256k1-zkp", "serde 1.0.126", "serde_json 0.9.10", @@ -554,6 +584,18 @@ dependencies = [ "url", ] +[[package]] +name = "elements-miniscript" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc1fe1025a4d64d76bcc479685610a2d9c4903fdb1eb72c8bf4c2d1b3dbdc1a" +dependencies = [ + "bitcoin 0.27.0", + "elements", + "miniscript 6.0.0", + "serde 1.0.126", +] + [[package]] name = "encoding_rs" version = "0.8.28" @@ -1145,7 +1187,16 @@ version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71f455be59a359d50370c4f587afbc5739c862e684c5afecae80ab93e7474b4e" dependencies = [ - "bitcoin", + "bitcoin 0.26.0", +] + +[[package]] +name = "miniscript" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f88ac7d08e876153a4a81e851693db71f325b382baa77723f63210451e2414f" +dependencies = [ + "bitcoin 0.27.0", ] [[package]] @@ -2499,7 +2550,6 @@ dependencies = [ "aes-gcm-siv", "anyhow", "baru", - "bdk", "bip32", "coin_selection", "conquer-once", diff --git a/bobtimus/Cargo.toml b/bobtimus/Cargo.toml index 01af7634..46d32b2e 100644 --- a/bobtimus/Cargo.toml +++ b/bobtimus/Cargo.toml @@ -7,12 +7,12 @@ edition = "2018" [dependencies] anyhow = "1" async-trait = "0.1" -baru = "0.1" +baru = "0.2" bitcoin_hashes = "0.9.0" diesel = { version = "1.4", features = ["sqlite"] } diesel_migrations = "1.4" directories = "3.0" -elements = { version = "0.17", features = ["serde-feature"] } +elements = { version = "0.18", features = ["serde-feature"] } elements-harness = { git = "https://github.com/comit-network/elements-harness" } futures = { version = "0.3", default-features = false } hex = "0.4" diff --git a/bobtimus/src/elements_rpc.rs b/bobtimus/src/elements_rpc.rs index 18614151..220e7f1c 100644 --- a/bobtimus/src/elements_rpc.rs +++ b/bobtimus/src/elements_rpc.rs @@ -1,7 +1,7 @@ use anyhow::{bail, Context, Result}; use bitcoin_hashes::hex::FromHex; use elements::{ - bitcoin::{Amount, PrivateKey}, + bitcoin::Amount, confidential::{Asset, Nonce, Value}, encode::serialize_hex, secp256k1_zkp::{SecretKey, Signature}, @@ -338,18 +338,16 @@ impl Client { Ok(sig) } - pub async fn dump_private_key(&self, address: &Address) -> Result { - let privkey = self.dumpprivkey(address).await?; - let privkey = PrivateKey::from_wif(&privkey)?; - - Ok(privkey.key) - } - pub async fn get_blockcount(&self) -> Result { let blockcount = self.getblockcount().await?; Ok(blockcount) } + + pub async fn get_address_blinding_key(&self, address: &Address) -> Result { + let key = self.dumpblindingkey(address).await?; + Ok(key) + } } #[derive(Debug, Deserialize)] diff --git a/bobtimus/src/lib.rs b/bobtimus/src/lib.rs index 0562cc2e..599b7993 100644 --- a/bobtimus/src/lib.rs +++ b/bobtimus/src/lib.rs @@ -3,7 +3,7 @@ extern crate diesel; #[macro_use] extern crate diesel_migrations; -use std::{collections::HashMap, convert::TryInto}; +use std::collections::HashMap; use crate::{ database::{queries, Sqlite}, @@ -45,6 +45,7 @@ pub mod schema; use crate::loan::{Interest, LoanOffer}; pub use amounts::*; +use elements::bitcoin::PublicKey; use rust_decimal_macros::dec; pub const USDT_ASSET_ID: &str = "ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"; @@ -295,17 +296,31 @@ where // Currently there is no ID for incoming loan requests, that would associate them with an offer, // so we just have to ensure that the loan is still "acceptable" in the current state of Bobtimus. + let oracle_secret_key = elements::secp256k1_zkp::key::ONE_KEY; + let oralce_priv_key = elements::bitcoin::PrivateKey::new( + oracle_secret_key, + elements::bitcoin::Network::Regtest, + ); + let oracle_pk = PublicKey::from_private_key(&self.secp, &oralce_priv_key); + let lender_address = self .elementsd .get_new_segwit_confidential_address() .await .context("failed to get lender address")?; + let address_blinder = self + .elementsd + .get_address_blinding_key(&lender_address) + .await?; + let lender0 = Lender0::new( &mut self.rng, self.btc_asset_id, self.usdt_asset_id, lender_address, + address_blinder, + oracle_pk, ) .unwrap(); @@ -328,7 +343,7 @@ where let loan_response = lender1.loan_response(); self.lender_states - .insert(loan_response.transaction.txid(), lender1); + .insert(loan_response.transaction().txid(), lender1); Ok(loan_response) } @@ -360,16 +375,14 @@ where let txid = self.elementsd.send_raw_transaction(&transaction).await?; - let liquidation_tx = - lender.liquidation_transaction(&mut self.rng, &self.secp, Amount::ONE_SAT)?; - let locktime = lender - .timelock - .try_into() - .expect("TODO: locktimes should be modelled as u32"); + let liquidation_tx = lender + .liquidation_transaction(&mut self.rng, &self.secp, Amount::ONE_SAT) + .await?; + let locktime = lender.collateral_contract().timelock(); self.db .do_in_transaction(|conn| { - LiquidationForm::new(txid, &liquidation_tx, locktime).insert(conn)?; + LiquidationForm::new(txid, &liquidation_tx, *locktime).insert(conn)?; Ok(()) }) diff --git a/coin_selection/Cargo.toml b/coin_selection/Cargo.toml index 3d75252d..e6c8f9e4 100644 --- a/coin_selection/Cargo.toml +++ b/coin_selection/Cargo.toml @@ -6,6 +6,6 @@ edition = "2018" [dependencies] bdk = { version = "0.4", default-features = false } -elements = "0.17" +elements = "0.18" estimate_transaction_size = { path = "../estimate_transaction_size" } thiserror = "1" diff --git a/coin_selection/src/lib.rs b/coin_selection/src/lib.rs index 9c1df2f1..4699a5f2 100644 --- a/coin_selection/src/lib.rs +++ b/coin_selection/src/lib.rs @@ -1,11 +1,13 @@ use bdk::{ - bitcoin::{Amount, Denomination}, database::{BatchOperations, Database}, wallet::coin_selection::{ BranchAndBoundCoinSelection, CoinSelectionAlgorithm, CoinSelectionResult, }, }; -use elements::{AssetId, OutPoint, Script}; +use elements::{ + bitcoin::{Amount, Denomination}, + AssetId, OutPoint, Script, +}; use estimate_transaction_size::avg_vbytes; /// Select a subset of `utxos` to cover the `target` amount. @@ -70,7 +72,8 @@ pub fn coin_select( utxos .iter() .find(|utxo| { - bdk_utxo.outpoint.txid.as_hash() == utxo.outpoint.txid.as_hash() + format!("{}", bdk_utxo.outpoint.txid) + == format!("{}", utxo.outpoint.txid.as_hash()) && bdk_utxo.outpoint.vout == utxo.outpoint.vout }) .expect("same source of utxos") @@ -100,7 +103,7 @@ pub enum Error { #[error("All UTXOs must have the same asset ID")] HeterogeneousUtxos, #[error("Failed to parse recommended fee: {0}")] - ParseFee(#[from] bdk::bitcoin::util::amount::ParseAmountError), + ParseFee(#[from] elements::bitcoin::util::amount::ParseAmountError), #[error("Error from bdk: {0}")] Bdk(#[from] bdk::Error), } @@ -121,7 +124,9 @@ impl From for bdk::UTXO { Self { outpoint: bdk::bitcoin::OutPoint { - txid: bdk::bitcoin::Txid::from_hash(utxo.outpoint.txid.as_hash()), + txid: format!("{}", utxo.outpoint.txid) + .parse() + .expect("txid to be a txid"), vout: utxo.outpoint.vout, }, txout: bdk::bitcoin::TxOut { diff --git a/extension/wallet/Cargo.toml b/extension/wallet/Cargo.toml index 897608d4..18781c1d 100644 --- a/extension/wallet/Cargo.toml +++ b/extension/wallet/Cargo.toml @@ -13,13 +13,12 @@ default = ["console_error_panic_hook"] [dependencies] aes-gcm-siv = { version = "0.9", features = ["std"] } anyhow = "1" -baru = "0.1" -bdk = { version = "0.4", default-features = false } +baru = "0.2" bip32 = { version = "0.2", features = ["secp256k1-ffi", "bip39"], default-features = false } coin_selection = { path = "../../coin_selection" } conquer-once = "0.3" console_error_panic_hook = { version = "0.1.6", optional = true } -elements = { version = "0.17", features = ["serde-feature"] } +elements = { version = "0.18", features = ["serde-feature"] } estimate_transaction_size = { path = "../../estimate_transaction_size" } futures = "0.3" getrandom = { version = "0.2", features = ["wasm-bindgen", "js"] } @@ -48,7 +47,7 @@ wasm-bindgen-test = "0.3.13" [build-dependencies] anyhow = "1" conquer-once = "0.3" -elements = { version = "0.17" } +elements = { version = "0.18" } # By default wasm-opt is true which makes the build fail. [package.metadata.wasm-pack.profile.release] diff --git a/extension/wallet/src/lib.rs b/extension/wallet/src/lib.rs index 087e01cc..8dd71d3c 100644 --- a/extension/wallet/src/lib.rs +++ b/extension/wallet/src/lib.rs @@ -240,7 +240,7 @@ pub async fn make_loan_request( // TODO: Change the UI to handle SATs not BTC let collateral_in_btc = map_err_from_anyhow!(parse_to_bitcoin_amount(collateral))?; let fee_rate_in_sat = Amount::from_sat(map_err_from_anyhow!(u64::from_str(fee_rate.as_str()))?); - let timeout = map_err_from_anyhow!(u64::from_str(timeout.as_str()))?; + let timeout = map_err_from_anyhow!(u32::from_str(timeout.as_str()))?; let loan_request = map_err_from_anyhow!( wallet::make_loan_request( wallet_name, diff --git a/extension/wallet/src/wallet.rs b/extension/wallet/src/wallet.rs index 0f8263b0..4738f1ef 100644 --- a/extension/wallet/src/wallet.rs +++ b/extension/wallet/src/wallet.rs @@ -310,8 +310,8 @@ impl fmt::Display for ListOfWallets { pub struct CreateSwapPayload { pub alice_inputs: Vec, pub address: Address, - #[serde(with = "bdk::bitcoin::util::amount::serde::as_sat")] - pub amount: bdk::bitcoin::Amount, + #[serde(with = "elements::bitcoin::util::amount::serde::as_sat")] + pub amount: elements::bitcoin::Amount, } #[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] @@ -426,7 +426,7 @@ pub struct LoanDetails { pub principal: TradeSide, pub principal_repayment: Decimal, // TODO: Express as target date or number of days instead? - pub term: u64, + pub term: u32, pub txid: Txid, } @@ -439,7 +439,7 @@ impl LoanDetails { principal_asset: AssetId, principal_amount: Amount, principal_balance: Decimal, - timelock: u64, + timelock: u32, txid: Txid, ) -> Result { let collateral = TradeSide::new_sell( diff --git a/extension/wallet/src/wallet/extract_loan.rs b/extension/wallet/src/wallet/extract_loan.rs index 297b50ef..03a01008 100644 --- a/extension/wallet/src/wallet/extract_loan.rs +++ b/extension/wallet/src/wallet/extract_loan.rs @@ -45,10 +45,10 @@ pub async fn extract_loan( .ok_or(Error::EmptyState)?; let borrower = serde_json::from_str::(&borrower).map_err(Error::Deserialize)?; - let timelock = loan_response.timelock; let borrower = borrower .interpret(SECP256K1, loan_response) .map_err(Error::InterpretLoanResponse)?; + let timelock = borrower.collateral_contract().timelock(); let collateral_balance = balances .iter() @@ -72,15 +72,15 @@ pub async fn extract_loan( }) .unwrap_or_default(); - let loan_txid = borrower.loan_transaction.txid(); + let loan_txid = borrower.loan_transaction().txid(); let loan_details = LoanDetails::new( btc_asset_id, - borrower.collateral_amount, + borrower.collateral_amount(), collateral_balance, usdt_asset_id, - borrower.principal_tx_out_amount, + borrower.principal_amount(), principal_balance, - timelock, + *timelock, loan_txid, ) .map_err(Error::LoanDetails)?; diff --git a/extension/wallet/src/wallet/make_create_swap_payload.rs b/extension/wallet/src/wallet/make_create_swap_payload.rs index 3d8ead3e..092f3516 100644 --- a/extension/wallet/src/wallet/make_create_swap_payload.rs +++ b/extension/wallet/src/wallet/make_create_swap_payload.rs @@ -2,9 +2,8 @@ use crate::{ wallet::{current, get_txouts, CreateSwapPayload, SwapUtxo, Wallet}, BTC_ASSET_ID, USDT_ASSET_ID, }; -use bdk::bitcoin::Amount; use coin_selection::{self, coin_select}; -use elements::{secp256k1_zkp::SECP256K1, AssetId, OutPoint}; +use elements::{bitcoin::Amount, secp256k1_zkp::SECP256K1, AssetId, OutPoint}; use estimate_transaction_size::avg_vbytes; use futures::lock::Mutex; use wasm_bindgen::UnwrapThrowExt; diff --git a/extension/wallet/src/wallet/make_loan_request.rs b/extension/wallet/src/wallet/make_loan_request.rs index 5da12ce6..c6730368 100644 --- a/extension/wallet/src/wallet/make_loan_request.rs +++ b/extension/wallet/src/wallet/make_loan_request.rs @@ -19,7 +19,7 @@ pub async fn make_loan_request( current_wallet: &Mutex>, collateral_amount: Amount, fee_rate: Amount, - timelock: u64, + timelock: u32, ) -> Result { let btc_asset_id = { let guard = BTC_ASSET_ID.lock().expect_throw("can get lock"); From 8cef14c8d57fdc895005157041d3ddaffe36dc84 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Mon, 2 Aug 2021 14:45:11 +1000 Subject: [PATCH 3/7] Lender calculates timelock upon loan request Instead of calculating the absolute timelock and serving it in the loan offer, we use relative term in days in the offer and calculate the actual timelock upon loan request. --- Cargo.lock | 3 +- bobtimus/Cargo.toml | 2 +- bobtimus/src/lib.rs | 19 +++++---- bobtimus/src/loan.rs | 39 +++++++++++++++++-- extension/src/background/index.ts | 1 - extension/src/components/OpenLoans.tsx | 4 +- extension/src/wasmProxy.ts | 3 +- extension/wallet/Cargo.toml | 2 +- extension/wallet/src/lib.rs | 3 -- .../wallet/src/wallet/make_loan_request.rs | 2 - waves/src/App.test.tsx | 9 ++--- waves/src/App.tsx | 2 +- waves/src/Bobtimus.tsx | 29 ++++++++++++-- waves/src/Borrow.tsx | 10 ++--- waves/src/waves-provider/index.d.ts | 1 - waves/src/waves-provider/wavesProvider.ts | 1 - 16 files changed, 85 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27464184..10042462 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,8 +124,7 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "baru" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a23513ccaf1216e056a58cbf696e14c21c4b0236da951747c9ede655e7c502a" +source = "git+https://github.com/da-kami/baru?branch=lender-decides-timelock#782ac49d81440f544dacc266d2720d48f3956655" dependencies = [ "anyhow", "bitcoin_hashes 0.9.6", diff --git a/bobtimus/Cargo.toml b/bobtimus/Cargo.toml index 46d32b2e..4a10eb8b 100644 --- a/bobtimus/Cargo.toml +++ b/bobtimus/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] anyhow = "1" async-trait = "0.1" -baru = "0.2" +baru = { git = "https://github.com/da-kami/baru", branch = "lender-decides-timelock" } bitcoin_hashes = "0.9.0" diesel = { version = "1.4", features = ["sqlite"] } diesel_migrations = "1.4" diff --git a/bobtimus/src/lib.rs b/bobtimus/src/lib.rs index 599b7993..f6f63fba 100644 --- a/bobtimus/src/lib.rs +++ b/bobtimus/src/lib.rs @@ -12,7 +12,7 @@ use crate::{ use anyhow::{Context, Result}; use baru::{ input::Input, - loan::{Lender0, Lender1, LoanRequest, LoanResponse}, + loan::{Lender0, Lender1, LoanResponse}, swap, }; use database::LiquidationForm; @@ -43,7 +43,7 @@ pub mod loan; pub mod problem; pub mod schema; -use crate::loan::{Interest, LoanOffer}; +use crate::loan::{Interest, LoanOffer, LoanRequest}; pub use amounts::*; use elements::bitcoin::PublicKey; use rust_decimal_macros::dec; @@ -266,8 +266,6 @@ where /// We return the range of possible loan terms to the borrower. /// The borrower can then request a loan using parameters that are within our terms. pub async fn handle_loan_offer_request(&mut self) -> Result { - let current_height = self.elementsd.get_blockcount().await?; - Ok(LoanOffer { rate: self.rate_service.latest_rate(), // TODO: Dynamic fee estimation @@ -280,10 +278,9 @@ where max_ltv: dec!(0.8), // TODO: Dynamic interest based on current market values interest: vec![Interest { - // Absolute timelock calculated from current block height - // Assuming 1 min block interval, 43200 mins = 30 days - timelock: current_height + 43200, + term: 30, interest_rate: dec!(0.15), + collateralization: dec!(1.5), }], }) } @@ -303,6 +300,11 @@ where ); let oracle_pk = PublicKey::from_private_key(&self.secp, &oralce_priv_key); + // calculate the absolute timelock from the loan term + let current_height = self.elementsd.get_blockcount().await?; + let term_in_minutes = payload.term * 24 * 60; + let timelock = current_height + term_in_minutes; + let lender_address = self .elementsd .get_new_segwit_confidential_address() @@ -334,8 +336,9 @@ where Self::find_inputs(&elementsd_client, asset, amount).await } }, - payload, + payload.into(), self.rate_service.latest_rate().bid.as_satodollar(), + timelock, ) .await .unwrap(); diff --git a/bobtimus/src/loan.rs b/bobtimus/src/loan.rs index e2a6875e..1b384289 100644 --- a/bobtimus/src/loan.rs +++ b/bobtimus/src/loan.rs @@ -1,5 +1,9 @@ use crate::{LiquidUsdt, Rate}; -use elements::bitcoin::Amount; +use baru::input::Input; +use elements::{ + bitcoin::{Amount, PublicKey}, + Address, +}; use rust_decimal::Decimal; #[derive(Debug, Clone, serde::Serialize)] @@ -45,8 +49,37 @@ pub struct LoanOffer { #[derive(Debug, Clone, serde::Serialize)] pub struct Interest { - /// Timelock in blocks - pub timelock: u32, + /// Loan term in days + pub term: u32, + /// Collateralization in percent + /// + /// Rational: If a borrower over-collateralizes with e.g. 150% -> better rate than at 140% + pub collateralization: Decimal, /// Interest rate in percent pub interest_rate: Decimal, } + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct LoanRequest { + #[serde(with = "::elements::bitcoin::util::amount::serde::as_sat")] + pub collateral_amount: Amount, + collateral_inputs: Vec, + #[serde(with = "::elements::bitcoin::util::amount::serde::as_sat")] + fee_sats_per_vbyte: Amount, + borrower_pk: PublicKey, + /// Loan term in days + pub term: u32, + borrower_address: Address, +} + +impl From for baru::loan::LoanRequest { + fn from(loan_request: LoanRequest) -> Self { + baru::loan::LoanRequest::new( + loan_request.collateral_amount, + loan_request.collateral_inputs, + loan_request.fee_sats_per_vbyte, + loan_request.borrower_pk, + loan_request.borrower_address, + ) + } +} diff --git a/extension/src/background/index.ts b/extension/src/background/index.ts index 2f5efde6..ddf120c9 100644 --- a/extension/src/background/index.ts +++ b/extension/src/background/index.ts @@ -73,7 +73,6 @@ browser.runtime.onMessage.addListener(async (msg: Message, sender) => { walletName, msg.payload.collateral, msg.payload.fee_rate, - msg.payload.timeout, ), MessageKind.LoanResponse, ); diff --git a/extension/src/components/OpenLoans.tsx b/extension/src/components/OpenLoans.tsx index fbc14d49..5a3aeea5 100644 --- a/extension/src/components/OpenLoans.tsx +++ b/extension/src/components/OpenLoans.tsx @@ -20,7 +20,7 @@ import { LoanDetails } from "../models"; import Btc from "./bitcoin.svg"; import Usdt from "./tether.svg"; -const debug = Debug("openloans:error"); +const error = Debug("openloans:error"); interface OpenLoansProps { openLoans: LoanDetails[] | undefined; @@ -53,7 +53,7 @@ function OpenLoan({ loanDetails, onRepayed, index }: OpenLoanProps) { await repayLoan(loanDetails.txid); onRepayed(); }, - onReject: (e) => debug("Failed to repay loan %s: %s", loanDetails.txid, e), + onReject: (e) => error("Failed to repay loan %s: %s", loanDetails.txid, e), }); const blockHeightHook = useAsync({ diff --git a/extension/src/wasmProxy.ts b/extension/src/wasmProxy.ts index ed726fa3..621d33be 100644 --- a/extension/src/wasmProxy.ts +++ b/extension/src/wasmProxy.ts @@ -59,12 +59,11 @@ export async function makeLoanRequestPayload( name: string, collateral: string, fee_rate: string, - timelock: string, ): Promise { const { make_loan_request } = await import("./wallet"); debug("makeLoanRequestPayload"); - return make_loan_request(name, collateral, fee_rate, timelock); + return make_loan_request(name, collateral, fee_rate); } export async function signAndSendSwap(name: string, hex: string): Promise { diff --git a/extension/wallet/Cargo.toml b/extension/wallet/Cargo.toml index 18781c1d..43acdbfc 100644 --- a/extension/wallet/Cargo.toml +++ b/extension/wallet/Cargo.toml @@ -13,7 +13,7 @@ default = ["console_error_panic_hook"] [dependencies] aes-gcm-siv = { version = "0.9", features = ["std"] } anyhow = "1" -baru = "0.2" +baru = { git = "https://github.com/da-kami/baru", branch = "lender-decides-timelock" } bip32 = { version = "0.2", features = ["secp256k1-ffi", "bip39"], default-features = false } coin_selection = { path = "../../coin_selection" } conquer-once = "0.3" diff --git a/extension/wallet/src/lib.rs b/extension/wallet/src/lib.rs index 8dd71d3c..90bae89b 100644 --- a/extension/wallet/src/lib.rs +++ b/extension/wallet/src/lib.rs @@ -235,19 +235,16 @@ pub async fn make_loan_request( wallet_name: String, collateral: String, fee_rate: String, - timeout: String, ) -> Result { // TODO: Change the UI to handle SATs not BTC let collateral_in_btc = map_err_from_anyhow!(parse_to_bitcoin_amount(collateral))?; let fee_rate_in_sat = Amount::from_sat(map_err_from_anyhow!(u64::from_str(fee_rate.as_str()))?); - let timeout = map_err_from_anyhow!(u32::from_str(timeout.as_str()))?; let loan_request = map_err_from_anyhow!( wallet::make_loan_request( wallet_name, &LOADED_WALLET, collateral_in_btc, fee_rate_in_sat, - timeout ) .await )?; diff --git a/extension/wallet/src/wallet/make_loan_request.rs b/extension/wallet/src/wallet/make_loan_request.rs index c6730368..4cb89865 100644 --- a/extension/wallet/src/wallet/make_loan_request.rs +++ b/extension/wallet/src/wallet/make_loan_request.rs @@ -19,7 +19,6 @@ pub async fn make_loan_request( current_wallet: &Mutex>, collateral_amount: Amount, fee_rate: Amount, - timelock: u32, ) -> Result { let btc_asset_id = { let guard = BTC_ASSET_ID.lock().expect_throw("can get lock"); @@ -113,7 +112,6 @@ pub async fn make_loan_request( blinding_key, collateral_amount, fee_rate, - timelock, btc_asset_id, usdt_asset_id, ) diff --git a/waves/src/App.test.tsx b/waves/src/App.test.tsx index 73d63de5..d4423c22 100644 --- a/waves/src/App.test.tsx +++ b/waves/src/App.test.tsx @@ -1,9 +1,5 @@ -import { act, render, screen } from "@testing-library/react"; import React from "react"; -import { Listener, Source, SSEProvider } from "react-hooks-sse"; -import { BrowserRouter } from "react-router-dom"; -import App, { Asset, reducer, State } from "./App"; -import { Interest, Rate } from "./Bobtimus"; +import { Asset, reducer, State } from "./App"; import calculateBetaAmount from "./calculateBetaAmount"; const defaultLoanOffer = { @@ -16,8 +12,9 @@ const defaultLoanOffer = { max_principal: 10000, max_ltv: 0.8, interest: [{ - timelock: 43200, + term: 30, interest_rate: 0.15, + collateralization: 1.5, }], }; diff --git a/waves/src/App.tsx b/waves/src/App.tsx index 95869dd9..1c5bb87b 100644 --- a/waves/src/App.tsx +++ b/waves/src/App.tsx @@ -224,7 +224,7 @@ export function reducer(state: State = initialState, action: Action) { // TODO: We currently always overwrite upon a new loan offer // This will have to be adapted once we refresh loan offers. const principalAmount = action.value.min_principal.toString(); - const loanTermInDays = action.value.interest[0].timelock / 60 / 24; + const loanTermInDays = action.value.interest[0].term; return { ...state, diff --git a/waves/src/Bobtimus.tsx b/waves/src/Bobtimus.tsx index 79f11faa..0ce56e21 100644 --- a/waves/src/Bobtimus.tsx +++ b/waves/src/Bobtimus.tsx @@ -1,7 +1,7 @@ import Debug from "debug"; import React, { ReactElement } from "react"; import { SSEProvider } from "react-hooks-sse"; -import { CreateSwapPayload, LoanRequestPayload } from "./waves-provider/wavesProvider"; +import { CreateSwapPayload, LoanRequestPayload, OutPoint } from "./waves-provider/wavesProvider"; const debug = Debug("bobtimus"); @@ -25,8 +25,9 @@ export interface Rate { } export interface Interest { - timelock: number; + term: number; interest_rate: number; // percentage, decimal represented as float + collateralization: number; // percentage, decimal represented as float } export interface LoanOffer { @@ -38,6 +39,17 @@ export interface LoanOffer { interest: Interest[]; } +export interface LoanRequest { + collateral_amount: number; + collateral_inputs: { txin: OutPoint; original_txout: any; blinding_key: string }[]; + fee_sats_per_vbyte: number; + borrower_pk: string; + borrower_address: string; + + /// Loan term in days + term: number; +} + export async function getLoanOffer(): Promise { let res = await fetch(`/api/loan/lbtc-lusdt`, { method: "GET", @@ -55,14 +67,23 @@ export async function getLoanOffer(): Promise { return await res.json(); } -export async function postLoanRequest(payload: LoanRequestPayload) { +export async function postLoanRequest(walletParams: LoanRequestPayload, termInDays: number) { + let loanRequest: LoanRequest = { + borrower_address: walletParams.borrower_address, + borrower_pk: walletParams.borrower_pk, + collateral_amount: walletParams.collateral_amount, + collateral_inputs: walletParams.collateral_inputs, + fee_sats_per_vbyte: walletParams.fee_sats_per_vbyte, + term: termInDays, + }; + let res = await fetch(`/api/loan/lbtc-lusdt`, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", }, - body: JSON.stringify(payload), + body: JSON.stringify(loanRequest), }); if (res.status !== 200) { diff --git a/waves/src/Borrow.tsx b/waves/src/Borrow.tsx index 48c3573d..e4a5dd5d 100644 --- a/waves/src/Borrow.tsx +++ b/waves/src/Borrow.tsx @@ -72,17 +72,13 @@ function Borrow({ dispatch, state, rate, wavesProvider, walletStatusAsyncState } try { const feeRate = state.loanOffer!.fee_sats_per_vbyte; - // Liquid has 1 minute blocktime - const loanTermInBlocks = state.loanTermInDays * 24 * 60; - debug("loan term in blocks: " + loanTermInBlocks); - - let loanRequest = await wavesProvider.makeLoanRequestPayload( + let loanRequestWalletParams = await wavesProvider.makeLoanRequestPayload( collateralAmount.toString(), feeRate.toString(), - loanTermInBlocks.toString(), ); - let loanResponse = await postLoanRequest(loanRequest); + + let loanResponse = await postLoanRequest(loanRequestWalletParams, state.loanTermInDays); debug(JSON.stringify(loanResponse)); let loanTransaction = await wavesProvider.signLoan(loanResponse); diff --git a/waves/src/waves-provider/index.d.ts b/waves/src/waves-provider/index.d.ts index a31f8a88..c711df07 100644 --- a/waves/src/waves-provider/index.d.ts +++ b/waves/src/waves-provider/index.d.ts @@ -18,7 +18,6 @@ export default class WavesProvider { public async makeLoanRequestPayload( collateral: string, fee_rate: string, - timeout: string, ): Promise; public async signAndSendSwap(tx_hex: string): Promise; diff --git a/waves/src/waves-provider/wavesProvider.ts b/waves/src/waves-provider/wavesProvider.ts index 7dd0c27a..9acbb238 100644 --- a/waves/src/waves-provider/wavesProvider.ts +++ b/waves/src/waves-provider/wavesProvider.ts @@ -35,7 +35,6 @@ export interface LoanRequestPayload { collateral_inputs: { txin: OutPoint; original_txout: any; blinding_key: string }[]; fee_sats_per_vbyte: number; borrower_pk: string; - timelock: number; borrower_address: string; } From 686ecdfe847ff06880a698f5396de42da2288425 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 3 Aug 2021 12:22:35 +1000 Subject: [PATCH 4/7] Latest version of fork that includes repay fix --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 10042462..9b94485b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,7 +124,7 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "baru" version = "0.2.0" -source = "git+https://github.com/da-kami/baru?branch=lender-decides-timelock#782ac49d81440f544dacc266d2720d48f3956655" +source = "git+https://github.com/da-kami/baru?branch=lender-decides-timelock#9bb512a0037b888659a8f66e6f1f5b682da1bd3b" dependencies = [ "anyhow", "bitcoin_hashes 0.9.6", From 705c266848c0f603237412c1456485c8216fcb42 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 3 Aug 2021 12:27:37 +1000 Subject: [PATCH 5/7] Sort UTXOs to have more deterministic logs This should help us to see more deterministic logs upon repay because the UTXOs that are skipped should always be in the same order. An unordered vec does not cause (known) bugs, but the log output can be confusing. Since this sorting should not be expensive we put it in. --- extension/wallet/src/esplora.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/extension/wallet/src/esplora.rs b/extension/wallet/src/esplora.rs index 9309f481..52befd98 100644 --- a/extension/wallet/src/esplora.rs +++ b/extension/wallet/src/esplora.rs @@ -39,10 +39,16 @@ pub async fn fetch_utxos(address: &Address) -> Result> { )); } - response + let mut utxos = response .json::>() .await - .context("failed to deserialize response") + .context("failed to deserialize response")?; + + // Sort UTXOs to have more deterministic output in case something goes wrong. + // Note that the order of these UTXOs does not have to be strictly assured. + utxos.sort_by(|l, r| l.txid.cmp(&r.txid).then(l.vout.cmp(&r.vout))); + + Ok(utxos) } /// Fetch transaction history for the specified address. From 23d94d67ecb6da0b94658cada18e03f80ed98a32 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 3 Aug 2021 16:57:18 +1000 Subject: [PATCH 6/7] Timelock as Unix timestamp instad of block-height MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Time is hard and u32 for timestamp does not help 😅 --- Cargo.lock | 80 ++++++++++++++++++++++++++- bobtimus/Cargo.toml | 1 + bobtimus/proptest-regressions/lib.txt | 7 +++ bobtimus/src/lib.rs | 54 ++++++++++++++++-- 4 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 bobtimus/proptest-regressions/lib.txt diff --git a/Cargo.lock b/Cargo.lock index 9b94485b..b959f3df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,6 +207,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitcoin" version = "0.26.0" @@ -290,6 +305,7 @@ dependencies = [ "jsonrpc_client", "log", "mime_guess", + "proptest", "reqwest", "rust-embed", "rust_decimal", @@ -1444,6 +1460,38 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "proptest" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits 0.2.14", + "quick-error 2.0.1", + "rand 0.8.3", + "rand_chacha 0.3.0", + "rand_xorshift 0.3.0", + "regex-syntax", + "rusty-fork", + "tempfile", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.9" @@ -1468,7 +1516,7 @@ dependencies = [ "rand_jitter", "rand_os", "rand_pcg", - "rand_xorshift", + "rand_xorshift 0.1.1", "winapi", ] @@ -1641,6 +1689,15 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.2", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -1833,6 +1890,18 @@ dependencies = [ "webpki", ] +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.5" @@ -2531,6 +2600,15 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.3.2" diff --git a/bobtimus/Cargo.toml b/bobtimus/Cargo.toml index 4a10eb8b..9d2f0a5f 100644 --- a/bobtimus/Cargo.toml +++ b/bobtimus/Cargo.toml @@ -21,6 +21,7 @@ http-api-problem = { version = "0.21", features = ["warp"] } jsonrpc_client = { version = "0.6", features = ["reqwest"] } log = "0.4" mime_guess = "2.0.3" +proptest = "1" reqwest = "0.11" rust-embed = "5.7.0" rust_decimal = { version = "1.15", features = ["serde-float"] } diff --git a/bobtimus/proptest-regressions/lib.txt b/bobtimus/proptest-regressions/lib.txt new file mode 100644 index 00000000..d958802b --- /dev/null +++ b/bobtimus/proptest-regressions/lib.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 9bb7f1b54a4968641948d278e51a05ed2930034b95de73641862e33bfcdcea4d # shrinks to term_in_days = 30868 diff --git a/bobtimus/src/lib.rs b/bobtimus/src/lib.rs index f6f63fba..01b6612e 100644 --- a/bobtimus/src/lib.rs +++ b/bobtimus/src/lib.rs @@ -47,6 +47,10 @@ use crate::loan::{Interest, LoanOffer, LoanRequest}; pub use amounts::*; use elements::bitcoin::PublicKey; use rust_decimal_macros::dec; +use std::{ + convert::TryFrom, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; pub const USDT_ASSET_ID: &str = "ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"; @@ -300,10 +304,7 @@ where ); let oracle_pk = PublicKey::from_private_key(&self.secp, &oralce_priv_key); - // calculate the absolute timelock from the loan term - let current_height = self.elementsd.get_blockcount().await?; - let term_in_minutes = payload.term * 24 * 60; - let timelock = current_height + term_in_minutes; + let timelock = days_to_unix_timestamp_timelock(payload.term, SystemTime::now())?; let lender_address = self .elementsd @@ -443,6 +444,21 @@ pub async fn liquidate_loans(elementsd: &Client, db: Sqlite) -> Result<()> { Ok(()) } +/// Calculates the absolute timelock from the loan term in days +/// +/// The timelock is represented as Unix timestamp (seconds since the epoch). +/// Note: Miniscript uses u32 for representing the timestamp so we return a u32. +fn days_to_unix_timestamp_timelock(term_in_days: u32, now: SystemTime) -> Result { + let since_the_epoch = now.duration_since(UNIX_EPOCH).expect("Time went backwards"); + + let term = Duration::from_secs((term_in_days * 24 * 60 * 60) as u64); + + let timelock = (since_the_epoch + term).as_secs(); + let timelock = u32::try_from(timelock) + .context("Overflow, the given timestamp appears to be too far in the future")?; + + Ok(timelock) +} #[cfg(test)] mod tests { @@ -460,8 +476,38 @@ mod tests { Address, AddressParams, OutPoint, Transaction, TxOut, }; use elements_harness::Elementsd; + use proptest::proptest; use testcontainers::clients::Cli; + // This test ensures that this function will not panic on different systems now and in the future. + // At the point of writing 30868 days were supported, equivalent to 84.569863 calendar years. + // We allow a maximum of 18250 days = 50 years for loan terms. + // This test will pass for the next ~34.5 years given a correct system time. + proptest! { + #[test] + fn timelock_calculation_does_not_panic_between_1_day_and_100_years( + term_in_days in 1u32..18250, // 18250 days = 50 years + ) { + let now = SystemTime::now(); + let _ = days_to_unix_timestamp_timelock(term_in_days, now).unwrap(); + } + } + + #[test] + fn timelock_calculation_30_days() { + let term_in_days = 30; + let now = SystemTime::now(); + + let since_epoch = u32::try_from(now.duration_since(UNIX_EPOCH).unwrap().as_secs()).unwrap(); + + let timelock = days_to_unix_timestamp_timelock(term_in_days, now).unwrap(); + + let difference = timelock - since_epoch; + + // 2_592_000 = 30 days in secs + assert_eq!(difference, 2_592_000) + } + #[tokio::test] async fn test_handle_btc_sell_swap_request() { let db = Sqlite::new_ephemeral_db().expect("A ephemeral db"); From fd5131a839526f4dae6f4dc4d177423a04f99f04 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Tue, 3 Aug 2021 21:16:18 +1000 Subject: [PATCH 7/7] Liquidation based on Unix timestamp With the timelock represented as Unix timestamp (in seconds since the epoch) we also have to filter for loans to liquidate using a Unix timestamp of the current time. --- bobtimus/src/database.rs | 4 ++-- bobtimus/src/lib.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bobtimus/src/database.rs b/bobtimus/src/database.rs index 3151cf87..6559bb96 100644 --- a/bobtimus/src/database.rs +++ b/bobtimus/src/database.rs @@ -111,10 +111,10 @@ pub mod queries { pub fn get_publishable_liquidations_txs( conn: &SqliteConnection, - blockcount: u32, + secs_since_epoch: u64, ) -> Result> { let txs = liquidations::table - .filter(liquidations::locktime.le(blockcount as i64)) + .filter(liquidations::locktime.le(secs_since_epoch as i64)) .get_results::(conn)?; let txs = txs diff --git a/bobtimus/src/lib.rs b/bobtimus/src/lib.rs index 01b6612e..cd305c54 100644 --- a/bobtimus/src/lib.rs +++ b/bobtimus/src/lib.rs @@ -427,10 +427,14 @@ impl RateSubscription { } pub async fn liquidate_loans(elementsd: &Client, db: Sqlite) -> Result<()> { - let blockcount = elementsd.get_blockcount().await?; + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + let secs_since_epoch = now.as_secs(); + let liquidation_txs = db .do_in_transaction(|conn| { - let txs = queries::get_publishable_liquidations_txs(conn, blockcount)?; + let txs = queries::get_publishable_liquidations_txs(conn, secs_since_epoch)?; Ok(txs) }) .await?;