You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

318 lines
13 KiB

#! /usr/bin/env python
from __future__ import absolute_import
'''test daemon-protocol interfacae.'''
import pytest
from jmdaemon import (JMDaemonServerProtocolFactory, MessageChannelCollection)
from jmdaemon.orderbookwatch import OrderbookWatch
from jmdaemon.daemon_protocol import JMDaemonServerProtocol
from jmdaemon.protocol import (COMMAND_PREFIX, ORDER_KEYS, NICK_HASH_LENGTH,
NICK_MAX_ENCODED, JM_VERSION, JOINMARKET_NICK_HEADER)
from jmclient import (load_program_config, get_log, jm_single, get_irc_mchannels,
JMTakerClientProtocolFactory, Taker, AbstractWallet)
import os
from twisted.python.log import startLogging, err
from twisted.python.log import msg as tmsg
from twisted.internet import protocol, reactor, task
from twisted.internet.protocol import ServerFactory, ClientCreator
from twisted.internet.error import (ConnectionLost, ConnectionAborted,
ConnectionClosed, ConnectionDone)
from twisted.protocols.amp import UnknownRemoteError
from twisted.python import failure
from twisted.protocols import amp
from twisted.trial import unittest
from jmbase.commands import *
from msgdata import *
import json
import time
import base64
from dummy_mc import DummyMessageChannel
test_completed = False
end_early = False
jlog = get_log()
class JMProtocolError(Exception):
pass
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()
def defaultErrback(self, failure):
failure.trap(ConnectionAborted, ConnectionClosed, ConnectionDone,
ConnectionLost, UnknownRemoteError)
reactor.stop()
def defaultCallbacks(self, d):
d.addCallback(self.checkClientResponse)
d.addErrback(self.defaultErrback)
class JMTestClientProtocol(JMBaseProtocol):
def connectionMade(self):
self.clientStart()
def clientStart(self):
self.sigs_received = 0
irc = get_irc_mchannels()
d = self.callRemote(JMInit,
bcsource="dummyblockchain",
network="dummynetwork",
irc_configs=json.dumps(irc),
minmakers=2,
maker_timeout_sec=3)
self.defaultCallbacks(d)
@JMInitProto.responder
def on_JM_INIT_PROTO(self, nick_hash_length, nick_max_encoded,
joinmarket_nick_header, joinmarket_version):
show_receipt("JMINITPROTO", nick_hash_length, nick_max_encoded,
joinmarket_nick_header, joinmarket_version)
d = self.callRemote(JMStartMC,
nick="dummynick")
self.defaultCallbacks(d)
return {'accepted': True}
@JMUp.responder
def on_JM_UP(self):
show_receipt("JMUP")
d = self.callRemote(JMSetup,
role="TAKER",
n_counterparties=4) #TODO this number should be set
self.defaultCallbacks(d)
return {'accepted': True}
@JMSetupDone.responder
def on_JM_SETUP_DONE(self):
show_receipt("JMSETUPDONE")
d = self.callRemote(JMRequestOffers)
self.defaultCallbacks(d)
return {'accepted': True}
@JMFillResponse.responder
def on_JM_FILL_RESPONSE(self, success, ioauth_data):
show_receipt("JMFILLRESPONSE", success, ioauth_data)
reactor.callLater(1, self.maketx, ioauth_data)
return {'accepted': True}
def maketx(self, ioauth_data):
ioauth_data = json.loads(ioauth_data)
nl = ioauth_data.keys()
d = self.callRemote(JMMakeTx,
nick_list= json.dumps(nl),
txhex="deadbeef")
self.defaultCallbacks(d)
@JMOffers.responder
def on_JM_OFFERS(self, orderbook):
if end_early:
return {'accepted': True}
jlog.debug("JMOFFERS" + str(orderbook))
#Trigger receipt of verified privmsgs, including unverified
nick = str(t_chosen_orders.keys()[0])
b64tx = base64.b64encode("deadbeef")
d1 = self.callRemote(JMMsgSignatureVerify,
verif_result=True,
nick=nick,
fullmsg="!push " + b64tx + " abc def",
hostid="dummy")
self.defaultCallbacks(d1)
#unverified
d2 = self.callRemote(JMMsgSignatureVerify,
verif_result=False,
nick=nick,
fullmsg="!push " + b64tx + " abc def",
hostid="dummy")
self.defaultCallbacks(d2)
d = self.callRemote(JMFill,
amount=100,
commitment="dummycommitment",
revelation="dummyrevelation",
filled_offers=json.dumps(t_chosen_orders))
self.defaultCallbacks(d)
return {'accepted': True}
@JMSigReceived.responder
def on_JM_SIG_RECEIVED(self, nick, sig):
show_receipt("JMSIGRECEIVED", nick, sig)
self.sigs_received += 1
if self.sigs_received == 3:
#end of test
reactor.callLater(1, end_test)
return {'accepted': True}
@JMRequestMsgSig.responder
def on_JM_REQUEST_MSGSIG(self, nick, cmd, msg, msg_to_be_signed, hostid):
show_receipt("JMREQUESTMSGSIG", nick, cmd, msg, msg_to_be_signed, hostid)
d = self.callRemote(JMMsgSignature,
nick=nick,
cmd=cmd,
msg_to_return="xxxcreatedsigxx",
hostid=hostid)
self.defaultCallbacks(d)
return {'accepted': True}
@JMRequestMsgSigVerify.responder
def on_JM_REQUEST_MSGSIG_VERIFY(self, msg, fullmsg, sig, pubkey, nick,
hashlen, max_encoded, hostid):
show_receipt("JMREQUESTMSGSIGVERIFY", msg, fullmsg, sig, pubkey,
nick, hashlen, max_encoded, hostid)
d = self.callRemote(JMMsgSignatureVerify,
verif_result=True,
nick=nick,
fullmsg=fullmsg,
hostid=hostid)
self.defaultCallbacks(d)
return {'accepted': True}
class JMTestClientProtocolFactory(protocol.ClientFactory):
protocol = JMTestClientProtocol
def show_receipt(name, *args):
tmsg("Received msgtype: " + name + ", args: " + ",".join([str(x) for x in args]))
def end_test():
global test_completed
test_completed = True
class JMDaemonTestServerProtocol(JMDaemonServerProtocol):
def __init__(self, factory):
super(JMDaemonTestServerProtocol, self).__init__(factory)
#respondtoioauths should do nothing unless jmstate = 2
self.respondToIoauths(True)
#calling on_JM_MAKE_TX should also do nothing in wrong state
assert super(JMDaemonTestServerProtocol, self).on_JM_MAKE_TX(
1, 2) == {'accepted': False}
#calling on_JM_FILL with negative amount should reject
assert super(JMDaemonTestServerProtocol, self).on_JM_FILL(
-1000, 2, 3, 4) == {'accepted': False}
#checkutxos also does nothing for rejection at the moment
self.checkUtxosAccepted(False)
#None should be returned requesting a cryptobox for an unknown cp
assert self.get_crypto_box_from_nick("notrealcp") == None
#does nothing yet
self.on_error()
@JMRequestOffers.responder
def on_JM_REQUEST_OFFERS(self):
for o in t_orderbook:
#counterparty, oid, ordertype, minsize, maxsize,txfee, cjfee):
self.on_order_seen(o["counterparty"], o["oid"], o["ordertype"],
o["minsize"], o["maxsize"],
o["txfee"], o["cjfee"])
return super(JMDaemonTestServerProtocol, self).on_JM_REQUEST_OFFERS()
@JMInit.responder
def on_JM_INIT(self, bcsource, network, irc_configs, minmakers,
maker_timeout_sec):
self.maker_timeout_sec = int(maker_timeout_sec)
self.minmakers = int(minmakers)
mcs = [DummyMessageChannel(None)]
self.mcc = MessageChannelCollection(mcs)
#The following is a hack to get the counterparties marked seen/active;
#note it must happen before callign set_msgchan for OrderbookWatch
self.mcc.on_order_seen = None
for c in [o['counterparty'] for o in t_orderbook]:
self.mcc.on_order_seen_trigger(mcs[0], c, "a", "b", "c", "d", "e", "f")
OrderbookWatch.set_msgchan(self, self.mcc)
#register taker-specific msgchan callbacks here
self.mcc.register_taker_callbacks(self.on_error, self.on_pubkey,
self.on_ioauth, self.on_sig)
self.mcc.set_daemon(self)
self.restart_mc_required = True
d = self.callRemote(JMInitProto,
nick_hash_length=NICK_HASH_LENGTH,
nick_max_encoded=NICK_MAX_ENCODED,
joinmarket_nick_header=JOINMARKET_NICK_HEADER,
joinmarket_version=JM_VERSION)
self.defaultCallbacks(d)
return {'accepted': True}
@JMFill.responder
def on_JM_FILL(self, amount, commitment, revelation, filled_offers):
tmpfo = json.loads(filled_offers)
dummypub = "073732a7ca60470f709f23c602b2b8a6b1ba62ee8f3f83a61e5484ab5cbf9c3d"
#trigger invalid on_pubkey conditions
reactor.callLater(1, self.on_pubkey, "notrealcp", dummypub)
reactor.callLater(2, self.on_pubkey, tmpfo.keys()[0], dummypub + "deadbeef")
#trigger invalid on_ioauth condition
reactor.callLater(2, self.on_ioauth, "notrealcp", 1, 2, 3, 4, 5)
#trigger msg sig verify request operation for a dummy message
#currently a pass-through
reactor.callLater(1, self.request_signature_verify, "1",
"!push abcd abc def", "3", "4",
str(tmpfo.keys()[0]), 6, 7, self.mcc.mchannels[0].hostid)
#send "valid" onpubkey, onioauth messages
for k, v in tmpfo.iteritems():
reactor.callLater(1, self.on_pubkey, k, dummypub)
reactor.callLater(2, self.on_ioauth, k, ['a', 'b'], "auth_pub",
"cj_addr", "change_addr", "btc_sig")
return super(JMDaemonTestServerProtocol, self).on_JM_FILL(amount,
commitment, revelation, filled_offers)
@JMMakeTx.responder
def on_JM_MAKE_TX(self, nick_list, txhex):
for n in nick_list:
reactor.callLater(1, self.on_sig, n, "dummytxsig")
return super(JMDaemonTestServerProtocol, self).on_JM_MAKE_TX(nick_list,
txhex)
class JMDaemonTestServerProtocolFactory(ServerFactory):
protocol = JMDaemonTestServerProtocol
def buildProtocol(self, addr):
return JMDaemonTestServerProtocol(self)
class TrialTestJMDaemonProto(unittest.TestCase):
def setUp(self):
load_program_config()
jm_single().maker_timeout_sec = 1
self.port = reactor.listenTCP(27184, JMDaemonTestServerProtocolFactory())
self.addCleanup(self.port.stopListening)
clientconn = reactor.connectTCP("localhost", 27184,
JMTestClientProtocolFactory())
self.addCleanup(clientconn.disconnect)
print("Got here")
def test_waiter(self):
print("test_main()")
return task.deferLater(reactor, 12, self._called_by_deffered)
def _called_by_deffered(self):
pass
class TestJMDaemonProtoInit(unittest.TestCase):
def setUp(self):
global end_early
end_early = True
print("setUp()")
load_program_config()
jm_single().maker_timeout_sec = 1
self.port = reactor.listenTCP(27184, JMDaemonServerProtocolFactory())
self.addCleanup(self.port.stopListening)
clientconn = reactor.connectTCP("localhost", 27184,
JMTestClientProtocolFactory())
self.addCleanup(clientconn.disconnect)
print("Got here")
def test_waiter(self):
print("test_main()")
return task.deferLater(reactor, 5, self._called_by_deffered)
def _called_by_deffered(self):
global end_early
end_early = False