From 94d7b45399af020268745043d5716d7e1c85fe8e Mon Sep 17 00:00:00 2001 From: Adam Gibson Date: Fri, 14 Jul 2017 14:46:47 +0300 Subject: [PATCH] update Taker code to use segwit wallet --- jmclient/jmclient/blockchaininterface.py | 23 +++++++++----- jmclient/jmclient/configure.py | 1 - jmclient/jmclient/support.py | 8 ++--- jmclient/jmclient/taker.py | 39 +++++++++++++++++++----- jmdaemon/jmdaemon/orderbookwatch.py | 2 +- jmdaemon/jmdaemon/protocol.py | 4 +++ scripts/joinmarket-qt.py | 4 +-- 7 files changed, 58 insertions(+), 23 deletions(-) diff --git a/jmclient/jmclient/blockchaininterface.py b/jmclient/jmclient/blockchaininterface.py index 4f5df43..f6faed1 100644 --- a/jmclient/jmclient/blockchaininterface.py +++ b/jmclient/jmclient/blockchaininterface.py @@ -68,7 +68,8 @@ class BlockchainInterface(object): unconfirmfun, confirmfun, notifyaddr, - timeoutfun=None): + timeoutfun=None, + vb=None): """ Invokes unconfirmfun and confirmfun when tx is seen on the network If timeoutfun not None, called with boolean argument that tells @@ -242,7 +243,9 @@ class BlockchaininfoInterface(BlockchainInterface): log.info('blockchaininfo sync_unspent took ' + str((self.last_sync_unspent - st)) + 'sec') - def add_tx_notify(self, txd, unconfirmfun, confirmfun, notifyaddr): + def add_tx_notify(self, txd, unconfirmfun, confirmfun, notifyaddr, vb=None): + if not vb: + vb = get_p2pk_vbyte() unconfirm_timeout = 10 * 60 # seconds unconfirm_poll_period = 15 confirm_timeout = 2 * 60 * 60 @@ -258,7 +261,7 @@ class BlockchaininfoInterface(BlockchainInterface): self.tx_output_set = set([(sv['script'], sv['value']) for sv in txd['outs']]) self.output_addresses = [ - btc.script_to_address(scrval[0], get_p2pk_vbyte()) + btc.script_to_address(scrval[0], vb) for scrval in self.tx_output_set] log.debug('txoutset=' + pprint.pformat(self.tx_output_set)) log.debug('outaddrs=' + ','.join(self.output_addresses)) @@ -615,7 +618,10 @@ class BlockrInterface(BlockchainInterface): #pragma: no cover unconfirmfun, confirmfun, notifyaddr, - timeoutfun=None): + timeoutfun=None, + vb=None): + if not vb: + vb = get_p2pk_vbyte() unconfirm_timeout = jm_single().config.getint('TIMEOUT', 'unconfirm_timeout_sec') unconfirm_poll_period = 5 @@ -636,7 +642,7 @@ class BlockrInterface(BlockchainInterface): #pragma: no cover self.tx_output_set = set([(sv['script'], sv['value']) for sv in txd['outs']]) self.output_addresses = [ - btc.script_to_address(scrval[0], get_p2pk_vbyte()) + btc.script_to_address(scrval[0], vb) for scrval in self.tx_output_set ] log.debug('txoutset=' + pprint.pformat(self.tx_output_set)) @@ -1222,13 +1228,16 @@ class BitcoinCoreInterface(BlockchainInterface): unconfirmfun, confirmfun, notifyaddr, - timeoutfun=None): + timeoutfun=None, + vb=None): + if not vb: + vb = get_p2pk_vbyte() if not self.notifythread: self.notifythread = BitcoinCoreNotifyThread(self) self.notifythread.start() one_addr_imported = False for outs in txd['outs']: - addr = btc.script_to_address(outs['script'], get_p2pk_vbyte()) + addr = btc.script_to_address(outs['script'], vb) if self.rpc('getaccount', [addr]) != '': one_addr_imported = True break diff --git a/jmclient/jmclient/configure.py b/jmclient/jmclient/configure.py index fe7d561..b906fca 100644 --- a/jmclient/jmclient/configure.py +++ b/jmclient/jmclient/configure.py @@ -66,7 +66,6 @@ global_singleton.DUST_THRESHOLD = 10 * global_singleton.BITCOIN_DUST_THRESHOLD global_singleton.bc_interface = None global_singleton.maker_timeout_sec = 60 global_singleton.debug_file_lock = threading.Lock() -global_singleton.ordername_list = ["reloffer", "absoffer"] global_singleton.debug_file_handle = None global_singleton.blacklist_file_lock = threading.Lock() global_singleton.core_alert = core_alert diff --git a/jmclient/jmclient/support.py b/jmclient/jmclient/support.py index 5666b65..2a85482 100644 --- a/jmclient/jmclient/support.py +++ b/jmclient/jmclient/support.py @@ -157,9 +157,9 @@ def select_greediest(unspent, value): def calc_cj_fee(ordertype, cjfee, cj_amount): - if ordertype == 'absoffer': + if ordertype in ['swabsoffer', 'absoffer']: real_cjfee = int(cjfee) - elif ordertype == 'reloffer': + elif ordertype in ['swreloffer', 'reloffer']: real_cjfee = int((Decimal(cjfee) * Decimal(cj_amount)).quantize(Decimal( 1))) else: @@ -282,9 +282,9 @@ def choose_sweep_orders(offers, sumtxfee_contribution = 0 for order in ordercombo: sumtxfee_contribution += order['txfee'] - if order['ordertype'] == 'absoffer': + if order['ordertype'] in ['swabsoffer', 'absoffer']: sumabsfee += int(order['cjfee']) - elif order['ordertype'] == 'reloffer': + elif order['ordertype'] in ['swreloffer', 'reloffer']: sumrelfee += Decimal(order['cjfee']) #this is unreachable since calc_cj_fee must already have been called else: #pragma: no cover diff --git a/jmclient/jmclient/taker.py b/jmclient/jmclient/taker.py index f65e1c1..8b42372 100644 --- a/jmclient/jmclient/taker.py +++ b/jmclient/jmclient/taker.py @@ -9,7 +9,7 @@ import time import copy import btc -from jmclient.configure import jm_single, get_p2pk_vbyte, donation_address +from jmclient.configure import jm_single, get_p2pk_vbyte, get_p2sh_vbyte from jmbase.support import get_log from jmclient.support import (calc_cj_fee, weighted_order_choose, choose_orders, choose_sweep_orders) @@ -237,7 +237,8 @@ class Taker(object): jlog.debug("Estimated ins: "+str(est_ins)) est_outs = 2*self.n_counterparties + 1 jlog.debug("Estimated outs: "+str(est_outs)) - estimated_fee = estimate_tx_fee(est_ins, est_outs) + estimated_fee = estimate_tx_fee(est_ins, est_outs, + txtype=self.wallet.get_txtype()) jlog.info("We have a fee estimate: "+str(estimated_fee)) jlog.info("And a requested fee of: "+str( self.txfee_default * self.n_counterparties)) @@ -451,11 +452,31 @@ class Taker(object): for i, u in utxo.iteritems(): if utxo_data[i] is None: continue + #Check if the sender serialize_scripted the witness + #item into the sig message; if so, also pick up the amount + #from the utxo data retrieved from the blockchain to verify + #the segwit-style signature. Note that this allows a mixed + #SW/non-SW transaction as each utxo is interpreted separately. + sig_deserialized = btc.deserialize_script(sig) + if len(sig_deserialized) == 2: + ver_sig, ver_pub = sig_deserialized + wit = None + elif len(sig_deserialized) == 3: + ver_sig, ver_pub, wit = sig_deserialized + else: + jlog.debug("Invalid signature message - more than 3 items") + break + ver_amt = utxo_data[i]['value'] if wit else None sig_good = btc.verify_tx_input(txhex, u[0], utxo_data[i]['script'], - *btc.deserialize_script(sig)) + ver_sig, ver_pub, witness=wit, + amount=ver_amt) if sig_good: jlog.debug('found good sig at index=%d' % (u[0])) - self.latest_tx['ins'][u[0]]['script'] = sig + if wit: + self.latest_tx["ins"][u[0]]["txinwitness"] = [ver_sig, ver_pub] + self.latest_tx["ins"][u[0]]["script"] = "16" + wit + else: + self.latest_tx["ins"][u[0]]["script"] = sig inserted_sig = True # check if maker has sent everything possible self.utxos[nick].remove(u[1]) @@ -593,9 +614,9 @@ class Taker(object): #Note: donation code removed (possibly temporarily) raise NotImplementedError - def sign_tx(self, tx, i, priv): + def sign_tx(self, tx, i, priv, amount): if self.my_cj_addr: - return btc.sign(tx, i, priv) + return self.wallet.sign(tx, i, priv, amount) else: #Note: donation code removed (possibly temporarily) raise NotImplementedError @@ -620,7 +641,9 @@ class Taker(object): if utxo not in self.input_utxos.keys(): continue addr = self.input_utxos[utxo]['address'] - tx = self.sign_tx(tx, index, self.wallet.get_key_from_addr(addr)) + amount = self.input_utxos[utxo]["value"] + tx = self.sign_tx(tx, index, self.wallet.get_key_from_addr(addr), + amount) self.latest_tx = btc.deserialize(tx) def push(self): @@ -652,7 +675,7 @@ class Taker(object): else: jm_single().bc_interface.add_tx_notify( self.latest_tx, self.unconfirm_callback, - self.confirm_callback, self.my_cj_addr) + self.confirm_callback, self.my_cj_addr, vb=get_p2sh_vbyte()) if nick_to_use: return (nick_to_use, tx) #if push was not successful, return None diff --git a/jmdaemon/jmdaemon/orderbookwatch.py b/jmdaemon/jmdaemon/orderbookwatch.py index d1a51f6..fae9380 100644 --- a/jmdaemon/jmdaemon/orderbookwatch.py +++ b/jmdaemon/jmdaemon/orderbookwatch.py @@ -95,7 +95,7 @@ class OrderbookWatch(object): "from {}").format log.debug(fmt(minsize, maxsize, counterparty)) return - if ordertype == 'absoffer' and not isinstance(cjfee, int): + if ordertype in ['swabsoffer', 'absoffer'] and not isinstance(cjfee, int): try: cjfee = int(cjfee) except ValueError: diff --git a/jmdaemon/jmdaemon/protocol.py b/jmdaemon/jmdaemon/protocol.py index e94cf14..f7f71b8 100644 --- a/jmdaemon/jmdaemon/protocol.py +++ b/jmdaemon/jmdaemon/protocol.py @@ -8,6 +8,10 @@ separator = " " offertypes = {"reloffer": [(int, "oid"), (int, "minsize"), (int, "maxsize"), (int, "txfee"), (float, "cjfee")], "absoffer": [(int, "oid"), (int, "minsize"), (int, "maxsize"), + (int, "txfee"), (int, "cjfee")], + "swreloffer": [(int, "oid"), (int, "minsize"), (int, "maxsize"), + (int, "txfee"), (float, "cjfee")], + "swabsoffer": [(int, "oid"), (int, "minsize"), (int, "maxsize"), (int, "txfee"), (int, "cjfee")]} offername_list = offertypes.keys() diff --git a/scripts/joinmarket-qt.py b/scripts/joinmarket-qt.py index d7b3e00..398c5b2 100644 --- a/scripts/joinmarket-qt.py +++ b/scripts/joinmarket-qt.py @@ -790,10 +790,10 @@ class SpendTab(QWidget): mbinfo.append("Counterparties chosen:") mbinfo.append('Name, Order id, Coinjoin fee (sat.)') for k, o in offers.iteritems(): - if o['ordertype'] == 'reloffer': + if o['ordertype'] in ['swreloffer', 'reloffer']: display_fee = int(self.taker.cjamount * float(o['cjfee'])) - int(o['txfee']) - elif o['ordertype'] == 'absoffer': + elif o['ordertype'] in ['swabsoffer', 'absoffer']: display_fee = int(o['cjfee']) - int(o['txfee']) else: log.debug("Unsupported order type: " + str(o['ordertype']) +