Browse Source

move client/server data encoding to twisted

master
undeath 5 years ago committed by Adam Gibson
parent
commit
7dcd3f3b5a
  1. 10
      jmbase/jmbase/arguments.py
  2. 46
      jmbase/jmbase/commands.py
  3. 20
      jmbase/test/test_commands.py
  4. 61
      jmclient/jmclient/client_protocol.py
  5. 6
      jmclient/jmclient/maker.py
  6. 18
      jmclient/test/test_client_protocol.py
  7. 76
      jmdaemon/jmdaemon/daemon_protocol.py
  8. 8
      jmdaemon/jmdaemon/message_channel.py
  9. 25
      jmdaemon/test/test_daemon_protocol.py
  10. 8
      test/ygrunner.py

10
jmbase/jmbase/arguments.py

@ -0,0 +1,10 @@
import json
from twisted.protocols.amp import String
class JsonEncodable(String):
def toString(self, inObject):
return super().toString(json.dumps(inObject).encode('ascii'))
def fromString(self, inString):
return super().fromString(json.loads(inString))

46
jmbase/jmbase/commands.py

@ -3,8 +3,10 @@ Commands defining client-server (daemon)
messaging protocol (*not* Joinmarket p2p protocol). messaging protocol (*not* Joinmarket p2p protocol).
Used for AMP asynchronous messages. Used for AMP asynchronous messages.
""" """
from twisted.protocols.amp import Boolean, Command, Integer, Unicode from twisted.protocols.amp import Boolean, Command, Integer, Unicode, ListOf,\
String
from .bigstring import BigUnicode from .bigstring import BigUnicode
from .arguments import JsonEncodable
class DaemonNotReady(Exception): class DaemonNotReady(Exception):
@ -29,7 +31,7 @@ class JMInit(JMCommand):
""" """
arguments = [(b'bcsource', Unicode()), arguments = [(b'bcsource', Unicode()),
(b'network', Unicode()), (b'network', Unicode()),
(b'irc_configs', Unicode()), (b'irc_configs', JsonEncodable()),
(b'minmakers', Integer()), (b'minmakers', Integer()),
(b'maker_timeout_sec', Integer()), (b'maker_timeout_sec', Integer()),
(b'dust_threshold', Integer())] (b'dust_threshold', Integer())]
@ -47,7 +49,7 @@ class JMSetup(JMCommand):
role, passes initial offers for announcement (for TAKER, this data is "none") role, passes initial offers for announcement (for TAKER, this data is "none")
""" """
arguments = [(b'role', Unicode()), arguments = [(b'role', Unicode()),
(b'offers', Unicode()), (b'initdata', JsonEncodable()),
(b'use_fidelity_bond', Boolean())] (b'use_fidelity_bond', Boolean())]
class JMMsgSignature(JMCommand): class JMMsgSignature(JMCommand):
@ -82,13 +84,13 @@ class JMFill(JMCommand):
arguments = [(b'amount', Integer()), arguments = [(b'amount', Integer()),
(b'commitment', Unicode()), (b'commitment', Unicode()),
(b'revelation', Unicode()), (b'revelation', Unicode()),
(b'filled_offers', Unicode())] (b'filled_offers', JsonEncodable())]
class JMMakeTx(JMCommand): class JMMakeTx(JMCommand):
"""Send a hex encoded raw bitcoin transaction """Send a hex encoded raw bitcoin transaction
to a set of counterparties to a set of counterparties
""" """
arguments = [(b'nick_list', Unicode()), arguments = [(b'nick_list', ListOf(Unicode())),
(b'txhex', Unicode())] (b'txhex', Unicode())]
class JMPushTx(JMCommand): class JMPushTx(JMCommand):
@ -106,9 +108,9 @@ class JMAnnounceOffers(JMCommand):
to the daemon, along with new announcement to the daemon, along with new announcement
and cancellation lists (deltas). and cancellation lists (deltas).
""" """
arguments = [(b'to_announce', Unicode()), arguments = [(b'to_announce', JsonEncodable()),
(b'to_cancel', Unicode()), (b'to_cancel', JsonEncodable()),
(b'offerlist', Unicode())] (b'offerlist', JsonEncodable())]
class JMFidelityBondProof(JMCommand): class JMFidelityBondProof(JMCommand):
"""Send requested fidelity bond proof message""" """Send requested fidelity bond proof message"""
@ -120,7 +122,7 @@ class JMIOAuth(JMCommand):
verifying Taker's auth message verifying Taker's auth message
""" """
arguments = [(b'nick', Unicode()), arguments = [(b'nick', Unicode()),
(b'utxolist', Unicode()), (b'utxolist', JsonEncodable()),
(b'pubkey', Unicode()), (b'pubkey', Unicode()),
(b'cjaddr', Unicode()), (b'cjaddr', Unicode()),
(b'changeaddr', Unicode()), (b'changeaddr', Unicode()),
@ -131,7 +133,7 @@ class JMTXSigs(JMCommand):
sent by TAKER sent by TAKER
""" """
arguments = [(b'nick', Unicode()), arguments = [(b'nick', Unicode()),
(b'sigs', Unicode())] (b'sigs', ListOf(Unicode()))]
"""COMMANDS FROM DAEMON TO CLIENT """COMMANDS FROM DAEMON TO CLIENT
================================= =================================
@ -197,7 +199,7 @@ class JMFillResponse(JMCommand):
"""Returns ioauth data from MAKER if successful. """Returns ioauth data from MAKER if successful.
""" """
arguments = [(b'success', Boolean()), arguments = [(b'success', Boolean()),
(b'ioauth_data', Unicode())] (b'ioauth_data', JsonEncodable())]
class JMSigReceived(JMCommand): class JMSigReceived(JMCommand):
"""Returns an individual bitcoin transaction signature """Returns an individual bitcoin transaction signature
@ -221,9 +223,9 @@ class JMAuthReceived(JMCommand):
before setting up encryption and continuing. before setting up encryption and continuing.
""" """
arguments = [(b'nick', Unicode()), arguments = [(b'nick', Unicode()),
(b'offer', Unicode()), (b'offer', JsonEncodable()),
(b'commitment', Unicode()), (b'commitment', Unicode()),
(b'revelation', Unicode()), (b'revelation', JsonEncodable()),
(b'amount', Integer()), (b'amount', Integer()),
(b'kphex', Unicode())] (b'kphex', Unicode())]
@ -232,8 +234,8 @@ class JMTXReceived(JMCommand):
by TAKER, along with offerdata to verify fees. by TAKER, along with offerdata to verify fees.
""" """
arguments = [(b'nick', Unicode()), arguments = [(b'nick', Unicode()),
(b'txhex', Unicode()), (b'tx', String()),
(b'offer', Unicode())] (b'offer', JsonEncodable())]
class JMTXBroadcast(JMCommand): class JMTXBroadcast(JMCommand):
""" Accept a bitcoin transaction """ Accept a bitcoin transaction
@ -241,7 +243,7 @@ class JMTXBroadcast(JMCommand):
and relay it to the client for network and relay it to the client for network
broadcast. broadcast.
""" """
arguments = [(b'txhex', Unicode())] arguments = [(b'tx', String())]
"""SNICKER related commands. """SNICKER related commands.
""" """
@ -251,12 +253,12 @@ class SNICKERReceiverInit(JMCommand):
See documentation of `netconfig` in See documentation of `netconfig` in
jmdaemon.HTTPPassThrough.on_INIT jmdaemon.HTTPPassThrough.on_INIT
""" """
arguments = [(b'netconfig', Unicode())] arguments = [(b'netconfig', JsonEncodable())]
class SNICKERProposerInit(JMCommand): class SNICKERProposerInit(JMCommand):
""" As for receiver. """ As for receiver.
""" """
arguments = [(b'netconfig', Unicode())] arguments = [(b'netconfig', JsonEncodable())]
class SNICKERReceiverUp(JMCommand): class SNICKERReceiverUp(JMCommand):
arguments = [] arguments = []
@ -307,7 +309,7 @@ class BIP78SenderInit(JMCommand):
See documentation of `netconfig` in See documentation of `netconfig` in
jmdaemon.HTTPPassThrough.on_INIT jmdaemon.HTTPPassThrough.on_INIT
""" """
arguments = [(b'netconfig', Unicode())] arguments = [(b'netconfig', JsonEncodable())]
class BIP78SenderUp(JMCommand): class BIP78SenderUp(JMCommand):
arguments = [] arguments = []
@ -319,7 +321,7 @@ class BIP78SenderOriginalPSBT(JMCommand):
to be sent as an http request to the receiver. to be sent as an http request to the receiver.
""" """
arguments = [(b'body', BigUnicode()), arguments = [(b'body', BigUnicode()),
(b'params', Unicode())] (b'params', JsonEncodable())]
class BIP78SenderReceiveProposal(JMCommand): class BIP78SenderReceiveProposal(JMCommand):
""" Sends the payjoin proposal PSBT, received """ Sends the payjoin proposal PSBT, received
@ -341,7 +343,7 @@ class BIP78SenderReceiveError(JMCommand):
class BIP78ReceiverInit(JMCommand): class BIP78ReceiverInit(JMCommand):
""" Initialization data for a BIP78 hidden service. """ Initialization data for a BIP78 hidden service.
""" """
arguments = [(b'netconfig', Unicode())] arguments = [(b'netconfig', JsonEncodable())]
class BIP78ReceiverUp(JMCommand): class BIP78ReceiverUp(JMCommand):
""" Returns onion hostname to client when """ Returns onion hostname to client when
@ -356,7 +358,7 @@ class BIP78ReceiverOriginalPSBT(JMCommand):
parameters in the url, from the daemon to the client. parameters in the url, from the daemon to the client.
""" """
arguments = [(b'body', BigUnicode()), arguments = [(b'body', BigUnicode()),
(b'params', Unicode())] (b'params', JsonEncodable())]
class BIP78ReceiverSendProposal(JMCommand): class BIP78ReceiverSendProposal(JMCommand):
""" Receives a payjoin proposal PSBT from """ Receives a payjoin proposal PSBT from

20
jmbase/test/test_commands.py

@ -63,8 +63,8 @@ class JMTestServerProtocol(JMBaseProtocol):
return {'accepted': True} return {'accepted': True}
@JMSetup.responder @JMSetup.responder
def on_JM_SETUP(self, role, offers, use_fidelity_bond): def on_JM_SETUP(self, role, initdata, use_fidelity_bond):
show_receipt("JMSETUP", role, offers, use_fidelity_bond) show_receipt("JMSETUP", role, initdata, use_fidelity_bond)
d = self.callRemote(JMSetupDone) d = self.callRemote(JMSetupDone)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}
@ -85,7 +85,7 @@ class JMTestServerProtocol(JMBaseProtocol):
show_receipt("JMFILL", amount, commitment, revelation, filled_offers) show_receipt("JMFILL", amount, commitment, revelation, filled_offers)
d = self.callRemote(JMFillResponse, d = self.callRemote(JMFillResponse,
success=True, success=True,
ioauth_data = json.dumps(['dummy', 'list'])) ioauth_data=['dummy', 'list'])
return {'accepted': True} return {'accepted': True}
@JMMakeTx.responder @JMMakeTx.responder
@ -113,7 +113,7 @@ class JMTestServerProtocol(JMBaseProtocol):
max_encoded=5, max_encoded=5,
hostid="hostid2") hostid="hostid2")
self.defaultCallbacks(d3) self.defaultCallbacks(d3)
d4 = self.callRemote(JMTXBroadcast, txhex="deadbeef") d4 = self.callRemote(JMTXBroadcast, tx=b"deadbeef")
self.defaultCallbacks(d4) self.defaultCallbacks(d4)
return {'accepted': True} return {'accepted': True}
@ -137,7 +137,7 @@ class JMTestClientProtocol(JMBaseProtocol):
d = self.callRemote(JMInit, d = self.callRemote(JMInit,
bcsource="dummyblockchain", bcsource="dummyblockchain",
network="dummynetwork", network="dummynetwork",
irc_configs=json.dumps(['dummy', 'irc', 'config']), irc_configs=['dummy', 'irc', 'config'],
minmakers=7, minmakers=7,
maker_timeout_sec=8, maker_timeout_sec=8,
dust_threshold=1500) dust_threshold=1500)
@ -158,7 +158,7 @@ class JMTestClientProtocol(JMBaseProtocol):
show_receipt("JMUP") show_receipt("JMUP")
d = self.callRemote(JMSetup, d = self.callRemote(JMSetup,
role="TAKER", role="TAKER",
offers="{}", initdata=None,
use_fidelity_bond=False) use_fidelity_bond=False)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}
@ -174,7 +174,7 @@ class JMTestClientProtocol(JMBaseProtocol):
def on_JM_FILL_RESPONSE(self, success, ioauth_data): def on_JM_FILL_RESPONSE(self, success, ioauth_data):
show_receipt("JMFILLRESPONSE", success, ioauth_data) show_receipt("JMFILLRESPONSE", success, ioauth_data)
d = self.callRemote(JMMakeTx, d = self.callRemote(JMMakeTx,
nick_list= json.dumps(['nick1', 'nick2', 'nick3']), nick_list=['nick1', 'nick2', 'nick3'],
txhex="deadbeef") txhex="deadbeef")
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}
@ -186,7 +186,7 @@ class JMTestClientProtocol(JMBaseProtocol):
amount=100, amount=100,
commitment="dummycommitment", commitment="dummycommitment",
revelation="dummyrevelation", revelation="dummyrevelation",
filled_offers=json.dumps(['list', 'of', 'filled', 'offers'])) filled_offers=['list', 'of', 'filled', 'offers'])
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}
@ -222,8 +222,8 @@ class JMTestClientProtocol(JMBaseProtocol):
return {'accepted': True} return {'accepted': True}
@JMTXBroadcast.responder @JMTXBroadcast.responder
def on_JM_TX_BROADCAST(self, txhex): def on_JM_TX_BROADCAST(self, tx):
show_receipt("JMTXBROADCAST", txhex) show_receipt("JMTXBROADCAST", tx)
return {"accepted": True} return {"accepted": True}
class JMTestClientProtocolFactory(protocol.ClientFactory): class JMTestClientProtocolFactory(protocol.ClientFactory):

