switch base91 to hex, add duration time

This commit is contained in:
Felix
2024-06-12 16:00:42 +00:00
parent 0ad4b5d761
commit f513d17454
10 changed files with 76 additions and 64 deletions

View File

@ -5,7 +5,7 @@ use tokio::net::TcpListener;
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
struct OrderRequest { struct OrderRequest {
robohash_base91: String, robohash_hex: String,
amount_satoshi: u64, amount_satoshi: u64,
order_type: String, order_type: String,
bond_ratio: u8, bond_ratio: u8,
@ -16,7 +16,7 @@ async fn receive_order(Json(order): Json<OrderRequest>) {
println!("Received order: {:?}", order); println!("Received order: {:?}", order);
// Access individual fields // Access individual fields
let robohash = &order.robohash_base91; let robohash = &order.robohash_hex;
let amount = order.amount_satoshi; let amount = order.amount_satoshi;
let order_type = &order.order_type; let order_type = &order.order_type;
let bond_ratio = order.bond_ratio; let bond_ratio = order.bond_ratio;

View File

@ -137,12 +137,6 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base91"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4eb5fbae7b5ee422f239444a3dca9bdf5ecb3abf3af1bf87c8097db3f7bc025"
[[package]] [[package]]
name = "bdk" name = "bdk"
version = "0.29.0" version = "0.29.0"
@ -575,6 +569,12 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hex_lit" name = "hex_lit"
version = "0.1.1" version = "0.1.1"
@ -1586,10 +1586,10 @@ name = "trader"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base91",
"bdk", "bdk",
"dotenv", "dotenv",
"env_logger", "env_logger",
"hex",
"log", "log",
"musig2", "musig2",
"rand_core", "rand_core",

View File

@ -5,10 +5,10 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.86" anyhow = "1.0.86"
base91 = "0.1.0"
bdk = "0.29.0" bdk = "0.29.0"
dotenv = "0.15.0" dotenv = "0.15.0"
env_logger = "0.11.3" env_logger = "0.11.3"
hex = "0.4.3"
log = "0.4.21" log = "0.4.21"
musig2 = "0.0.11" musig2 = "0.0.11"
rand_core = "0.6.4" rand_core = "0.6.4"

View File

@ -1,7 +1,8 @@
ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server
COORDINATOR_ENDPOINT="http://127.0.0.1:3000" COORDINATOR_ENDPOINT="http://127.0.0.1:3000"
ROBOHASH_BASE91="o!kfNkee;RF?m6FT87:1bjASE2/(1NFTnR;ILmHB<1p/vP%STz~IWolNDJa.#PmTI)sfBk+Gs!,(w8M" # "base91 of hex of sha256 of robot21" ROBOHASH_HEX="26ee3dee4815655d223c3505162fd4610294a9542f89bb3d3e9748f534ac10ae" # sha256 of "robot21"
TRADE_TYPE="buy" TRADE_TYPE="buy"
PAYOUT_ADDRESS="tb1p45daj2eaza6drcd85c3wvn0zrpqxuduk3rzcmla4eu7a02cep9kqjzkc64" PAYOUT_ADDRESS="tb1p45daj2eaza6drcd85c3wvn0zrpqxuduk3rzcmla4eu7a02cep9kqjzkc64"
BOND_RATIO=5 BOND_RATIO=5
XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32" # wallet xprv XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32" # wallet xprv
OFFER_DURATION_HOURS=48

View File

@ -1,10 +1,13 @@
use anyhow::{anyhow, Result};
use sha2::{Digest, Sha256};
use std::env;
use std::io::{self, Write};
use crate::wallet::get_wallet_xprv; use crate::wallet::get_wallet_xprv;
use anyhow::{anyhow, Result};
use bdk::bitcoin::bip32::ExtendedPrivKey; use bdk::bitcoin::bip32::ExtendedPrivKey;
use hex;
use sha2::{Digest, Sha256};
use std::{
env,
io::{self, Write},
time::{SystemTime, UNIX_EPOCH},
};
#[derive(Debug)] #[derive(Debug)]
pub struct Coordinator; pub struct Coordinator;
@ -19,11 +22,12 @@ pub enum OfferType {
pub struct TraderSettings { pub struct TraderSettings {
pub electrum_endpoint: String, pub electrum_endpoint: String,
pub coordinator_endpoint: String, pub coordinator_endpoint: String,
pub robosats_robohash_base91: String, pub robosats_robohash_hex: String,
pub trade_type: OfferType, pub trade_type: OfferType,
pub payout_address: String, pub payout_address: String,
pub bond_ratio: u8, pub bond_ratio: u8,
pub wallet_xprv: ExtendedPrivKey, pub wallet_xprv: ExtendedPrivKey,
pub duration_unix_ts: u64, // until when the order should stay available
} }
#[derive(Debug)] #[derive(Debug)]
@ -39,14 +43,6 @@ fn hash256(input: &String) -> [u8; 32] {
hasher.finalize().into() hasher.finalize().into()
} }
// Robosats uses base91 encoded sha256 hash of the private robot key
fn bytes_to_base91(input: &[u8; 32]) -> String {
let encoded_robohash: String = base91::EncodeIterator::new(input.iter().copied())
.as_char_iter()
.collect();
encoded_robohash
}
impl OfferType { impl OfferType {
pub fn value(&self) -> u64 { pub fn value(&self) -> u64 {
match self { match self {
@ -87,11 +83,17 @@ impl CliSettings {
} }
} }
// parses the hours input string and returns the unix timestamp + the trade duration in seconds
fn hours_to_ts(hours: &String) -> Result<u64> {
let duration: u64 = hours.parse()?;
Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() + duration * 3600)
}
fn get_trader_settings() -> Result<TraderSettings> { fn get_trader_settings() -> Result<TraderSettings> {
let electrum_endpoint = Self::get_user_input("Enter electrum endpoint: "); let electrum_endpoint = Self::get_user_input("Enter electrum endpoint: ");
let coordinator_endpoint = Self::get_user_input("Enter coordinator endpoint: "); let coordinator_endpoint = Self::get_user_input("Enter coordinator endpoint: ");
let robosats_robohash_base91 = bytes_to_base91(&hash256(&Self::get_user_input( let robosats_robohash_hex = hex::encode(&hash256(&Self::get_user_input(
"Enter your robosats robot key: ", "Enter your robosats robot key: ", // just for testing purposes, to be improved to the real robohash spec
))); )));
let trade_type: OfferType = Self::get_trade_type(None); let trade_type: OfferType = Self::get_trade_type(None);
let payout_address = Self::get_user_input( let payout_address = Self::get_user_input(
@ -101,14 +103,18 @@ impl CliSettings {
let wallet_xprv = Self::check_xprv_input(Some(Self::get_user_input( let wallet_xprv = Self::check_xprv_input(Some(Self::get_user_input(
"Enter funded testnet wallet xprv or leave empty to generate: ", "Enter funded testnet wallet xprv or leave empty to generate: ",
)))?; )))?;
let duration_unix_ts: u64 = Self::hours_to_ts(&Self::get_user_input(
"How many hours should the offer stay online: ",
))?;
Ok(TraderSettings { Ok(TraderSettings {
electrum_endpoint, electrum_endpoint,
coordinator_endpoint, coordinator_endpoint,
robosats_robohash_base91, robosats_robohash_hex,
trade_type, trade_type,
payout_address, payout_address,
bond_ratio, bond_ratio,
wallet_xprv, wallet_xprv,
duration_unix_ts,
}) })
} }
@ -126,11 +132,12 @@ impl CliSettings {
Ok(TraderSettings { Ok(TraderSettings {
electrum_endpoint: env::var("ELECTRUM_ENDPOINT")?, electrum_endpoint: env::var("ELECTRUM_ENDPOINT")?,
coordinator_endpoint: env::var("COORDINATOR_ENDPOINT")?, coordinator_endpoint: env::var("COORDINATOR_ENDPOINT")?,
robosats_robohash_base91: env::var("ROBOHASH_BASE91")?, robosats_robohash_hex: env::var("ROBOHASH_HEX")?,
trade_type: Self::get_trade_type(Some(env::var("TRADE_TYPE")?)), trade_type: Self::get_trade_type(Some(env::var("TRADE_TYPE")?)),
payout_address: env::var("PAYOUT_ADDRESS")?, payout_address: env::var("PAYOUT_ADDRESS")?,
bond_ratio: env::var("BOND_RATIO")?.parse()?, bond_ratio: env::var("BOND_RATIO")?.parse()?,
wallet_xprv: Self::check_xprv_input(Some(env::var("XPRV")?))?, wallet_xprv: Self::check_xprv_input(Some(env::var("XPRV")?))?,
duration_unix_ts: Self::hours_to_ts(&env::var("OFFER_DURATION_HOURS")?)?,
}) })
} }

View File

@ -1,24 +1,36 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)] // maker step 1
pub struct OfferCreationResponse { // requesting to create an offer on the orderbook (POST request)
pub bond_address: String,
pub locking_amount: u64, // validate
}
#[derive(Serialize)] #[derive(Serialize)]
pub struct OrderRequest { pub struct OrderRequest {
pub robohash_base91: String, pub robohash_hex: String, // identifier of the trader
pub amount_satoshi: u64, pub amount_satoshi: u64, // amount in satoshi to buy or sell
pub order_type: String, // buy or sell pub is_buy_order: bool, // true if buy, false if sell
pub bond_ratio: u8, // [2, 50] pub bond_ratio: u8, // [2, 50]% of trading amount
pub offer_duration_ts: u64, // unix timestamp how long the offer should stay available
} }
// coordinator answer to maker step 1
// direct Json answer to step 1 (same request)
#[derive(Debug, Deserialize)]
pub struct OfferCreationResponse {
pub bond_address: String, // address the bond has to be locked to
pub locking_amount_sat: u64, // min amount of the bond output in sat
}
// maker step 2
// (submission of signed bond and other data neccessary to coordinate the trade)
#[derive(Serialize)] #[derive(Serialize)]
pub struct BondSubmissionRequest { pub struct BondSubmissionRequest {
pub robohash_base91: String, pub robohash_hex: String,
pub signed_bond_base91: String, pub signed_bond_hex: String, // signed bond transaction, hex encoded
pub payout_address: String, // does this make sense here? pub payout_address: String, // does this make sense here?
pub musig_pub_nonce_base91: String, pub musig_pub_nonce_hex: String,
pub musig_pubkey_base91: String, pub musig_pubkey_hex: String,
}
pub struct OrderActivatedResponse {
pub order_id_hex: String,
pub bond_locked_until_timestamp: u128, // unix timestamp. Do not touch bond till then unless offer gets taken.
} }

View File

@ -9,22 +9,23 @@ use api::{OfferCreationResponse, OrderRequest};
impl OfferCreationResponse { impl OfferCreationResponse {
fn _format_request(trader_setup: &TraderSettings) -> OrderRequest { fn _format_request(trader_setup: &TraderSettings) -> OrderRequest {
let amount: u64; let amount: u64;
let trade_type = match &trader_setup.trade_type { let is_buy_order = match &trader_setup.trade_type {
OfferType::Buy(val) => { OfferType::Buy(val) => {
amount = *val; amount = *val;
"buy" true
} }
OfferType::Sell(val) => { OfferType::Sell(val) => {
amount = *val; amount = *val;
"sell" false
} }
}; };
OrderRequest { OrderRequest {
robohash_base91: trader_setup.robosats_robohash_base91.clone(), robohash_hex: trader_setup.robosats_robohash_hex.clone(),
amount_satoshi: amount, amount_satoshi: amount,
order_type: trade_type.to_string(), is_buy_order,
bond_ratio: trader_setup.bond_ratio, bond_ratio: trader_setup.bond_ratio,
offer_duration_ts: trader_setup.duration_unix_ts,
} }
} }

View File

@ -7,13 +7,6 @@ use crate::wallet::{
}; };
use anyhow::Result; use anyhow::Result;
use bdk::database::MemoryDatabase; use bdk::database::MemoryDatabase;
// use bdk::{
// bitcoin::block,
// blockchain::{Blockchain, ElectrumBlockchain},
// electrum_client::Client,
// wallet::AddressIndex::LastUnused,
// Wallet,
// };
pub struct ActiveOffer {} pub struct ActiveOffer {}
@ -22,20 +15,17 @@ impl ActiveOffer {
trading_wallet: &TradingWallet, trading_wallet: &TradingWallet,
maker_config: &TraderSettings, maker_config: &TraderSettings,
) -> Result<ActiveOffer> { ) -> Result<ActiveOffer> {
let offer_conditions = OfferCreationResponse::fetch(maker_config)?;
let trading_wallet = &trading_wallet.wallet; let trading_wallet = &trading_wallet.wallet;
// let offer_conditions = OfferCreationResponse::fetch(maker_config)?;
let offer_conditions = OfferCreationResponse { let offer_conditions = OfferCreationResponse {
// hardcoded for testing, locking_address is owned by .env xprv // hardcoded for testing, locking_address is owned by .env xprv
locking_amount: 90000, locking_amount_sat: 90000,
bond_address: "tb1pfdvgfzwp8vhmelpv8w9kezz7nsmxw68jz6yehgze6mzx0t6r9t2qv9ynmm" bond_address: "tb1pfdvgfzwp8vhmelpv8w9kezz7nsmxw68jz6yehgze6mzx0t6r9t2qv9ynmm"
.to_string(), .to_string(),
}; };
let bond = Bond::assemble(trading_wallet, &offer_conditions, maker_config)?; let bond = Bond::assemble(trading_wallet, &offer_conditions, maker_config)?;
let payout_pubkey = trading_wallet.get_address(bdk::wallet::AddressIndex::LastUnused)?; let payout_pubkey = trading_wallet.get_address(bdk::wallet::AddressIndex::LastUnused)?;
let musig_data = MuSigData::create(&maker_config.wallet_xprv, trading_wallet.secp_ctx())?; let musig_data = MuSigData::create(&maker_config.wallet_xprv, trading_wallet.secp_ctx())?;
Ok(ActiveOffer {}) Ok(ActiveOffer {})

View File

@ -44,7 +44,7 @@ impl Bond {
)); ));
builder builder
.add_recipient(address.script_pubkey(), bond_target.locking_amount) .add_recipient(address.script_pubkey(), bond_target.locking_amount_sat)
.do_not_spend_change() .do_not_spend_change()
.fee_rate(FeeRate::from_sat_per_vb(201.0)); .fee_rate(FeeRate::from_sat_per_vb(201.0));

View File

@ -1,7 +1,8 @@
ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server
COORDINATOR_ENDPOINT="http://127.0.0.1:3000" COORDINATOR_ENDPOINT="http://127.0.0.1:3000"
ROBOHASH_BASE91="n!DyWmzXG3"1"*$S(GH<r+!G/!!u^aFTe%!x(iee}R[+vv+S4t^a<mAk!I{ybCQiD2%Idj>G4RB2gvM" # "base91 of hex of sha256 of robot22" ROBOHASH_HEX="169b6049cf865eba7d01e1ad26975f1d5ff29d570297ff18d40a53c8281dff5d" # sha256 of "robot22"
TRADE_TYPE="sell" TRADE_TYPE="sell"
PAYOUT_ADDRESS="tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm" PAYOUT_ADDRESS="tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm"
BOND_RATIO=5 BOND_RATIO=5
XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32" # wallet xprv XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32" # wallet xprv
OFFER_DURATION_HOURS=48