Skip to content

Commit

Permalink
Allow both BTC and sat amounts for single send / CJ
Browse files Browse the repository at this point in the history
  • Loading branch information
kristapsk committed Nov 7, 2019
1 parent 9db7325 commit b2e4308
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 33 deletions.
1 change: 1 addition & 0 deletions jmbitcoin/jmbitcoin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
from jmbitcoin.secp256k1_deterministic import *
from jmbitcoin.btscript import *
from jmbitcoin.bech32 import *
from jmbitcoin.amount import *

46 changes: 46 additions & 0 deletions jmbitcoin/jmbitcoin/amount.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from decimal import Decimal

def btc_to_sat(btc):
return int(Decimal(btc) * Decimal('1e8'))

def sat_to_btc(sat):
return Decimal(sat) / Decimal('1e8')

# 1 = 0.00000001 BTC = 1sat
# 1sat = 0.00000001 BTC = 1sat
# 1.123sat = 0.00000001 BTC = 1sat
# 0.00000001 = 0.00000001 BTC = 1sat
# 0.00000001btc = 0.00000001 BTC = 1sat
# 1.00000000 = 1.00000000 BTC = 100000000sat
# 1.12300000sat = 0.00000001 BTC = 1sat
# 1btc = 1.00000000 BTC = 10000000sat

def amount_to_sat(amount_str):
amount_str = str(amount_str)
if amount_str.lower().endswith("btc"):
return int(btc_to_sat(amount_str[:-3]))
elif amount_str.lower().endswith("sat"):
return int(Decimal(amount_str[:-3]))
elif "." in amount_str:
return int(btc_to_sat(amount_str))
else:
return int(Decimal(amount_str))

def amount_to_btc(amount_str):
return amount_to_sat(amount_str) / Decimal('1e8')

def amount_to_sat_str(amount_str):
return str(amount_to_sat(amount_str)) + " sat"

def amount_to_btc_str(amount_str):
return str(amount_to_btc(amount_str)) + " BTC"

def amount_to_str(amount_str):
return amount_to_btc_str(amount_str) + " (" + amount_to_sat_str(amount_str) + ")"

def sat_to_str(sat):
return '%.8f' % sat_to_btc(sat)

def sat_to_str_p(sat):
return '%+.8f' % sat_to_btc(sat)

8 changes: 4 additions & 4 deletions jmclient/jmclient/taker_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\
schedule_to_text
from .wallet import BaseWallet, estimate_tx_fee
from jmbitcoin import deserialize, mktx, serialize, txhash
from jmbitcoin import deserialize, mktx, serialize, txhash, amount_to_str
log = get_log()