61
jmclient/jmclient/client_protocol.py

@ -73,13 +73,13 @@ class BIP78ClientProtocol(BaseClientProtocol):
"tls_whitelist": ",".join(self.tls_whitelist), "tls_whitelist": ",".join(self.tls_whitelist),
"servers": [self.manager.server]} "servers": [self.manager.server]}
d = self.callRemote(commands.BIP78SenderInit, d = self.callRemote(commands.BIP78SenderInit,
netconfig=json.dumps(netconfig)) netconfig=netconfig)
else: else:
netconfig = {"port": 80, netconfig = {"port": 80,
"tor_control_host": jcg("PAYJOIN", "tor_control_host"), "tor_control_host": jcg("PAYJOIN", "tor_control_host"),
"tor_control_port": jcg("PAYJOIN", "tor_control_port")} "tor_control_port": jcg("PAYJOIN", "tor_control_port")}
d = self.callRemote(commands.BIP78ReceiverInit, d = self.callRemote(commands.BIP78ReceiverInit,
netconfig=json.dumps(netconfig)) netconfig=netconfig)
self.defaultCallbacks(d) self.defaultCallbacks(d)
@commands.BIP78ReceiverUp.responder @commands.BIP78ReceiverUp.responder
@ -89,7 +89,6 @@ class BIP78ClientProtocol(BaseClientProtocol):
@commands.BIP78ReceiverOriginalPSBT.responder @commands.BIP78ReceiverOriginalPSBT.responder
def on_BIP78_RECEIVER_ORIGINAL_PSBT(self, body, params): def on_BIP78_RECEIVER_ORIGINAL_PSBT(self, body, params):
params = json.loads(params)
# TODO: we don't need binary key/vals client side, but will have to edit # TODO: we don't need binary key/vals client side, but will have to edit
# PayjoinConverter for that: # PayjoinConverter for that:
retval = self.success_callback(body.encode("utf-8"), bdict_sdict_convert( retval = self.success_callback(body.encode("utf-8"), bdict_sdict_convert(
@ -121,7 +120,7 @@ class BIP78ClientProtocol(BaseClientProtocol):
def on_BIP78_SENDER_UP(self): def on_BIP78_SENDER_UP(self):
d = self.callRemote(commands.BIP78SenderOriginalPSBT, d = self.callRemote(commands.BIP78SenderOriginalPSBT,
body=self.manager.initial_psbt.to_base64(), body=self.manager.initial_psbt.to_base64(),
params=json.dumps(self.params)) params=self.params)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {"accepted": True} return {"accepted": True}
@ -168,10 +167,10 @@ class SNICKERClientProtocol(BaseClientProtocol):
if isinstance(self.client, SNICKERReceiver): if isinstance(self.client, SNICKERReceiver):
d = self.callRemote(commands.SNICKERReceiverInit, d = self.callRemote(commands.SNICKERReceiverInit,
netconfig=json.dumps(netconfig)) netconfig=netconfig)
else: else:
d = self.callRemote(commands.SNICKERProposerInit, d = self.callRemote(commands.SNICKERProposerInit,
netconfig=json.dumps(netconfig)) netconfig=netconfig)
self.defaultCallbacks(d) self.defaultCallbacks(d)
def shutdown(self): def shutdown(self):
@ -363,7 +362,7 @@ class JMClientProtocol(BaseClientProtocol):
def make_tx(self, nick_list, txhex): def make_tx(self, nick_list, txhex):
d = self.callRemote(commands.JMMakeTx, d = self.callRemote(commands.JMMakeTx,
nick_list= json.dumps(nick_list), nick_list=nick_list,
txhex=txhex) txhex=txhex)
self.defaultCallbacks(d) self.defaultCallbacks(d)
@ -394,7 +393,7 @@ class JMMakerClientProtocol(JMClientProtocol):
self.offers_ready_loop.stop() self.offers_ready_loop.stop()
d = self.callRemote(commands.JMSetup, d = self.callRemote(commands.JMSetup,
role="MAKER", role="MAKER",
offers=json.dumps(self.client.offerlist), initdata=self.client.offerlist,
use_fidelity_bond=(self.client.fidelity_bond is not None)) use_fidelity_bond=(self.client.fidelity_bond is not None))
self.defaultCallbacks(d) self.defaultCallbacks(d)
@ -423,7 +422,7 @@ class JMMakerClientProtocol(JMClientProtocol):
d = self.callRemote(commands.JMInit, d = self.callRemote(commands.JMInit,
bcsource=blockchain_source, bcsource=blockchain_source,
network=network, network=network,
irc_configs=json.dumps(irc_configs), irc_configs=irc_configs,
minmakers=minmakers, minmakers=minmakers,
maker_timeout_sec=maker_timeout_sec, maker_timeout_sec=maker_timeout_sec,
dust_threshold=jm_single().DUST_THRESHOLD) dust_threshold=jm_single().DUST_THRESHOLD)
@ -443,8 +442,6 @@ class JMMakerClientProtocol(JMClientProtocol):
@commands.JMAuthReceived.responder @commands.JMAuthReceived.responder
def on_JM_AUTH_RECEIVED(self, nick, offer, commitment, revelation, amount, def on_JM_AUTH_RECEIVED(self, nick, offer, commitment, revelation, amount,
kphex): kphex):
offer = json.loads(offer)
revelation = json.loads(revelation)
retval = self.client.on_auth_received(nick, offer, retval = self.client.on_auth_received(nick, offer,
commitment, revelation, amount, kphex) commitment, revelation, amount, kphex)
if not retval[0]: if not retval[0]:
@ -461,7 +458,7 @@ class JMMakerClientProtocol(JMClientProtocol):
auth_pub_hex = bintohex(auth_pub) auth_pub_hex = bintohex(auth_pub)
d = self.callRemote(commands.JMIOAuth, d = self.callRemote(commands.JMIOAuth,
nick=nick, nick=nick,
utxolist=json.dumps(utxos_strkeyed), utxolist=utxos_strkeyed,
pubkey=auth_pub_hex, pubkey=auth_pub_hex,
cjaddr=cj_addr, cjaddr=cj_addr,
changeaddr=change_addr, changeaddr=change_addr,
@ -470,17 +467,15 @@ class JMMakerClientProtocol(JMClientProtocol):
return {"accepted": True} return {"accepted": True}
@commands.JMTXReceived.responder @commands.JMTXReceived.responder
def on_JM_TX_RECEIVED(self, nick, txhex, offer): def on_JM_TX_RECEIVED(self, nick, tx, offer):
offer = json.loads(offer) retval = self.client.on_tx_received(nick, tx, offer)
retval = self.client.on_tx_received(nick, txhex, offer)
if not retval[0]: if not retval[0]:
jlog.info("Maker refuses to continue on receipt of tx") jlog.info("Maker refuses to continue on receipt of tx")
else: else:
sigs = retval[1] sigs = retval[1]
self.finalized_offers[nick] = offer self.finalized_offers[nick] = offer
tx = btc.CMutableTransaction.deserialize(hextobin(txhex)) tx = btc.CMutableTransaction.deserialize(tx)
self.finalized_offers[nick]["txd"] = tx self.finalized_offers[nick]["txd"] = tx
txid = tx.GetTxid()[::-1]
# we index the callback by the out-set of the transaction, # we index the callback by the out-set of the transaction,
# because the txid is not known until all scriptSigs collected # because the txid is not known until all scriptSigs collected
# (hence this is required for Makers, but not Takers). # (hence this is required for Makers, but not Takers).
@ -497,14 +492,12 @@ class JMMakerClientProtocol(JMClientProtocol):
txinfo, self.unconfirm_callback, "unconfirmed", txinfo, self.unconfirm_callback, "unconfirmed",
"transaction with outputs: " + str(txinfo) + " not broadcast.") "transaction with outputs: " + str(txinfo) + " not broadcast.")
d = self.callRemote(commands.JMTXSigs, d = self.callRemote(commands.JMTXSigs, nick=nick, sigs=sigs)
nick=nick,
sigs=json.dumps(sigs))
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {"accepted": True} return {"accepted": True}
@commands.JMTXBroadcast.responder @commands.JMTXBroadcast.responder
def on_JM_TX_BROADCAST(self, txhex): def on_JM_TX_BROADCAST(self, tx):
""" Makers have no issue broadcasting anything, """ Makers have no issue broadcasting anything,
so only need to prevent crashes. so only need to prevent crashes.
Note in particular we don't check the return value, Note in particular we don't check the return value,
@ -512,11 +505,10 @@ class JMMakerClientProtocol(JMClientProtocol):
our (maker)'s concern. our (maker)'s concern.
""" """
try: try:
txbin = hextobin(txhex) jm_single().bc_interface.pushtx(tx)
jm_single().bc_interface.pushtx(txbin)
except: except:
jlog.info("We received an invalid transaction broadcast " jlog.info("We received an invalid transaction broadcast "
"request: " + txhex) "request: " + tx.hex())
return {"accepted": True} return {"accepted": True}
def tx_match(self, txd): def tx_match(self, txd):
@ -547,9 +539,9 @@ class JMMakerClientProtocol(JMClientProtocol):
"transaction with outputs " + str(txinfo) + " not confirmed.") "transaction with outputs " + str(txinfo) + " not confirmed.")
d = self.callRemote(commands.JMAnnounceOffers, d = self.callRemote(commands.JMAnnounceOffers,
to_announce=json.dumps(to_announce), to_announce=to_announce,
to_cancel=json.dumps(to_cancel), to_cancel=to_cancel,
offerlist=json.dumps(self.client.offerlist)) offerlist=self.client.offerlist)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return True return True
@ -564,9 +556,9 @@ class JMMakerClientProtocol(JMClientProtocol):
txid, confirms) txid, confirms)
self.client.modify_orders(to_cancel, to_announce) self.client.modify_orders(to_cancel, to_announce)
d = self.callRemote(commands.JMAnnounceOffers, d = self.callRemote(commands.JMAnnounceOffers,
to_announce=json.dumps(to_announce), to_announce=to_announce,
to_cancel=json.dumps(to_cancel), to_cancel=to_cancel,
offerlist=json.dumps(self.client.offerlist)) offerlist=self.client.offerlist)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return True return True
@ -601,7 +593,7 @@ class JMTakerClientProtocol(JMClientProtocol):
d = self.callRemote(commands.JMInit, d = self.callRemote(commands.JMInit,
bcsource=blockchain_source, bcsource=blockchain_source,
network=network, network=network,
irc_configs=json.dumps(irc_configs), irc_configs=irc_configs,
minmakers=minmakers, minmakers=minmakers,
maker_timeout_sec=maker_timeout_sec, maker_timeout_sec=maker_timeout_sec,
dust_threshold=jm_single().DUST_THRESHOLD) dust_threshold=jm_single().DUST_THRESHOLD)
@ -641,7 +633,7 @@ class JMTakerClientProtocol(JMClientProtocol):
def on_JM_UP(self): def on_JM_UP(self):
d = self.callRemote(commands.JMSetup, d = self.callRemote(commands.JMSetup,
role="TAKER", role="TAKER",
offers="{}", initdata=None,
use_fidelity_bond=False) use_fidelity_bond=False)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}
@ -669,13 +661,12 @@ class JMTakerClientProtocol(JMClientProtocol):
the ioauth data and returns the proposed the ioauth data and returns the proposed
transaction, passes the phase 2 initiating data to the daemon. transaction, passes the phase 2 initiating data to the daemon.
""" """
ioauth_data = json.loads(ioauth_data)
if not success: if not success:
jlog.info("Makers who didnt respond: " + str(ioauth_data)) jlog.info("Makers who didnt respond: " + str(ioauth_data))
self.client.add_ignored_makers(ioauth_data) self.client.add_ignored_makers(ioauth_data)
return {'accepted': True} return {'accepted': True}
else: else:
jlog.info("Makers responded with: " + json.dumps(ioauth_data)) jlog.info("Makers responded with: " + str(ioauth_data))
retval = self.client.receive_utxos(ioauth_data) retval = self.client.receive_utxos(ioauth_data)
if not retval[0]: if not retval[0]:
jlog.info("Taker is not continuing, phase 2 abandoned.") jlog.info("Taker is not continuing, phase 2 abandoned.")
@ -717,7 +708,7 @@ class JMTakerClientProtocol(JMClientProtocol):
amount=amt, amount=amt,
commitment=str(cmt), commitment=str(cmt),
revelation=str(rev), revelation=str(rev),
filled_offers=json.dumps(foffers)) filled_offers=foffers)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}

