Browse Source

Allow both BTC and sat amounts for single send / CJ

master
Kristaps Kaupe 6 years ago
parent
commit
b2e4308a90
No known key found for this signature in database
GPG Key ID: D47B1B4232B55437
  1. 1
      jmbitcoin/jmbitcoin/__init__.py
  2. 46
      jmbitcoin/jmbitcoin/amount.py
  3. 8
      jmclient/jmclient/taker_utils.py
  4. 18
      jmclient/jmclient/wallet_utils.py
  5. 23
      scripts/joinmarket-qt.py
  6. 7
      scripts/sendpayment.py

1
jmbitcoin/jmbitcoin/__init__.py

@ -7,4 +7,5 @@ from jmbitcoin.secp256k1_transaction import *
from jmbitcoin.secp256k1_deterministic import *
from jmbitcoin.btscript import *
from jmbitcoin.bech32 import *
from jmbitcoin.amount import *

46
jmbitcoin/jmbitcoin/amount.py

@ -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
jmclient/jmclient/taker_utils.py

@ -12,7 +12,7 @@ from .configure import jm_single, validate_address
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()
"""
@ -74,9 +74,9 @@ 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))
@ -84,7 +84,7 @@ def direct_send(wallet_service, amount, mixdepth, destaddr, answeryes=False,
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':

18
jmclient/jmclient/wallet_utils.py

@ -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")
@ -870,8 +866,8 @@ def wallet_fetch_history(wallet, options):
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:
@ -879,7 +875,7 @@ def wallet_fetch_history(wallet, options):
'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 ''

23
scripts/joinmarket-qt.py

@ -27,7 +27,6 @@ import sys, datetime, os, logging
import platform, json, threading, time
import qrcode
from decimal import Decimal
from PySide2 import QtCore
from PySide2.QtGui import *
@ -83,8 +82,6 @@ from qtsupport import ScheduleWizard, TumbleRestartWizard, config_tips,\
from twisted.internet import task
def satoshis_to_amt_str(x):
return str(Decimal(x)/Decimal('1e8')) + " BTC"
log = get_log()
@ -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]
@ -591,9 +588,9 @@ class SpendTab(QWidget):
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")
@ -614,7 +611,7 @@ class SpendTab(QWidget):
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:
@ -724,11 +721,9 @@ class SpendTab(QWidget):
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:")
@ -746,8 +741,8 @@ class SpendTab(QWidget):
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",
@ -842,7 +837,7 @@ class SpendTab(QWidget):
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

7
scripts/sendpayment.py

@ -22,6 +22,7 @@ from twisted.python.log import startLogging
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()
@ -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]
@ -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')

Loading…
Cancel
Save