From d33cc5c6a879eea4e0be391b139979d2a4847b77 Mon Sep 17 00:00:00 2001 From: undeath Date: Sat, 1 Dec 2018 18:58:38 +0100 Subject: [PATCH 1/2] clean up Maker interface --- jmclient/jmclient/maker.py | 27 +++++++++++++++++++++++++++ jmclient/jmclient/yieldgenerator.py | 25 ------------------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/jmclient/jmclient/maker.py b/jmclient/jmclient/maker.py index bf1b858..56afcbc 100644 --- a/jmclient/jmclient/maker.py +++ b/jmclient/jmclient/maker.py @@ -4,6 +4,7 @@ from __future__ import print_function import base64 import pprint import sys +import abc from binascii import unhexlify import btc @@ -234,3 +235,29 @@ class Maker(object): return assert hasattr(bci, 'get_wallet_name') bci.import_addresses(addr_list, bci.get_wallet_name(self.wallet)) + + @abc.abstractmethod + def create_my_orders(self): + """Must generate a set of orders to be displayed + according to the contents of the wallet + some algo. + (Note: should be called "create_my_offers") + """ + + @abc.abstractmethod + def oid_to_order(self, cjorder, oid, amount): + """Must convert an order with an offer/order id + into a set of utxos to fill the order. + Also provides the output addresses for the Taker. + """ + + @abc.abstractmethod + def on_tx_unconfirmed(self, cjorder, txid, removed_utxos): + """Performs action on receipt of transaction into the + mempool in the blockchain instance (e.g. announcing orders) + """ + + @abc.abstractmethod + def on_tx_confirmed(self, cjorder, confirmations, txid): + """Performs actions on receipt of 1st confirmation of + a transaction into a block (e.g. announce orders) + """ diff --git a/jmclient/jmclient/yieldgenerator.py b/jmclient/jmclient/yieldgenerator.py index d325223..ca6cb54 100644 --- a/jmclient/jmclient/yieldgenerator.py +++ b/jmclient/jmclient/yieldgenerator.py @@ -41,31 +41,6 @@ class YieldGenerator(Maker): self.income_statement.write(','.join(data) + '\n') self.income_statement.close() - @abc.abstractmethod - def create_my_orders(self): - """Must generate a set of orders to be displayed - according to the contents of the wallet + some algo. - (Note: should be called "create_my_offers") - """ - - @abc.abstractmethod - def oid_to_order(self, cjorder, oid, amount): - """Must convert an order with an offer/order id - into a set of utxos to fill the order. - Also provides the output addresses for the Taker. - """ - - @abc.abstractmethod - def on_tx_unconfirmed(self, cjorder, txid, removed_utxos): - """Performs action on receipt of transaction into the - mempool in the blockchain instance (e.g. announcing orders) - """ - - @abc.abstractmethod - def on_tx_confirmed(self, cjorder, confirmations, txid): - """Performs actions on receipt of 1st confirmation of - a transaction into a block (e.g. announce orders) - """ class YieldGeneratorBasic(YieldGenerator): """A simplest possible instantiation of a yieldgenerator. From a43eceb026a377e2255f4816cc96bc3df476fdfc Mon Sep 17 00:00:00 2001 From: undeath Date: Sat, 1 Dec 2018 18:59:43 +0100 Subject: [PATCH 2/2] add very basic JMMakerClientProtocol test --- jmclient/test/test_client_protocol.py | 149 +++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 3 deletions(-) diff --git a/jmclient/test/test_client_protocol.py b/jmclient/test/test_client_protocol.py index 5c58691..1ed50c8 100644 --- a/jmclient/test/test_client_protocol.py +++ b/jmclient/test/test_client_protocol.py @@ -3,15 +3,17 @@ from __future__ import absolute_import '''test client-protocol interfacae.''' from jmclient import load_program_config, Taker, get_log,\ - JMClientProtocolFactory, jm_single + JMClientProtocolFactory, jm_single, Maker from jmclient.client_protocol import JMTakerClientProtocol from twisted.python.log import msg as tmsg from twisted.internet import protocol, reactor, task +from twisted.internet.defer import inlineCallbacks from twisted.internet.error import (ConnectionLost, ConnectionAborted, ConnectionClosed, ConnectionDone) from twisted.protocols.amp import UnknownRemoteError from twisted.protocols import amp from twisted.trial import unittest +from twisted.test import proto_helpers from jmbase.commands import * from taker_test_data import t_raw_signed_tx import json @@ -26,9 +28,11 @@ clientfactory = None runno = 0 jlog = get_log() + def dummy_taker_finished(res, fromtx, waittime=0.0): pass + class DummyTaker(Taker): def set_fail_init(self, val): @@ -72,18 +76,62 @@ class DummyTaker(Taker): return None +class DummyWallet(object): + def get_wallet_id(self): + return 'aaaa' + + +class DummyMaker(Maker): + def __init__(self): + self.aborted = False + self.wallet = DummyWallet() + + def try_to_create_my_orders(self): + pass + + def on_auth_received(self, nick, offer, commitment, cr, amount, kphex): + # success, utxos, auth_pub, cj_addr, change_addr, btc_sig + return True, [], '', '', '', '' + + def on_tx_received(self, nick, txhex, offerinfo): + # success, sigs + return True, [] + + def verify_unsigned_tx(self, txd, offerinfo): + # success, errormsg + return True, None + + def modify_orders(self, to_cancel, to_announce): + pass + + def import_new_addresses(self, addr_list): + pass + + def create_my_orders(self): + return [] + + def oid_to_order(self, cjorder, oid, amount): + # utxos, cj_addr, change_addr + return [], '', '' + + def on_tx_unconfirmed(self, cjorder, txid, removed_utxos): + return [], [] + + def on_tx_confirmed(self, cjorder, confirmations, txid): + return [], [] + + class JMBaseProtocol(amp.AMP): def checkClientResponse(self, response): """A generic check of client acceptance; any failure is considered criticial. """ if 'accepted' not in response or not response['accepted']: - reactor.stop() + raise Exception("unexpected client response") def defaultErrback(self, failure): failure.trap(ConnectionAborted, ConnectionClosed, ConnectionDone, ConnectionLost, UnknownRemoteError) - reactor.stop() def defaultCallbacks(self, d): d.addCallback(self.checkClientResponse) @@ -203,6 +251,7 @@ class DummyClientProtocolFactory(JMClientProtocolFactory): def buildProtocol(self, addr): return JMTakerClientProtocol(self, self.client, nick_priv="aa"*32) + class TrialTestJMClientProto(unittest.TestCase): def setUp(self): @@ -236,9 +285,103 @@ class TrialTestJMClientProto(unittest.TestCase): clientfactories[0]) self.addCleanup(clientconn.disconnect) + def tearDown(self): + for dc in reactor.getDelayedCalls(): + dc.cancel() + def test_waiter(self): print("test_main()") return task.deferLater(reactor, 3, self._called_by_deffered) def _called_by_deffered(self): pass + + +class TestMakerClientProtocol(unittest.TestCase): + """ + very basic test case for JMMakerClientProtocol + + This test does not do much useful things right now, other than making sure + no exceptions are fired. Most argument data is not crafted carefully and + may lead to failing tests if anything changes. + """ + def get_response_cb(self, cmd): + def response_cb(response): + parsed = cmd.parseResponse(response, self.client) + assert parsed['accepted'] is True + return response_cb + + def callClient(self, jmcmd, response_cb=None, error_cb=None, **kwargs): + box = jmcmd.makeArguments(kwargs, self.client) + box[b'_command'] = jmcmd.__name__ + d = self.client.dispatchCommand(box) + d.addCallback(response_cb or self.get_response_cb(jmcmd)) + if error_cb: + d.addErrback(error_cb) + return d + + def setUp(self): + load_program_config() + factory = JMClientProtocolFactory(DummyMaker(), proto_type='MAKER') + self.client = factory.buildProtocol(None) + self.tr = proto_helpers.StringTransport() + self.client.makeConnection(self.tr) + + def tearDown(self): + for dc in reactor.getDelayedCalls(): + dc.cancel() + + def init_client(self): + return self.callClient( + JMInitProto, nick_hash_length=1, nick_max_encoded=2, + joinmarket_nick_header='J', joinmarket_version=5) + + @inlineCallbacks + def test_JMInitProto(self): + yield self.init_client() + + @inlineCallbacks + def test_JMRequestMsgSig(self): + yield self.init_client() + yield self.callClient( + JMRequestMsgSig, nick='dummynickforsign', cmd='command1', + msg='msgforsign', msg_to_be_signed='fullmsgforsign', + hostid='hostid1') + + @inlineCallbacks + def test_JMRequestMsgSigVerify(self): + fullmsg = 'fullmsgforverify' + priv = 'aa'*32 + '01' + pub = bitcoin.privkey_to_pubkey(priv) + sig = bitcoin.ecdsa_sign(fullmsg, priv) + yield self.init_client() + yield self.callClient( + JMRequestMsgSigVerify, msg='msgforverify', fullmsg=fullmsg, + sig=sig, pubkey=pub, nick='dummynickforverify', hashlen=4, + max_encoded=5, hostid='hostid2') + + @inlineCallbacks + def test_JMUp(self): + yield self.init_client() + yield self.callClient(JMUp) + + @inlineCallbacks + def test_JMSetupDone(self): + yield self.init_client() + yield self.callClient(JMSetupDone) + + + @inlineCallbacks + def test_JMAuthReceived(self): + yield self.init_client() + yield self.callClient( + JMAuthReceived, nick='testnick', offer='{}', + commitment='testcommitment', revelation='{}', amount=100000, + kphex='testkphex') + + @inlineCallbacks + def test_JMTXReceived(self): + yield self.init_client() + yield self.callClient( + JMTXReceived, nick='testnick', txhex=t_raw_signed_tx, + offer='{"cjaddr":"2MwfecDHsQTm4Gg3RekQdpqAMR15BJrjfRF"}')