6
jmclient/jmclient/maker.py

@ -118,7 +118,7 @@ class Maker(object):
return (True, utxos, auth_pub, cj_addr, change_addr, btc_sig) return (True, utxos, auth_pub, cj_addr, change_addr, btc_sig)
@hexbin @hexbin
def on_tx_received(self, nick, tx_from_taker, offerinfo): def on_tx_received(self, nick, tx, offerinfo):
"""Called when the counterparty has sent an unsigned """Called when the counterparty has sent an unsigned
transaction. Sigs are created and returned if and only transaction. Sigs are created and returned if and only
if the transaction passes verification checks (see if the transaction passes verification checks (see
@ -129,9 +129,9 @@ class Maker(object):
if not isinstance(offerinfo["offer"]["cjfee"], str): if not isinstance(offerinfo["offer"]["cjfee"], str):
offerinfo["offer"]["cjfee"] = bintohex(offerinfo["offer"]["cjfee"]) offerinfo["offer"]["cjfee"] = bintohex(offerinfo["offer"]["cjfee"])
try: try:
tx = btc.CMutableTransaction.deserialize(tx_from_taker) tx = btc.CMutableTransaction.deserialize(tx)
except Exception as e: except Exception as e:
return (False, 'malformed txhex. ' + repr(e)) return (False, 'malformed tx. ' + repr(e))
# if the above deserialization was successful, the human readable # if the above deserialization was successful, the human readable
# parsing will be also: # parsing will be also:
jlog.info('obtained tx\n' + btc.human_readable_transaction(tx)) jlog.info('obtained tx\n' + btc.human_readable_transaction(tx))

18
jmclient/test/test_client_protocol.py

@ -20,6 +20,7 @@ from commontest import default_max_cj_fee
import json import json
import jmbitcoin as bitcoin import jmbitcoin as bitcoin
import twisted import twisted
import base64
twisted.internet.base.DelayedCall.debug = True twisted.internet.base.DelayedCall.debug = True
test_completed = False test_completed = False
@ -99,7 +100,7 @@ class DummyMaker(Maker):
# success, utxos, auth_pub, cj_addr, change_addr, btc_sig # success, utxos, auth_pub, cj_addr, change_addr, btc_sig
return True, [], b"", '', '', '' return True, [], b"", '', '', ''
def on_tx_received(self, nick, txhex, offerinfo): def on_tx_received(self, nick, tx, offerinfo):
# success, sigs # success, sigs
return True, [] return True, []
@ -185,8 +186,8 @@ class JMTestServerProtocol(JMBaseProtocol):
return {'accepted': True} return {'accepted': True}
@JMSetup.responder @JMSetup.responder
def on_JM_SETUP(self, role, offers, use_fidelity_bond): def on_JM_SETUP(self, role, initdata, use_fidelity_bond):
show_receipt("JMSETUP", role, offers, use_fidelity_bond) show_receipt("JMSETUP", role, initdata, use_fidelity_bond)
d = self.callRemote(JMSetupDone) d = self.callRemote(JMSetupDone)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}
@ -209,7 +210,7 @@ class JMTestServerProtocol(JMBaseProtocol):
show_receipt("JMFILL", amount, commitment, revelation, filled_offers) show_receipt("JMFILL", amount, commitment, revelation, filled_offers)
d = self.callRemote(JMFillResponse, d = self.callRemote(JMFillResponse,
success=success, success=success,
ioauth_data = json.dumps(['dummy', 'list'])) ioauth_data=['dummy', 'list'])
return {'accepted': True} return {'accepted': True}
@JMMakeTx.responder @JMMakeTx.responder
@ -390,13 +391,14 @@ class TestMakerClientProtocol(unittest.TestCase):
def test_JMAuthReceived(self): def test_JMAuthReceived(self):
yield self.init_client() yield self.init_client()
yield self.callClient( yield self.callClient(
JMAuthReceived, nick='testnick', offer='{}', JMAuthReceived, nick='testnick', offer={},
commitment='testcommitment', revelation='{}', amount=100000, commitment='testcommitment', revelation={}, amount=100000,
kphex='testkphex') kphex='testkphex')
@inlineCallbacks @inlineCallbacks
def test_JMTXReceived(self): def test_JMTXReceived(self):
yield self.init_client() yield self.init_client()
yield self.callClient( yield self.callClient(
JMTXReceived, nick='testnick', txhex=t_raw_signed_tx, JMTXReceived, nick='testnick',
offer='{"cjaddr":"2MwfecDHsQTm4Gg3RekQdpqAMR15BJrjfRF"}') tx=base64.b16decode(t_raw_signed_tx, casefold=True),
offer={"cjaddr":"2MwfecDHsQTm4Gg3RekQdpqAMR15BJrjfRF"})

76
jmdaemon/jmdaemon/daemon_protocol.py

@ -9,7 +9,7 @@ from .protocol import (COMMAND_PREFIX, ORDER_KEYS, NICK_HASH_LENGTH,
COMMITMENT_PREFIXES) COMMITMENT_PREFIXES)
from .irc import IRCMessageChannel from .irc import IRCMessageChannel
from jmbase import (hextobin, is_hs_uri, get_tor_agent, JMHiddenService, from jmbase import (is_hs_uri, get_tor_agent, JMHiddenService,
get_nontor_agent, BytesProducer, wrapped_urlparse, get_nontor_agent, BytesProducer, wrapped_urlparse,
bdict_sdict_convert, JMHTTPResource) bdict_sdict_convert, JMHTTPResource)
from jmbase.commands import * from jmbase.commands import *
@ -32,7 +32,6 @@ import os
from io import BytesIO from io import BytesIO
import copy import copy
from functools import wraps from functools import wraps
from numbers import Integral
"""Joinmarket application protocol control flow. """Joinmarket application protocol control flow.
For documentation on protocol (formats, message sequence) see For documentation on protocol (formats, message sequence) see
@ -174,7 +173,6 @@ class HTTPPassThrough(amp.AMP):
filterconfig (not yet defined) filterconfig (not yet defined)
credentials (not yet defined) credentials (not yet defined)
""" """
netconfig = json.loads(netconfig)
self.socks5_host = netconfig["socks5_host"] self.socks5_host = netconfig["socks5_host"]
self.socks5_port = int(netconfig["socks5_port"]) self.socks5_port = int(netconfig["socks5_port"])
self.servers = [a for a in netconfig["servers"] if a != ""] self.servers = [a for a in netconfig["servers"] if a != ""]
@ -272,7 +270,6 @@ class HTTPPassThrough(amp.AMP):
class BIP78ServerProtocol(HTTPPassThrough): class BIP78ServerProtocol(HTTPPassThrough):
@BIP78ReceiverInit.responder @BIP78ReceiverInit.responder
def on_BIP78_RECEIVER_INIT(self, netconfig): def on_BIP78_RECEIVER_INIT(self, netconfig):
netconfig = json.loads(netconfig)
self.serving_port = int(netconfig["port"]) self.serving_port = int(netconfig["port"])
self.tor_control_host = netconfig["tor_control_host"] self.tor_control_host = netconfig["tor_control_host"]
self.tor_control_port = int(netconfig["tor_control_port"]) self.tor_control_port = int(netconfig["tor_control_port"])
@ -325,7 +322,7 @@ class BIP78ServerProtocol(HTTPPassThrough):
""" """
self.post_request = request self.post_request = request
d = self.callRemote(BIP78ReceiverOriginalPSBT, body=body, d = self.callRemote(BIP78ReceiverOriginalPSBT, body=body,
params=json.dumps(bdict_sdict_convert(params))) params=bdict_sdict_convert(params))
self.defaultCallbacks(d) self.defaultCallbacks(d)
@BIP78ReceiverSendProposal.responder @BIP78ReceiverSendProposal.responder
@ -355,7 +352,7 @@ class BIP78ServerProtocol(HTTPPassThrough):
def on_BIP78_SENDER_ORIGINAL_PSBT(self, body, params): def on_BIP78_SENDER_ORIGINAL_PSBT(self, body, params):
self.postRequest(body, self.servers[0], self.postRequest(body, self.servers[0],
self.bip78_receiver_response, self.bip78_receiver_response,
params=json.loads(params), params=params,
headers=Headers({"Content-Type": ["text/plain"]})) headers=Headers({"Content-Type": ["text/plain"]}))
return {"accepted": True} return {"accepted": True}
@ -475,6 +472,8 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
self.sig_lock = threading.Lock() self.sig_lock = threading.Lock()
self.active_orders = {} self.active_orders = {}
self.use_fidelity_bond = False self.use_fidelity_bond = False
self.offerlist = None
self.kp = None
def checkClientResponse(self, response): def checkClientResponse(self, response):
"""A generic check of client acceptance; any failure """A generic check of client acceptance; any failure
@ -502,11 +501,9 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
If a new message channel configuration is required, the current If a new message channel configuration is required, the current
one is shutdown in preparation. one is shutdown in preparation.
""" """
self.maker_timeout_sec = int(maker_timeout_sec) self.maker_timeout_sec = maker_timeout_sec
# used in OrderbookWatch: self.minmakers = minmakers
self.dust_threshold = int(dust_threshold) self.dust_threshold = int(dust_threshold)
self.minmakers = int(minmakers)
irc_configs = json.loads(irc_configs)
#(bitcoin) network only referenced in channel name construction #(bitcoin) network only referenced in channel name construction
self.network = network self.network = network
if irc_configs == self.irc_configs: if irc_configs == self.irc_configs:
@ -554,7 +551,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
return {'accepted': True} return {'accepted': True}
@JMSetup.responder @JMSetup.responder
def on_JM_SETUP(self, role, offers, use_fidelity_bond): def on_JM_SETUP(self, role, initdata, use_fidelity_bond):
assert self.jm_state == 0 assert self.jm_state == 0
self.role = role self.role = role
self.crypto_boxes = {} self.crypto_boxes = {}
@ -568,7 +565,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
if self.role == "TAKER": if self.role == "TAKER":
self.mcc.pubmsg(COMMAND_PREFIX + "orderbook") self.mcc.pubmsg(COMMAND_PREFIX + "orderbook")
elif self.role == "MAKER": elif self.role == "MAKER":
self.offerlist = json.loads(offers) self.offerlist = initdata
self.use_fidelity_bond = use_fidelity_bond self.use_fidelity_bond = use_fidelity_bond
self.mcc.announce_orders(self.offerlist, None, None, None) self.mcc.announce_orders(self.offerlist, None, None, None)
self.jm_state = 1 self.jm_state = 1
@ -614,15 +611,14 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
"""Takes the necessary data from the Taker and initiates the Stage 1 """Takes the necessary data from the Taker and initiates the Stage 1
interaction with the Makers. interaction with the Makers.
""" """
if not (self.jm_state == 1 and isinstance(amount, Integral) if self.jm_state != 1 or amount < 0:
and amount >= 0):
return {'accepted': False} return {'accepted': False}
self.cjamount = amount self.cjamount = amount
self.commitment = commitment self.commitment = commitment
self.revelation = revelation self.revelation = revelation
#Reset utxo data to null for this new transaction #Reset utxo data to null for this new transaction
self.ioauth_data = {} self.ioauth_data = {}
self.active_orders = json.loads(filled_offers) self.active_orders = filled_offers
for nick, offer_dict in self.active_orders.items(): for nick, offer_dict in self.active_orders.items():
offer_fill_msg = " ".join([str(offer_dict["oid"]), str(amount), offer_fill_msg = " ".join([str(offer_dict["oid"]), str(amount),
self.kp.hex_pk().decode('ascii'), str(commitment)]) self.kp.hex_pk().decode('ascii'), str(commitment)])
@ -639,7 +635,6 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
if not self.jm_state == 4: if not self.jm_state == 4:
log.msg("Make tx was called in wrong state, rejecting") log.msg("Make tx was called in wrong state, rejecting")
return {'accepted': False} return {'accepted': False}
nick_list = json.loads(nick_list)
self.mcc.send_tx(nick_list, txhex) self.mcc.send_tx(nick_list, txhex)
return {'accepted': True} return {'accepted': True}
@ -659,9 +654,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
""" """
if self.role != "MAKER": if self.role != "MAKER":
return return
to_announce = json.loads(to_announce) self.offerlist = offerlist
to_cancel = json.loads(to_cancel)
self.offerlist = json.loads(offerlist)
if len(to_cancel) > 0: if len(to_cancel) > 0:
self.mcc.cancel_orders(to_cancel) self.mcc.cancel_orders(to_cancel)
if len(to_announce) > 0: if len(to_announce) > 0:
@ -682,14 +675,13 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
""" """
if not self.role == "MAKER": if not self.role == "MAKER":
return return
if not nick in self.active_orders: if nick not in self.active_orders:
return return
utxos= json.loads(utxolist)
#completed population of order/offer object #completed population of order/offer object
self.active_orders[nick]["cjaddr"] = cjaddr self.active_orders[nick]["cjaddr"] = cjaddr
self.active_orders[nick]["changeaddr"] = changeaddr self.active_orders[nick]["changeaddr"] = changeaddr
self.active_orders[nick]["utxos"] = utxos self.active_orders[nick]["utxos"] = utxolist
msg = str(",".join(utxos.keys())) + " " + " ".join( msg = str(",".join(utxolist)) + " " + " ".join(
[pubkey, cjaddr, changeaddr, pubkeysig]) [pubkey, cjaddr, changeaddr, pubkeysig])
self.mcc.prepare_privmsg(nick, "ioauth", msg) self.mcc.prepare_privmsg(nick, "ioauth", msg)
#In case of *blacklisted (ie already used) commitments, we already #In case of *blacklisted (ie already used) commitments, we already
@ -709,7 +701,6 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
broadcast one by one. TODO: could shorten this, broadcast one by one. TODO: could shorten this,
have more than one sig per message. have more than one sig per message.
""" """
sigs = json.loads(sigs)
for sig in sigs: for sig in sigs:
self.mcc.prepare_privmsg(nick, "sig", sig) self.mcc.prepare_privmsg(nick, "sig", sig)
return {"accepted": True} return {"accepted": True}
@ -798,15 +789,15 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
commitment revelation against the existing commitment, commitment revelation against the existing commitment,
which was already stored in active_orders[nick]. which was already stored in active_orders[nick].
""" """
if not nick in self.active_orders: if nick not in self.active_orders:
return return
ao =self.active_orders[nick] ao =self.active_orders[nick]
#ask the client to validate the commitment and prepare the utxo data #ask the client to validate the commitment and prepare the utxo data
d = self.callRemote(JMAuthReceived, d = self.callRemote(JMAuthReceived,
nick=nick, nick=nick,
offer=json.dumps(ao["offer"]), offer=ao["offer"],
commitment=ao["commit"], commitment=ao["commit"],
revelation=json.dumps(commitment_revelation), revelation=commitment_revelation,
amount=ao["amount"], amount=ao["amount"],
kphex=ao["kp"].hex_pk().decode('ascii')) kphex=ao["kp"].hex_pk().decode('ascii'))
self.defaultCallbacks(d) self.defaultCallbacks(d)
@ -831,20 +822,14 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
self.mcc.pubmsg("!hp2 " + commitment) self.mcc.pubmsg("!hp2 " + commitment)
@maker_only @maker_only
def on_push_tx(self, nick, txhex): def on_push_tx(self, nick, tx):
"""Broadcast unquestioningly, except checking """Broadcast unquestioningly
hex format.
""" """
try: d = self.callRemote(JMTXBroadcast, tx=tx)
dummy = hextobin(txhex)
except:
return
d = self.callRemote(JMTXBroadcast,
txhex=txhex)
self.defaultCallbacks(d) self.defaultCallbacks(d)
@maker_only @maker_only
def on_seen_tx(self, nick, txhex): def on_seen_tx(self, nick, tx):
"""Passes the txhex to the Maker for verification """Passes the txhex to the Maker for verification
and signing. Note the security checks occur in Maker. and signing. Note the security checks occur in Maker.
""" """
@ -855,10 +840,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
ao = copy.deepcopy(self.active_orders[nick]) ao = copy.deepcopy(self.active_orders[nick])
del ao["crypto_box"] del ao["crypto_box"]
del ao["kp"] del ao["kp"]
d = self.callRemote(JMTXReceived, d = self.callRemote(JMTXReceived, nick=nick, tx=tx, offer=ao)
nick=nick,
txhex=txhex,
offer=json.dumps(ao))
self.defaultCallbacks(d) self.defaultCallbacks(d)
@taker_only @taker_only
@ -898,9 +880,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
def on_sig(self, nick, sig): def on_sig(self, nick, sig):
"""Pass signature through to Taker. """Pass signature through to Taker.
""" """
d = self.callRemote(JMSigReceived, d = self.callRemote(JMSigReceived, nick=nick, sig=sig)
nick=nick,
sig=sig)
self.defaultCallbacks(d) self.defaultCallbacks(d)
def on_error(self, msg): def on_error(self, msg):
@ -990,12 +970,12 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
self.jm_state = 3 self.jm_state = 3
if not accepted: if not accepted:
#use ioauth data field to return the list of non-responsive makers #use ioauth data field to return the list of non-responsive makers
nonresponders = [x for x in self.active_orders.keys() if x not nonresponders = [x for x in self.active_orders
in self.ioauth_data.keys()] if x not in self.ioauth_data]
ioauth_data = self.ioauth_data if accepted else nonresponders ioauth_data = self.ioauth_data if accepted else nonresponders
d = self.callRemote(JMFillResponse, d = self.callRemote(JMFillResponse,
success=accepted, success=accepted,
ioauth_data = json.dumps(ioauth_data)) ioauth_data=ioauth_data)
if not accepted: if not accepted:
#Client simply accepts failure TODO #Client simply accepts failure TODO
self.defaultCallbacks(d) self.defaultCallbacks(d)
@ -1010,7 +990,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
either send success + ioauth data if enough makers, either send success + ioauth data if enough makers,
else send failure to client. else send failure to client.
""" """
response = True if len(self.ioauth_data.keys()) >= self.minmakers else False response = True if len(self.ioauth_data) >= self.minmakers else False
self.respondToIoauths(response) self.respondToIoauths(response)
def checkUtxosAccepted(self, accepted): def checkUtxosAccepted(self, accepted):

