7 changed files with 340 additions and 9 deletions
@ -0,0 +1,246 @@
|
||||
#! /usr/bin/env python |
||||
from __future__ import absolute_import |
||||
'''test client-protocol interfacae.''' |
||||
|
||||
import pytest |
||||
from jmclient import (get_schedule, load_program_config, start_reactor, |
||||
Taker, get_log, JMTakerClientProtocolFactory, jm_single) |
||||
from jmclient.client_protocol import JMProtocolError, JMTakerClientProtocol |
||||
import os |
||||
from twisted.python.log import startLogging, err |
||||
from twisted.python.log import msg as tmsg |
||||
from twisted.internet import protocol, reactor |
||||
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 jmbase.commands import * |
||||
from taker_test_data import t_raw_signed_tx |
||||
import json |
||||
import time |
||||
import jmbitcoin as bitcoin |
||||
|
||||
test_completed = False |
||||
|
||||
clientfactory = None |
||||
runno = 0 |
||||
jlog = get_log() |
||||
|
||||
class DummyTaker(Taker): |
||||
|
||||
def set_fail_init(self, val): |
||||
self.failinit = val |
||||
def set_fail_utxos(self, val): |
||||
self.failutxos = val |
||||
|
||||
def default_taker_info_callback(self, infotype, msg): |
||||
jlog.debug(infotype + ":" + msg) |
||||
|
||||
def initialize(self, orderbook): |
||||
"""Once the daemon is active and has returned the current orderbook, |
||||
select offers, re-initialize variables and prepare a commitment, |
||||
then send it to the protocol to fill offers. |
||||
""" |
||||
if self.failinit==-1: |
||||
return (True, -1, "aa"*32, {'dummy':'revelation'},orderbook[:2]) |
||||
elif self.failinit: |
||||
return (False,) |
||||
else: |
||||
return (True, 1000000, "aa"*32, {'dummy':'revelation'}, |
||||
orderbook[:2]) |
||||
|
||||
def receive_utxos(self, ioauth_data): |
||||
"""Triggered when the daemon returns utxo data from |
||||
makers who responded; this is the completion of phase 1 |
||||
of the protocol |
||||
""" |
||||
if self.failutxos: |
||||
return (False, "dummyreason") |
||||
else: |
||||
return (True, [x*64 + ":01" for x in ["a", "b", "c"]], t_raw_signed_tx) |
||||
|
||||
|
||||
def on_sig(self, nick, sigb64): |
||||
jlog.debug("We got a sig: " + sigb64) |
||||
end_test() |
||||
return True |
||||
|
||||
|
||||
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) |
||||
|
||||
def show_receipt(name, *args): |
||||
tmsg("Received msgtype: " + name + ", args: " + ",".join([str(x) for x in args])) |
||||
|
||||
def end_client(client): |
||||
client.shutdown_requested = True |
||||
|
||||
def end_test(): |
||||
#global runno |
||||
global test_completed |
||||
#runno += 1 |
||||
#jlog.info("Updated runno to: " + str(runno)) |
||||
#taker = DummyTaker(None, None) |
||||
#if runno == 1: |
||||
# jlog.info("Run number was less than 2") |
||||
# taker.set_fail_init(True) |
||||
# taker.set_fail_utxos(False) |
||||
# cfactory = JMTakerClientProtocolFactory(taker) |
||||
# reactor.connectTCP("localhost", 27184, cfactory) |
||||
# return |
||||
#elif runno == 2: |
||||
# taker.set_fail_init(False) |
||||
# taker.set_fail_utxos(True) |
||||
# cfactory = JMTakerClientProtocolFactory(taker) |
||||
# reactor.connectTCP("localhost", 27184, cfactory) |
||||
# return |
||||
test_completed = True |
||||
client = clientfactory.getClient() |
||||
reactor.callLater(2, end_client, client) |
||||
|
||||
class JMTestServerProtocol(JMBaseProtocol): |
||||
|
||||
@JMInit.responder |
||||
def on_JM_INIT(self, bcsource, network, irc_configs, minmakers, |
||||
maker_timeout_sec): |
||||
show_receipt("JMINIT", bcsource, network, irc_configs, minmakers, |
||||
maker_timeout_sec) |
||||
d = self.callRemote(JMInitProto, |
||||
nick_hash_length=1, |
||||
nick_max_encoded=2, |
||||
joinmarket_nick_header="J", |
||||
joinmarket_version=5) |
||||
self.defaultCallbacks(d) |
||||
return {'accepted': True} |
||||
|
||||
@JMStartMC.responder |
||||
def on_JM_START_MC(self, nick): |
||||
show_receipt("STARTMC", nick) |
||||
d = self.callRemote(JMUp) |
||||
self.defaultCallbacks(d) |
||||
return {'accepted': True} |
||||
|
||||
@JMSetup.responder |
||||
def on_JM_SETUP(self, role, n_counterparties): |
||||
show_receipt("JMSETUP", role,n_counterparties) |
||||
d = self.callRemote(JMSetupDone) |
||||
self.defaultCallbacks(d) |
||||
return {'accepted': True} |
||||
|
||||
@JMRequestOffers.responder |
||||
def on_JM_REQUEST_OFFERS(self): |
||||
show_receipt("JMREQUESTOFFERS") |
||||
#build a huge orderbook to test BigString Argument |
||||
orderbook = ["aaaa" for _ in range(15)] |
||||
d = self.callRemote(JMOffers, |
||||
orderbook=json.dumps(orderbook)) |
||||
self.defaultCallbacks(d) |
||||
return {'accepted': True} |
||||
|
||||
@JMFill.responder |
||||
def on_JM_FILL(self, amount, commitment, revelation, filled_offers): |
||||
success = False if amount == -1 else True |
||||
show_receipt("JMFILL", amount, commitment, revelation, filled_offers) |
||||
d = self.callRemote(JMFillResponse, |
||||
success=success, |
||||
ioauth_data = json.dumps(['dummy', 'list'])) |
||||
return {'accepted': True} |
||||
|
||||
@JMMakeTx.responder |
||||
def on_JM_MAKE_TX(self, nick_list, txhex): |
||||
show_receipt("JMMAKETX", nick_list, txhex) |
||||
d = self.callRemote(JMSigReceived, |
||||
nick="dummynick", |
||||
sig="xxxsig") |
||||
self.defaultCallbacks(d) |
||||
#add dummy calls to check message sign and message verify |
||||
d2 = self.callRemote(JMRequestMsgSig, |
||||
nick="dummynickforsign", |
||||
cmd="command1", |
||||
msg="msgforsign", |
||||
msg_to_be_signed="fullmsgforsign", |
||||
hostid="hostid1") |
||||
self.defaultCallbacks(d2) |
||||
#To test, this must include a valid ecdsa sig |
||||
fullmsg = "fullmsgforverify" |
||||
priv = "aa"*32 + "01" |
||||
pub = bitcoin.privkey_to_pubkey(priv) |
||||
sig = bitcoin.ecdsa_sign(fullmsg, priv) |
||||
d3 = self.callRemote(JMRequestMsgSigVerify, |
||||
msg="msgforverify", |
||||
fullmsg=fullmsg, |
||||
sig=sig, |
||||
pubkey=pub, |
||||
nick="dummynickforverify", |
||||
hashlen=4, |
||||
max_encoded=5, |
||||
hostid="hostid2") |
||||
self.defaultCallbacks(d3) |
||||
d4 = self.callRemote(JMSigReceived, |
||||
nick="dummynick", |
||||
sig="dummysig") |
||||
self.defaultCallbacks(d4) |
||||
return {'accepted': True} |
||||
|
||||
|
||||
@JMMsgSignature.responder |
||||
def on_JM_MSGSIGNATURE(self, nick, cmd, msg_to_return, hostid): |
||||
show_receipt("JMMSGSIGNATURE", nick, cmd, msg_to_return, hostid) |
||||
return {'accepted': True} |
||||
|
||||
@JMMsgSignatureVerify.responder |
||||
def on_JM_MSGSIGNATURE_VERIFY(self, verif_result, nick, fullmsg, hostid): |
||||
show_receipt("JMMSGSIGVERIFY", verif_result, nick, fullmsg, hostid) |
||||
return {'accepted': True} |
||||
|
||||
class JMTestServerProtocolFactory(protocol.ServerFactory): |
||||
protocol = JMTestServerProtocol |
||||
|
||||
class DummyClientProtocolFactory(JMTakerClientProtocolFactory): |
||||
def buildProtocol(self, addr): |
||||
return JMTakerClientProtocol(self, self.taker, nick_priv="aa"*32) |
||||
|
||||
def test_jm_protocol(): |
||||
"""We cannot use parametrize for different options as |
||||
we can't run in sequence; hence, parameters hardcoded as lists here |
||||
""" |
||||
params = [[False, False], [True, False], [False, True], [-1, False]] |
||||
global clientfactory |
||||
load_program_config() |
||||
jm_single().maker_timeout_sec = 1 |
||||
reactor.listenTCP(27184, JMTestServerProtocolFactory()) |
||||
clientfactories = [] |
||||
takers = [DummyTaker(None, None) for _ in range(len(params))] |
||||
for i, p in enumerate(params): |
||||
takers[i].set_fail_init(p[0]) |
||||
takers[i].set_fail_utxos(p[1]) |
||||
if i != 0: |
||||
clientfactories.append(JMTakerClientProtocolFactory(takers[i])) |
||||
reactor.connectTCP("localhost", 27184, clientfactories[i]) |
||||
else: |
||||
clientfactories.append(DummyClientProtocolFactory(takers[i])) |
||||
clientfactory = clientfactories[0] |
||||
start_reactor("localhost", 27184, clientfactories[0]) |
||||
print("Got here") |
||||
if not test_completed: |
||||
raise Exception("Failed test") |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python |
||||
from __future__ import print_function |
||||
from commontest import DummyBlockchainInterface |
||||
import pytest |
||||
|
||||
from jmclient import (load_program_config, jm_single) |
||||
from jmclient.commitment_utils import get_utxo_info, validate_utxo_data |
||||
from taker_test_data import (t_utxos_by_mixdepth, t_selected_utxos, t_orderbook, |
||||
t_maker_response, t_chosen_orders, t_dummy_ext) |
||||
|
||||
def test_get_utxo_info(): |
||||
load_program_config() |
||||
jm_single().config.set("BLOCKCHAIN", "network", "mainnet") |
||||
dbci = DummyBlockchainInterface() |
||||
privkey = "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi" |
||||
#to verify use from_wif_privkey and privkey_to_address |
||||
iaddr = "1LDsjB43N2NAQ1Vbc2xyHca4iBBciN8iwC" |
||||
fakeutxo = "aa"*32+":08" |
||||
|
||||
fake_query_results = [{'value': 200000000, |
||||
'address': iaddr, |
||||
'utxo': fakeutxo, |
||||
'confirms': 20}] |
||||
dbci.insert_fake_query_results(fake_query_results) |
||||
jm_single().bc_interface = dbci |
||||
u, priv = get_utxo_info(fakeutxo + "," + privkey) |
||||
assert u == fakeutxo |
||||
assert priv == privkey |
||||
#invalid format |
||||
with pytest.raises(Exception) as e_info: |
||||
u, priv = get_utxo_info(fakeutxo + privkey) |
||||
#invalid index |
||||
fu2 = "ab"*32 + ":00004" |
||||
with pytest.raises(Exception) as e_info: |
||||
u, priv = get_utxo_info(fu2 + "," + privkey) |
||||
#invalid privkey |
||||
p2 = privkey[:-1] + 'j' |
||||
with pytest.raises(Exception) as e_info: |
||||
u, priv = get_utxo_info(fakeutxo + "," + p2) |
||||
|
||||
utxodatas = [(fakeutxo, privkey)] |
||||
retval = validate_utxo_data(utxodatas, False) |
||||
assert retval |
||||
#try to retrieve |
||||
retval = validate_utxo_data(utxodatas, True) |
||||
assert retval[0] == (fakeutxo, 200000000) |
||||
fake_query_results[0]['address'] = "fakeaddress" |
||||
dbci.insert_fake_query_results(fake_query_results) |
||||
#validate should fail for wrong address |
||||
retval = validate_utxo_data(utxodatas, False) |
||||
assert not retval |
||||
#remove fake query result and trigger not found |
||||
dbci.fake_query_results = None |
||||
dbci.setQUSFail(True) |
||||
retval = validate_utxo_data(utxodatas, False) |
||||
assert not retval |
||||
dbci.setQUSFail(False) |
||||
|
||||
Loading…
Reference in new issue