"""
Expand Down Expand Up @@ -74,17 +74,17 @@ def direct_send(wallet_service, amount, mixdepth, destaddr, answeryes=False,
outs.append({"value": changeval, "address": change_addr})

#Now ready to construct transaction
log.info("Using a fee of : " + str(fee_est) + " satoshis.")
log.info("Using a fee of : " + amount_to_str(fee_est) + ".")
if amount != 0:
log.info("Using a change value of: " + str(changeval) + " satoshis.")
log.info("Using a change value of: " + amount_to_str(changeval) + ".")
txsigned = sign_tx(wallet_service, mktx(list(utxos.keys()), outs), utxos)
log.info("Got signed transaction:\n")
log.info(pformat(txsigned))
tx = serialize(txsigned)
log.info("In serialized form (for copy-paste):")
log.info(tx)
actual_amount = amount if amount != 0 else total_inputs_val - fee_est
log.info("Sends: " + str(actual_amount) + " satoshis to address: " + destaddr)
log.info("Sends: " + amount_to_str(actual_amount) + " to address: " + destaddr)
if not answeryes:
if not accept_callback:
if input('Would you like to push to the network? (y/n):')[0] != 'y':
Expand Down
18 changes: 7 additions & 11 deletions jmclient/jmclient/wallet_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,24 +636,20 @@ def wallet_fetch_history(wallet, options):

def s():
return ',' if options.csv else ' '
def sat_to_str(sat):
return '%.8f'%(sat/1e8)
def sat_to_str_p(sat):
return '%+.8f'%(sat/1e8)
def sat_to_str_na(sat):
if sat == 0:
return "N/A "
else:
return '%.8f'%(sat/1e8)
return btc.sat_to_str(sat)
def skip_n1(v):
return '% 2s'%(str(v)) if v != -1 else ' #'
def skip_n1_btc(v):
return sat_to_str(v) if v != -1 else '#' + ' '*10
return btc.sat_to_str(v) if v != -1 else '#' + ' '*10
def print_row(index, time, tx_type, amount, delta, balance, cj_n,
total_fees, utxo_count, mixdepth_src, mixdepth_dst, txid):
data = [index, datetime.fromtimestamp(time).strftime("%Y-%m-%d %H:%M"),
tx_type, sat_to_str(amount), sat_to_str_p(delta),
sat_to_str(balance), skip_n1(cj_n), sat_to_str_na(total_fees),
tx_type, btc.sat_to_str(amount), btc.sat_to_str_p(delta),
btc.sat_to_str(balance), skip_n1(cj_n), sat_to_str_na(total_fees),
'% 3d' % utxo_count, skip_n1(mixdepth_src), skip_n1(mixdepth_dst)]
if options.verbosity % 2 == 0: data += [txid]
jmprint(s().join(map('"{}"'.format, data)), "info")
Expand Down Expand Up @@ -870,16 +866,16 @@ def f(r, deposits, deposit_times, now, final_balance):
include_disabled=True).values())
if balance + unconfirmed_balance != total_wallet_balance:
jmprint(('BUG ERROR: wallet balance (%s) does not match balance from ' +
'history (%s)') % (sat_to_str(total_wallet_balance),
sat_to_str(balance)))
'history (%s)') % (btc.sat_to_str(total_wallet_balance),
btc.sat_to_str(balance)))
wallet_utxo_count = sum(map(len, wallet.get_utxos_by_mixdepth(
include_disabled=True, hexfmt=False).values()))
if utxo_count + unconfirmed_utxo_count != wallet_utxo_count:
jmprint(('BUG ERROR: wallet utxo count (%d) does not match utxo count from ' +
'history (%s)') % (wallet_utxo_count, utxo_count))

if unconfirmed_balance != 0:
jmprint('unconfirmed balance change = %s BTC' % sat_to_str(unconfirmed_balance))
jmprint('unconfirmed balance change = %s BTC' % btc.sat_to_str(unconfirmed_balance))

# wallet-tool.py prints return value, so return empty string instead of None here
return ''
Expand Down
23 changes: 9 additions & 14 deletions scripts/joinmarket-qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import platform, json, threading, time
import qrcode

from decimal import Decimal
from PySide2 import QtCore

from PySide2.QtGui import *
Expand Down Expand Up @@ -83,8 +82,6 @@


from twisted.internet import task
def satoshis_to_amt_str(x):
return str(Decimal(x)/Decimal('1e8')) + " BTC"

log = get_log()

Expand Down Expand Up @@ -124,13 +121,13 @@ def checkAddress(parent, addr):
def getSettingsWidgets():
results = []
sN = ['Recipient address', 'Number of counterparties', 'Mixdepth',
'Amount in bitcoins (BTC)']
'Amount (BTC or sat)']
sH = ['The address you want to send the payment to',
'How many other parties to send to; if you enter 4\n' +
', there will be 5 participants, including you.\n' +
'Enter 0 to send direct without coinjoin.',
'The mixdepth of the wallet to send the payment from',
'The amount IN BITCOINS to send.\n' +
'The amount to send, either BTC (if contains dot) or satoshis.\n' +
'If you enter 0, a SWEEP transaction\nwill be performed,' +
' spending all the coins \nin the given mixdepth.']
sT = [str, int, int, float]
Expand Down Expand Up @@ -591,9 +588,9 @@ def checkDirectSend(self, dtx, destaddr, amount, fee):
note the callback includes the full prettified transaction,
but currently not printing it for space reasons.
"""
mbinfo = ["Sending " + satoshis_to_amt_str(amount) + ",",
mbinfo = ["Sending " + btc.amount_to_str(amount) + ",",
"to: " + destaddr + ",",
"Fee: " + satoshis_to_amt_str(fee) + ".",
"Fee: " + btc.amount_to_str(fee) + ".",
"Accept?"]
reply = JMQtMessageBox(self, '\n'.join([m + '<p>' for m in mbinfo]),
mbtype='question', title="Direct send")
Expand All @@ -614,7 +611,7 @@ def startSingle(self):
destaddr = str(self.widgets[0][1].text())
#convert from bitcoins (enforced by QDoubleValidator) to satoshis
btc_amount_str = self.widgets[3][1].text()
amount = int(Decimal(btc_amount_str) * Decimal('1e8'))
amount = btc.amount_to_sat(btc_amount_str)
makercount = int(self.widgets[1][1].text())
mixdepth = int(self.widgets[2][1].text())
if makercount == 0:
Expand Down Expand Up @@ -724,11 +721,9 @@ def checkOffers(self, offers_fee, cjamount):
return
offers, total_cj_fee = offers_fee
total_fee_pc = 1.0 * total_cj_fee / self.taker.cjamount
#Note this will be a new value if sweep, else same as previously entered
btc_amount_str = satoshis_to_amt_str(self.taker.cjamount)

mbinfo = []
mbinfo.append("Sending amount: " + btc_amount_str)
mbinfo.append("Sending amount: " + btc.amount_to_str(self.taker.cjamount))
mbinfo.append("to address: " + self.taker.my_cj_addr)
mbinfo.append(" ")
mbinfo.append("Counterparties chosen:")
Expand All @@ -746,8 +741,8 @@ def checkOffers(self, offers_fee, cjamount):
return False
mbinfo.append(k + ', ' + str(o['oid']) + ', ' + str(
display_fee))
mbinfo.append('Total coinjoin fee = ' + str(total_cj_fee) +
' satoshis, or ' + str(float('%.3g' % (
mbinfo.append('Total coinjoin fee = ' + btc.amount_to_str(total_cj_fee) +
', or ' + str(float('%.3g' % (
100.0 * total_fee_pc))) + '%')
title = 'Check Transaction'
if total_fee_pc * 100 > jm_single().config.getint("GUI",
Expand Down Expand Up @@ -842,7 +837,7 @@ def takerFinished(self, res, fromtx=False, waittime=0.0, txdetails=None):
def persistTxToHistory(self, addr, amt, txid):
#persist the transaction to history
with open(jm_single().config.get("GUI", "history_file"), 'ab') as f:
f.write((','.join([addr, satoshis_to_amt_str(amt), txid,
f.write((','.join([addr, btc.amount_to_btc_str(amt), txid,
datetime.datetime.now(
).strftime("%Y/%m/%d %H:%M:%S")])).encode('utf-8'))
f.write(b'\n') #TODO: Windows
Expand Down
7 changes: 3 additions & 4 deletions scripts/sendpayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from jmbase.support import get_log, set_logging_level, jmprint
from cli_options import get_sendpayment_parser, get_max_cj_fee_values, \
check_regtest
import jmbitcoin as btc

log = get_log()

Expand Down Expand Up @@ -64,9 +65,7 @@ def main():
#of a single transaction
sweeping = False
if options.schedule == '':
#note that sendpayment doesn't support fractional amounts, fractions throw
#here.
amount = int(args[1])
amount = btc.amount_to_sat(args[1])
if amount == 0:
sweeping = True
destaddr = args[2]
Expand Down Expand Up @@ -123,7 +122,7 @@ def main():
if not options.p2ep and not options.pickorders and options.makercount != 0:
maxcjfee = get_max_cj_fee_values(jm_single().config, options)
log.info("Using maximum coinjoin fee limits per maker of {:.4%}, {} "
"sat".format(*maxcjfee))
"".format(maxcjfee[0], btc.amount_to_str(maxcjfee[1])))

log.debug('starting sendpayment')

Expand Down

0 comments on commit b2e4308

Please sign in to comment.