8
jmdaemon/jmdaemon/message_channel.py

@ -1028,21 +1028,21 @@ class MessageChannel(object):
elif _chunks[0] == 'tx': elif _chunks[0] == 'tx':
b64tx = _chunks[1] b64tx = _chunks[1]
try: try:
txhex = binascii.hexlify(base64.b64decode(b64tx)).decode('ascii') tx = base64.b64decode(b64tx)
except TypeError as e: except TypeError as e:
self.send_error(nick, 'bad base64 tx. ' + repr(e)) self.send_error(nick, 'bad base64 tx. ' + repr(e))
return return
if self.on_seen_tx: if self.on_seen_tx:
self.on_seen_tx(nick, txhex) self.on_seen_tx(nick, tx)
elif _chunks[0] == 'push': elif _chunks[0] == 'push':
b64tx = _chunks[1] b64tx = _chunks[1]
try: try:
txhex = binascii.hexlify(base64.b64decode(b64tx)).decode('ascii') tx = base64.b64decode(b64tx)
except TypeError as e: except TypeError as e:
self.send_error(nick, 'bad base64 tx. ' + repr(e)) self.send_error(nick, 'bad base64 tx. ' + repr(e))
return return
if self.on_push_tx: if self.on_push_tx:
self.on_push_tx(nick, txhex) self.on_push_tx(nick, tx)
except (IndexError, ValueError): except (IndexError, ValueError):
# TODO proper error handling # TODO proper error handling
log.debug('cj peer error TODO handle') log.debug('cj peer error TODO handle')

