mirror of
https://github.com/RoboSats/taptrade-core.git
synced 2025-07-21 10:13:23 +00:00
switch base91 to hex, add duration time
This commit is contained in:
@ -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;
|
||||||
|
14
taptrade-cli-demo/trader/Cargo.lock
generated
14
taptrade-cli-demo/trader/Cargo.lock
generated
@ -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",
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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")?)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {})
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user