diff --git a/fake-coinjoin.sh b/fake-coinjoin.sh index ab5ee6a..c6d6dc1 100755 --- a/fake-coinjoin.sh +++ b/fake-coinjoin.sh @@ -16,6 +16,8 @@ fi # Values below 1000 are treated as estimated blocks to confirm, # 1000 and above as sat/kB. tx_fees=3 +# [fraction, 0-1] / variance around the average fee. Ex: 1000 fee, 0.2 var = fee is btw 800-1200 +txfee_factor=0.3 # Abort if TX fee per KB is above this number (satoshis) absurd_fee_per_kb=150000 # Coin selection ("merge") algorithm. @@ -43,9 +45,12 @@ else exit 1 fi fi +fee="$(randamount \ + "$(bc_float_calc "$fee * (1 - $txfee_factor)")" \ + "$(bc_float_calc "$fee * (1 + $txfee_factor)")")" mempoolminfee="$(call_bitcoin_cli getmempoolinfo | jq_btc_float ".mempoolminfee")" if is_btc_lt "$fee" "$mempoolminfee"; then - echo "Fee $fee is below minimum mempool fee, raising to $mempoolminfee" + echo "Feerate $fee is below minimum mempool fee, raising to $mempoolminfee" fee="$mempoolminfee" fi if is_btc_gte "$fee" "$(bc_float_calc "$absurd_fee_per_kb * 0.00000001")"; then diff --git a/inc.common.sh b/inc.common.sh index 993e655..f822683 100644 --- a/inc.common.sh +++ b/inc.common.sh @@ -174,6 +174,15 @@ function is_btc_lt() $(echo "$2" | btc_amount_format | tr -d '.' | sed 's/^0*//' | sed 's/^$/0/') \ )) } +# is "$1" less than or equal to "$2" +function is_btc_lte() +{ + (( \ + $(echo "$1" | btc_amount_format | tr -d '.' | sed 's/^0*//' | sed 's/^$/0/') \ + <= \ + $(echo "$2" | btc_amount_format | tr -d '.' | sed 's/^0*//' | sed 's/^$/0/') \ + )) +} function is_valid_bitcoin_address() { @@ -518,3 +527,11 @@ function bip21_get_param() fi fi } + +function randamount() +{ + minamount="$1" + maxamount="$2" + diff="$(bc_float_calc "$maxamount - $minamount")" + bc_float_calc "$minamount + $RANDOM * $diff * 0.00003055581" +} diff --git a/randbtc.sh b/randbtc.sh index e293ca4..736e058 100755 --- a/randbtc.sh +++ b/randbtc.sh @@ -1,4 +1,4 @@ -#! /bin/bash +#!/usr/bin/env bash . $(dirname $0)/inc.common.sh @@ -7,10 +7,4 @@ if [ "$2" == "" ]; then exit fi -minamount=$1 -maxamount=$2 - -diff=$(bc_float_calc "$maxamount - $minamount") - -bc_float_calc "$minamount + $RANDOM * $diff * 0.00003055581" - +randamount "$1" "$2" diff --git a/ricochet-send-from.sh b/ricochet-send-from.sh index f1e9adc..3187770 100755 --- a/ricochet-send-from.sh +++ b/ricochet-send-from.sh @@ -3,15 +3,16 @@ . $(dirname $0)/inc.common.sh if [ "$2" == "" ]; then - echo "Usage: $(basename $0) [options] source_address destination_address [hops [fee [sleeptime_min [sleeptime_max [hop_confirmations]]]]]" + echo "Usage: $(basename $0) [options] source_address destination_address [hops [txfee [sleeptime_min [sleeptime_max [hop_confirmations [txfee_factor]]]]]]" echo "Where:" echo " source_address - source address (all funds from that address will be send)" echo " destination_address - destination address" echo " hops - number of hops (default: 5)" - echo " fee - transaction fee per kW (default: \"estimatesmartfee 2\", currently $($(dirname $0)/estimatesmartfee.sh $bitcoin_cli_options 2) BTC)" + echo " txfee - average transaction fee per kvB (default: \"estimatesmartfee 2\", currently $($(dirname $0)/estimatesmartfee.sh $bitcoin_cli_options 2) BTC)" echo " sleeptime_min - minimum sleep time between hops in seconds (default: 10)" echo " sleeptime_max - maximum sleep time between hops in seconds (default: 15)" echo " hop_confirmations - minimum number of confirmations between hops (default: 0)" + echo " txfee_factor - variance around average transaction fee, e.g. 0.00002000 fee, 0.2 var = fee is between 0.00001600 and 0.00002400 (default: 0.3)" exit fi @@ -36,9 +37,9 @@ if [ "$3" != "" ]; then hops=$3 fi if [ "$4" != "" ]; then - fee=$4 + txfee="$4" else - fee=$($(dirname $0)/estimatesmartfee.sh $bitcoin_cli_options 2) + txfee="$($(dirname $0)/estimatesmartfee.sh $bitcoin_cli_options 2)" fi if [ "$6" != "" ]; then sleeptime_min=$5 @@ -52,13 +53,19 @@ if [ "$7" != "" ]; then else hop_confirmations=0 fi +if [ "$8" != "" ]; then + txfee_factor="$8" +else + txfee_factor="0.3" +fi # Force minimum required fee -fee="$(echo "$fee" | btc_amount_format)" +txfee_min="$(bc_float_calc "$txfee * (1 - $txfee_factor)")" +txfee_max="$(bc_float_calc "$txfee * (1 + $txfee_factor)")" mempoolminfee="$(call_bitcoin_cli getmempoolinfo | jq_btc_float ".mempoolminfee")" -if is_btc_lt "$fee" "$mempoolminfee"; then - echo "Fee $fee is below minimum mempool fee, raising to $mempoolminfee" - fee="$mempoolminfee" +if is_btc_lt "$txfee_min" "$mempoolminfee"; then + echo "Feerate $txfee_min is below minimum mempool fee, raising minimum to $mempoolminfee" + txfee_min="$mempoolminfee" fi utxo="$(call_bitcoin_cli listunspent 0 999999 "[\"$source_address\"]" false)" @@ -75,7 +82,8 @@ if [ "${#utxo_txids[@]}" == "0" ]; then exit 2 fi -echo "Ricocheting from $source_address ($send_amount BTC) to $destination_address via $hops hops using $fee fee per KB" +txfee_average="$(bc_float_calc "($txfee_min + $txfee_max) * 0.5")" +echo "Ricocheting from $source_address ($send_amount BTC) to $destination_address via $hops hops using average $txfee_average fee per kvB" read -p "Is this ok? " -n 1 -r echo @@ -138,6 +146,7 @@ for i in $(seq 0 $(( ${#utxo_txids[@]} - 1 )) ); do needs_comma=1 done rawtx_inputs="$rawtx_inputs]" +fee="$(randamount "$txfee_min" "$txfee_max")" send_amount=$(bc_float_calc "$send_amount - $(bc_float_calc "$tx_vsize * $fee * 0.001")") rawtx=$(call_bitcoin_cli createrawtransaction "$rawtx_inputs" "{\"${ricochet_addresses[$(( $hops - 1 ))]}\":$send_amount}") signedtx=$(signrawtransactionwithwallet "$rawtx") @@ -183,7 +192,7 @@ for i in $(seq 1 $(( $hops - 1)) | tac); do tx_vsize=$(calc_tx_vsize 0 0 1 0 0 1) fi fi - + fee="$(randamount "$txfee_min" "$txfee_max")" send_amount=$(bc_float_calc "$send_amount - $(bc_float_calc "$tx_vsize * $fee * 0.001")") echo -n "$j: ${ricochet_addresses[$i]} -> ${ricochet_addresses[$(( $i - 1 ))]} ($send_amount) - " rawtx=$(call_bitcoin_cli createrawtransaction "[{\"txid\":\"$use_txid\",\"vout\":0}]" "{\"${ricochet_addresses[$(( $i - 1 ))]}\":$send_amount}") diff --git a/ricochet-send.sh b/ricochet-send.sh index b9b98e9..fc33271 100755 --- a/ricochet-send.sh +++ b/ricochet-send.sh @@ -3,15 +3,16 @@ . $(dirname $0)/inc.common.sh if [ "$2" == "" ]; then - echo "Usage: $(basename $0) [options] amount destination_address [hops [fee [sleeptime_min [sleeptime_max [hop_confirmations]]]]" + echo "Usage: $(basename $0) [options] amount destination_address [hops [txfee [sleeptime_min [sleeptime_max [hop_confirmations [txfee_factor]]]]]]" echo "Where:" echo " amount - amount to send in BTC" echo " destination_address - destination address" echo " hops - number of hops (default: 5)" - echo " fee - transaction fee per kW (default: \"estimatesmartfee 2\", currently $($(dirname $0)/estimatesmartfee.sh $bitcoin_cli_options 2) BTC)" + echo " txfee - average transaction fee per kvB (default: \"estimatesmartfee 2\", currently $($(dirname $0)/estimatesmartfee.sh $bitcoin_cli_options 2) BTC)" echo " sleeptime_min - minimum sleep time between hops in seconds (default: 10)" echo " sleeptime_max - maximum sleep time between hops in seconds (default: 15)" echo " hop_confirmations - minimum number of confirmations between hops (default: 0)" + echo " txfee_factor - variance around average transaction fee, e.g. 0.00002000 fee, 0.2 var = fee is between 0.00001600 and 0.00002400 (default: 0.3)" exit fi @@ -32,9 +33,9 @@ if [ "$3" != "" ]; then hops=$3 fi if [ "$4" != "" ]; then - fee=$4 + txfee="$4" else - fee=$($(dirname $0)/estimatesmartfee.sh $bitcoin_cli_options 2) + txfee="$($(dirname $0)/estimatesmartfee.sh $bitcoin_cli_options 2)" fi if [ "$6" != "" ]; then sleeptime_min=$5 @@ -48,16 +49,23 @@ if [ "$7" != "" ]; then else hop_confirmations=0 fi +if [ "$8" != "" ]; then + txfee_factor="$8" +else + txfee_factor="0.3" +fi # Force minimum required fee -fee="$(echo "$fee" | btc_amount_format)" +txfee_min="$(bc_float_calc "$txfee * (1 - $txfee_factor)")" +txfee_max="$(bc_float_calc "$txfee * (1 + $txfee_factor)")" mempoolminfee="$(call_bitcoin_cli getmempoolinfo | jq_btc_float ".mempoolminfee")" -if is_btc_lt "$fee" "$mempoolminfee"; then - echo "Fee $fee is below minimum mempool fee, raising to $mempoolminfee" - fee="$mempoolminfee" +if is_btc_lt "$txfee_min" "$mempoolminfee"; then + echo "Feerate $txfee_min is below minimum mempool fee, raising minimum to $mempoolminfee" + txfee_min="$mempoolminfee" fi +txfee_average="$(bc_float_calc "($txfee_min + $txfee_max) * 0.5")" -echo "Ricocheting $amount BTC to $address via $hops hops using $fee fee per KB" +echo "Ricocheting $amount BTC to $address via $hops hops using average $txfee_average fee per kvB" read -p "Is this ok? " -n 1 -r echo @@ -77,18 +85,27 @@ ricochet_addresses+=("$address") # FixMe: TX size may vary depending on input and output address types ricochet_tx_size=192 -single_ricochet_tx_fee=$(bc_float_calc "$ricochet_tx_size * $fee * 0.001") -ricochet_fees=$(bc_float_calc "($hops - 1) * $single_ricochet_tx_fee") -send_amount=$(bc_float_calc "$amount + $ricochet_fees") +ricochet_fees=( + "$(randamount "$txfee_min" "$txfee_max")" +) +ricochet_fee_sum="0" +for i in $(seq 1 $(( $hops - 1 ))); do + fee="$(randamount "$txfee_min" "$txfee_max")" + ricochet_fees+=("$fee") + ricochet_fee_sum="$(bc_float_calc "$ricochet_fee_sum + $fee")" +done +ricochet_fees+=("0") +send_amount=$(bc_float_calc "$amount + $ricochet_fee_sum") #echo "Richochet addresses: ${ricochet_addresses[@]}" +#echo "Ricochet fees: ${ricochet_fees[@]}" # Send out first transaction echo -n "0: (wallet) -> ${ricochet_addresses[0]} ($send_amount) - " -call_bitcoin_cli settxfee $fee > /dev/null -txid=$(call_bitcoin_cli sendtoaddress ${ricochet_addresses[0]} $send_amount) +call_bitcoin_cli settxfee "${ricochet_fees[0]}" > /dev/null +txid="$(call_bitcoin_cli sendtoaddress ${ricochet_addresses[0]} $send_amount)" echo "$txid" -rawtx=$(show_tx_by_id $txid) +rawtx="$(show_tx_by_id $txid)" #echo "$rawtx" vout_idx="" idx=0 @@ -116,10 +133,10 @@ use_txid="$txid" echo "Preparing rest of transactions..." signedtxes=() for i in $(seq 1 $(( $hops - 1 ))); do - send_amount=$(bc_float_calc "$send_amount - $single_ricochet_tx_fee") + send_amount="$(bc_float_calc "$send_amount - ${ricochet_fees[$i]}")" echo -n "$i: ${ricochet_addresses[$(( $i - 1 ))]} -> ${ricochet_addresses[$i]} ($send_amount) - " - rawtx=$(call_bitcoin_cli createrawtransaction "[{\"txid\":\"$use_txid\",\"vout\":$vout_idx}]" "{\"${ricochet_addresses[$i]}\":$send_amount}") - privkey=$(call_bitcoin_cli dumpprivkey "${ricochet_addresses[$(( $i - 1 ))]}") + rawtx="$(call_bitcoin_cli createrawtransaction "[{\"txid\":\"$use_txid\",\"vout\":$vout_idx}]" "{\"${ricochet_addresses[$i]}\":$send_amount}")" + privkey="$(call_bitcoin_cli dumpprivkey "${ricochet_addresses[$(( $i - 1 ))]}")" signedtx="$(signrawtransactionwithkey "$rawtx" "[\"$privkey\"]" "[{\"txid\":\"$use_txid\",\"vout\":$vout_idx,\"scriptPubKey\":\"$prev_pubkey\",\"amount\":$send_amount}]")" decodedtx="$(call_bitcoin_cli decoderawtransaction "$signedtx")" use_txid="$(echo "$decodedtx" | jq -r ".txid")" diff --git a/tests/rand.bats b/tests/rand.bats new file mode 100755 index 0000000..ae3a098 --- /dev/null +++ b/tests/rand.bats @@ -0,0 +1,8 @@ +#!/usr/bin/env bats + +. ../inc.common.sh + +@test "Randomization" { + is_btc_gte "$(randamount "0.1" "0.2")" "0.1" + is_btc_lte "$(randamount "0.1" "0.2")" "0.2" +}