25
jmdaemon/test/test_daemon_protocol.py

@ -19,7 +19,6 @@ from twisted.protocols import amp
from twisted.trial import unittest from twisted.trial import unittest
from jmbase.commands import * from jmbase.commands import *
from msgdata import * from msgdata import *
import json
import base64 import base64
import sys import sys
from dummy_mc import DummyMessageChannel from dummy_mc import DummyMessageChannel
@ -64,7 +63,7 @@ class JMTestClientProtocol(JMBaseProtocol):
d = self.callRemote(JMInit, d = self.callRemote(JMInit,
bcsource="dummyblockchain", bcsource="dummyblockchain",
network="dummynetwork", network="dummynetwork",
irc_configs=json.dumps(irc), irc_configs=irc,
minmakers=2, minmakers=2,
maker_timeout_sec=3, maker_timeout_sec=3,
dust_threshold=27300) dust_threshold=27300)
@ -85,7 +84,7 @@ class JMTestClientProtocol(JMBaseProtocol):
show_receipt("JMUP") show_receipt("JMUP")
d = self.callRemote(JMSetup, d = self.callRemote(JMSetup,
role="TAKER", role="TAKER",
offers="{}", initdata=None,
use_fidelity_bond=False) use_fidelity_bond=False)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}
@ -104,10 +103,9 @@ class JMTestClientProtocol(JMBaseProtocol):
return {'accepted': True} return {'accepted': True}
def maketx(self, ioauth_data): def maketx(self, ioauth_data):
ioauth_data = json.loads(ioauth_data) nl = list(ioauth_data)
nl = list(ioauth_data.keys())
d = self.callRemote(JMMakeTx, d = self.callRemote(JMMakeTx,
nick_list= json.dumps(nl), nick_list=nl,
txhex="deadbeef") txhex="deadbeef")
self.defaultCallbacks(d) self.defaultCallbacks(d)
@ -136,7 +134,7 @@ class JMTestClientProtocol(JMBaseProtocol):
amount=100, amount=100,
commitment="dummycommitment", commitment="dummycommitment",
revelation="dummyrevelation", revelation="dummyrevelation",
filled_offers=json.dumps(t_chosen_orders)) filled_offers=t_chosen_orders)
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}
@ -215,9 +213,9 @@ class JMDaemonTestServerProtocol(JMDaemonServerProtocol):
@JMInit.responder @JMInit.responder
def on_JM_INIT(self, bcsource, network, irc_configs, minmakers, def on_JM_INIT(self, bcsource, network, irc_configs, minmakers,
maker_timeout_sec, dust_threshold): maker_timeout_sec, dust_threshold):
self.maker_timeout_sec = int(maker_timeout_sec) self.maker_timeout_sec = maker_timeout_sec
self.dust_threshold = int(dust_threshold) self.dust_threshold = int(dust_threshold)
self.minmakers = int(minmakers) self.minmakers = minmakers
mcs = [DummyMC(None)] mcs = [DummyMC(None)]
self.mcc = MessageChannelCollection(mcs) self.mcc = MessageChannelCollection(mcs)
#The following is a hack to get the counterparties marked seen/active; #The following is a hack to get the counterparties marked seen/active;
@ -241,20 +239,19 @@ class JMDaemonTestServerProtocol(JMDaemonServerProtocol):
@JMFill.responder @JMFill.responder
def on_JM_FILL(self, amount, commitment, revelation, filled_offers): def on_JM_FILL(self, amount, commitment, revelation, filled_offers):
tmpfo = json.loads(filled_offers)
dummypub = "073732a7ca60470f709f23c602b2b8a6b1ba62ee8f3f83a61e5484ab5cbf9c3d" dummypub = "073732a7ca60470f709f23c602b2b8a6b1ba62ee8f3f83a61e5484ab5cbf9c3d"
#trigger invalid on_pubkey conditions #trigger invalid on_pubkey conditions
reactor.callLater(1, self.on_pubkey, "notrealcp", dummypub) reactor.callLater(1, self.on_pubkey, "notrealcp", dummypub)
reactor.callLater(2, self.on_pubkey, list(tmpfo.keys())[0], dummypub + "deadbeef") reactor.callLater(2, self.on_pubkey, list(filled_offers)[0], dummypub + "deadbeef")
#trigger invalid on_ioauth condition #trigger invalid on_ioauth condition
reactor.callLater(2, self.on_ioauth, "notrealcp", 1, 2, 3, 4, 5) reactor.callLater(2, self.on_ioauth, "notrealcp", 1, 2, 3, 4, 5)
#trigger msg sig verify request operation for a dummy message #trigger msg sig verify request operation for a dummy message
#currently a pass-through #currently a pass-through
reactor.callLater(1, self.request_signature_verify, "1", reactor.callLater(1, self.request_signature_verify, "1",
"!push abcd abc def", "3", "4", "!push abcd abc def", "3", "4",
str(list(tmpfo.keys())[0]), 6, 7, self.mcc.mchannels[0].hostid) str(list(filled_offers)[0]), 6, 7, self.mcc.mchannels[0].hostid)
#send "valid" onpubkey, onioauth messages #send "valid" onpubkey, onioauth messages
for k, v in tmpfo.items(): for k, v in filled_offers.items():
reactor.callLater(1, self.on_pubkey, k, dummypub) reactor.callLater(1, self.on_pubkey, k, dummypub)
reactor.callLater(2, self.on_ioauth, k, ['a', 'b'], "auth_pub", reactor.callLater(2, self.on_ioauth, k, ['a', 'b'], "auth_pub",
"cj_addr", "change_addr", "btc_sig") "cj_addr", "change_addr", "btc_sig")
@ -262,7 +259,7 @@ class JMDaemonTestServerProtocol(JMDaemonServerProtocol):
@JMMakeTx.responder @JMMakeTx.responder
def on_JM_MAKE_TX(self, nick_list, txhex): def on_JM_MAKE_TX(self, nick_list, txhex):
for n in json.loads(nick_list): for n in nick_list:
reactor.callLater(1, self.on_sig, n, "dummytxsig") reactor.callLater(1, self.on_sig, n, "dummytxsig")
return super().on_JM_MAKE_TX(nick_list, txhex) return super().on_JM_MAKE_TX(nick_list, txhex)

8
test/ygrunner.py

@ -54,12 +54,12 @@ class MaliciousYieldGenerator(YieldGeneratorBasic):
jmprint("Counterparty commitment rejected maliciously", "debug") jmprint("Counterparty commitment rejected maliciously", "debug")
return (False,) return (False,)
return super().on_auth_received(nick, offer, commitment, cr, amount, kphex) return super().on_auth_received(nick, offer, commitment, cr, amount, kphex)
def on_tx_received(self, nick, txhex, offerinfo): def on_tx_received(self, nick, tx, offerinfo):
if self.txmal: if self.txmal:
if random.randint(1, 100) < self.mfrac: if random.randint(1, 100) < self.mfrac:
jmprint("Counterparty tx rejected maliciously", "debug") jmprint("Counterparty tx rejected maliciously", "debug")
return (False, "malicious tx rejection") return (False, "malicious tx rejection")
return super().on_tx_received(nick, txhex, offerinfo) return super().on_tx_received(nick, tx, offerinfo)
class DeterministicMaliciousYieldGenerator(YieldGeneratorBasic): class DeterministicMaliciousYieldGenerator(YieldGeneratorBasic):
"""Overrides, randomly chosen persistently, some maker functions """Overrides, randomly chosen persistently, some maker functions
@ -85,11 +85,11 @@ class DeterministicMaliciousYieldGenerator(YieldGeneratorBasic):
jmprint("Counterparty commitment rejected maliciously", "debug") jmprint("Counterparty commitment rejected maliciously", "debug")
return (False,) return (False,)
return super().on_auth_received(nick, offer, commitment, cr, amount, kphex) return super().on_auth_received(nick, offer, commitment, cr, amount, kphex)
def on_tx_received(self, nick, txhex, offerinfo): def on_tx_received(self, nick, tx, offerinfo):
if self.txmal: if self.txmal:
jmprint("Counterparty tx rejected maliciously", "debug") jmprint("Counterparty tx rejected maliciously", "debug")
return (False, "malicious tx rejection") return (False, "malicious tx rejection")
return super().on_tx_received(nick, txhex, offerinfo) return super().on_tx_received(nick, tx, offerinfo)
@pytest.mark.parametrize( @pytest.mark.parametrize(

Loading…
Cancel
Save