Browse Source

Add test coverage of Taker

Also, some minor changes: filter_orders_callback taken outside
Taker since it involves user input necessarily. Related, answeryes
removed from module, can be done outside.
Donation code explicitly removed from all code branches for now.
A couple of very minor changes thrown up from testing.
Coverage of Taker now 100%, will work on the rest of jmclient.
master
Adam Gibson 9 years ago
parent
commit
7a1354fa22
No known key found for this signature in database
GPG Key ID: B3AE09F1E9A3197A
  1. 70
      jmclient/jmclient/taker.py
  2. 190
      jmclient/test/taker_test_data.py
  3. 510
      jmclient/test/test_taker.py
  4. 3
      jmdaemon/test/test_enc_wrapper.py
  5. 24
      scripts/sendpayment.py
  6. 0
      test/randomfunc_test.py

70
jmclient/jmclient/taker.py

@ -28,7 +28,6 @@ class Taker(object):
def __init__(self,
wallet,
schedule,
answeryes,
order_chooser=weighted_order_choose,
sign_method=None,
callbacks=None):
@ -43,7 +42,6 @@ class Taker(object):
"""
self.wallet = wallet
self.schedule = schedule
self.answeryes = answeryes
self.order_chooser = order_chooser
self.ignored_makers = None
self.txid = None
@ -51,48 +49,23 @@ class Taker(object):
#allow custom wallet-based clients to use their own signing code;
#currently only setting "wallet" is allowed, calls wallet.sign_tx(tx)
self.sign_method = sign_method
#External callers can set any of the 3 callbacks for filtering orders,
#External callers set the 3 callbacks for filtering orders,
#sending info messages to client, and action on completion.
#"None" is allowable for taker_info_callback, defaults to log msg.
if callbacks:
"""Signature of filter_orders:
args: orders_fees, cjamount
returns: boolean representing accept/reject
"""
self.filter_orders_callback = callbacks[0]
if not self.filter_orders_callback:
self.filter_orders_callback = self.default_filter_orders_callback
self.taker_info_callback = callbacks[1]
if not self.taker_info_callback:
self.taker_info_callback = self.default_taker_info_callback
self.on_finished_callback = callbacks[2]
if not self.on_finished_callback:
self.on_finished_callback = self.default_on_finished_callback
def default_taker_info_callback(self, infotype, msg):
jlog.debug(infotype + ":" + msg)
def default_filter_orders_callback(self, orders_fees):
orders, total_cj_fee = orders_fees
jlog.info("Chose these orders: " +pprint.pformat(orders))
jlog.info('total cj fee = ' + str(total_cj_fee))
total_fee_pc = 1.0 * total_cj_fee / self.cjamount
jlog.info('total coinjoin fee = ' + str(float('%.3g' % (
100.0 * total_fee_pc))) + '%')
WARNING_THRESHOLD = 0.02 # 2%
if total_fee_pc > WARNING_THRESHOLD:
jlog.info('\n'.join(['=' * 60] * 3))
jlog.info('WARNING ' * 6)
jlog.info('\n'.join(['=' * 60] * 1))
jlog.info('OFFERED COINJOIN FEE IS UNUSUALLY HIGH. DOUBLE/TRIPLE CHECK.')
jlog.info('\n'.join(['=' * 60] * 1))
jlog.info('WARNING ' * 6)
jlog.info('\n'.join(['=' * 60] * 3))
if not self.answeryes:
if raw_input('send with these orders? (y/n):')[0] != 'y':
self.on_finished_callback(False)
return False
return True
def default_on_finished_callback(self, result, fromtx=False):
"""Currently not possible without access to the client protocol factory"""
raise NotImplementedError
def initialize(self, orderbook):
"""Once the daemon is active and has returned the current orderbook,
select offers, re-initialize variables and prepare a commitment,
@ -150,8 +123,10 @@ class Taker(object):
self.ignored_makers)
if self.filter_orders_callback:
accepted = self.filter_orders_callback([self.orderbook,
self.total_cj_fee])
self.total_cj_fee],
self.cjamount)
if not accepted:
self.on_finished_callback(False)
return False
return True
@ -211,7 +186,8 @@ class Taker(object):
"Could not find orders to complete transaction")
self.on_finished_callback(False)
return False
if not self.filter_orders_callback((self.orderbook, total_cj_fee)):
if not self.filter_orders_callback((self.orderbook, self.total_cj_fee),
self.cjamount):
self.on_finished_callback(False)
return False
@ -262,7 +238,6 @@ class Taker(object):
#this will not be added to the transaction, so we will have
#to recheck if we have enough
continue
total_input = sum([d['value'] for d in utxo_data])
real_cjfee = calc_cj_fee(self.orderbook[nick]['ordertype'],
self.orderbook[nick]['cjfee'],
@ -367,8 +342,13 @@ class Taker(object):
address/pubkey that will be used for coinjoining
with an ecdsa verification.
"""
if not btc.ecdsa_verify(maker_pk, btc_sig, auth_pub):
jlog.debug('signature didnt match pubkey and message')
try:
if not btc.ecdsa_verify(maker_pk, btc_sig, auth_pub):
jlog.debug('signature didnt match pubkey and message')
return False
except Exception as e:
jlog.info("Failed ecdsa verify for maker pubkey: " + str(maker_pk))
jlog.info("Exception was: " + repr(e))
return False
return True
@ -383,8 +363,8 @@ class Taker(object):
for index, ins in enumerate(self.latest_tx['ins']):
utxo_for_checking = ins['outpoint']['hash'] + ':' + str(ins[
'outpoint']['index'])
if (ins['script'] != '' or
utxo_for_checking in self.input_utxos.keys()):
#'deadbeef' markers mean our own input scripts are not ''
if (ins['script'] != ''):
continue
utxo[ctr] = [index, utxo_for_checking]
ctr += 1
@ -534,17 +514,15 @@ class Taker(object):
if self.my_cj_addr:
return self.my_cj_addr
else:
addr, self.sign_k = donation_address()
return addr
#Note: donation code removed (possibly temporarily)
raise NotImplementedError
def sign_tx(self, tx, i, priv):
if self.my_cj_addr:
return btc.sign(tx, i, priv)
else:
return btc.sign(tx,
i,
priv,
usenonce=btc.safe_hexlify(self.sign_k))
#Note: donation code removed (possibly temporarily)
raise NotImplementedError
def self_sign(self):
# now sign it ourselves

190
jmclient/test/taker_test_data.py

@ -0,0 +1,190 @@
#orderbook
t_orderbook = [{u'counterparty': u'J5FA1Gj7Ln4vSGne', u'ordertype': u'reloffer', u'oid': 0,
u'minsize': 7500000, u'txfee': 1000, u'maxsize': 599972700, u'cjfee': u'0.0002'},
{u'counterparty': u'J5CFffuuewjG44UJ', u'ordertype': u'reloffer', u'oid': 0,
u'minsize': 7500000, u'txfee': 1000, u'maxsize': 599972700, u'cjfee': u'0.0002'},
{u'counterparty': u'J55z23xdjxJjC7er', u'ordertype': u'reloffer', u'oid': 0,
u'minsize': 7500000, u'txfee': 1000, u'maxsize': 599972700, u'cjfee': u'0.0002'},
{u'counterparty': u'J54Ghp5PXCdY9H3t', u'ordertype': u'reloffer', u'oid': 0,
u'minsize': 7500000, u'txfee': 1000, u'maxsize': 599972700, u'cjfee': u'0.0002'},
{u'counterparty': u'J559UPUSLLjHJpaB', u'ordertype': u'reloffer', u'oid': 0,
u'minsize': 7500000, u'txfee': 1000, u'maxsize': 599972700, u'cjfee': u'0.0002'},
{u'counterparty': u'J5cBx1FwUVh9zzoO', u'ordertype': u'reloffer', u'oid': 0,
u'minsize': 7500000, u'txfee': 1000, u'maxsize': 599972700, u'cjfee': u'0.0002'}]
t_dest_addr = "mvw1NazKDRbeNufFANqpYNAANafsMC2zVU"
t_chosen_orders = {u'J559UPUSLLjHJpaB': {u'cjfee': u'0.0002',
u'counterparty': u'J559UPUSLLjHJpaB',
u'maxsize': 599972700,
u'minsize': 7500000,
u'oid': 0,
u'ordertype': u'reloffer',
u'txfee': 1000},
u'J55z23xdjxJjC7er': {u'cjfee': u'0.0002',
u'counterparty': u'J55z23xdjxJjC7er',
u'maxsize': 599972700,
u'minsize': 7500000,
u'oid': 0,
u'ordertype': u'reloffer',
u'txfee': 1000},
u'J5CFffuuewjG44UJ': {u'cjfee': u'0.0002',
u'counterparty': u'J5CFffuuewjG44UJ',
u'maxsize': 599972700,
u'minsize': 7500000,
u'oid': 0,
u'ordertype': u'reloffer',
u'txfee': 1000}}
"""
2016-12-01 15:27:33,351 [MainThread ] [INFO ] total cj fee = 63000
2016-12-01 15:27:33,351 [MainThread ] [INFO ] total coinjoin fee = 0.0573%
2016-12-01 15:27:34,887 [MainThread ] [DEBUG] INFO:Preparing bitcoin data..
2016-12-01 15:27:34,888 [MainThread ] [DEBUG] rpc: getaccount ['myzi6K9vt88rdiXpYayfJkU1x33G1wz2fP']
2016-12-01 15:27:34,889 [MainThread ] [DEBUG] total estimated amount spent = 110093000
"""
t_utxos_by_mixdepth = {0: {u'534b635ed8891f16c4ec5b8236ae86164783903e8e8bb47fa9ef2ca31f3c2d7a:0': {'address': u'mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ',
'value': 200000000}},
1: {u'0780d6e5e381bff01a3519997bb4fcba002493103a198fde334fd264f9835d75:1': {'address': u'mvtY8DVgn3TtvjHbVsauYoSQjAhNqVyqmM',
'value': 200000000},
u'7e574db96a4d43a99786b3ea653cda9e4388f377848f489332577e018380cff1:0': {'address': u'n3nELhmU2D7ebGYzJnGFWgVDK3cYErmTcQ',
'value': 200000000},
u'dd9711a2ef340750db21efb761f5f7d665d94b312332dc354e252c77e9c48349:0': {'address': u'mxeLuX8PP7qLkcM8uarHmdZyvP1b5e1Ynf',
'value': 200000000}},
2: {},
3: {},
4: {}}
t_selected_utxos = [{'utxo': u'0780d6e5e381bff01a3519997bb4fcba002493103a198fde334fd264f9835d75:1',
'value': 200000000}]
t_generated_podle = {'P': '025a2e04dc6bd5f58fe4eb13045b27f0dd17c39524264639f48607347cf6d69c4e',
'P2': '0223e54e9917d8482f1b54ead8e941907c17051b95397e8bc110adc6681d8d44c8',
'commit': 'aa0545c9ed918e66f86df467c96a4978529b836aa4688df682a2db4e27d4ed9d',
'e': '5b7ab1fa21287bbf0df4a0c46f6c31c3f17887ee9ea6ae584fc3a861ae9f1e9d',
'sig': 'ebe25d7b2d667de802677c30c6fea07386f0cd67d4e4c795e4a6ebc39b21eb39',
'used': 'False',
'utxo': u'0780d6e5e381bff01a3519997bb4fcba002493103a198fde334fd264f9835d75:1'}
t_maker_response = {"J559UPUSLLjHJpaB":
[["03243f4a659e278a1333f8308f6aaf32db4692ee7df0340202750fd6c09150f6:1"],
"03a2d1cbe977b1feaf8d0d5cc28c686859563d1520b28018be0c2661cf1ebe4857",
"mrKTGvFfYUEqk52qPKUroumZJcpjHLQ6pn",
"mxPnzFkCQpPzVQdajNLoT4us5pTPsQZZZp",
"MEQCIBeGrtxxVrj5tSUX6vEetmzE8nRBG/guSXq3SrqypIt5AiAnIZzDUXu8DtODgF2p1Bo27L8VcG1GJSfatZbS23YZQQ==",
"5bcc7ae1a3530e454812668620aced47d774bf06a1f5870d531422a1a958b629"],
"J55z23xdjxJjC7er":
[["498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3:0"],
"02b4b749d54e96b04066b0803e372a43d6ffa16e75a001ae0ed4b235674ab286be",
"mhatyHdna3Qt5FtnfwWaMVV1dohCaDYF3T",
"mjJoVN2HCUGVDvNebiFnHdB3zF56bxQm5z",
"MEQCIBlMF7DRbhr14e74He9m+UYjR5y8jjvP7TvUh8valebmAiBoIGjl436fsYim9pKSTbCKiBmT82hQ98LvIOGSLprk0A==",
"8204d1cba30d4cdabab16a5e8d10d17464e24c78a6f887ae2d920b223c030d28"],
"J5CFffuuewjG44UJ":
[["3f3ea820d706e08ad8dc1d2c392c98facb1b067ae4c671043ae9461057bd2a3c:1"],
"023bcbafb4f68455e0d1d117c178b0e82a84e66414f0987453d78da034b299c3a9",
"mpAEocXy8ckcJBo3fhQg9Mv1kfEzAuUivX",
"n29NWbsyq5MjCMC5ykjStd78zwfjvCvJJZ",
"MEUCIQDAM5Aa0aU5iKI0b9YnNtwH0m+6sz3zeTL8f398CPjuQAIgLeU9mCJX8SupNNMkA+bsUJeRYe3kiLnzq3OlmXTxck0=",
"7377d03477485884e0129dbdb2d79f4956f5b74366d805385b6f127509a8433f"]}
"""
2016-12-01 15:27:39,914 [MainThread ] [DEBUG] rpc: gettxout [u'03243f4a659e278a1333f8308f6aaf32db4692ee7df0340202750fd6c09150f6', 1, False]
2016-12-01 15:27:39,915 [MainThread ] [DEBUG] fee breakdown for J559UPUSLLjHJpaB totalin=200000000 cjamount=110000000 txfee=1000 realcjfee=22000
2016-12-01 15:27:39,915 [MainThread ] [DEBUG] rpc: gettxout [u'498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3', 0, False]
2016-12-01 15:27:39,915 [MainThread ] [DEBUG] fee breakdown for J55z23xdjxJjC7er totalin=200000000 cjamount=110000000 txfee=1000 realcjfee=22000
2016-12-01 15:27:39,916 [MainThread ] [DEBUG] rpc: gettxout [u'3f3ea820d706e08ad8dc1d2c392c98facb1b067ae4c671043ae9461057bd2a3c', 1, False]
2016-12-01 15:27:39,916 [MainThread ] [DEBUG] fee breakdown for J5CFffuuewjG44UJ totalin=200000000 cjamount=110000000 txfee=1000 realcjfee=22000
2016-12-01 15:27:39,916 [MainThread ] [DEBUG] INFO:Got all parts, enough to build a tx
2016-12-01 15:27:39,917 [MainThread ] [DEBUG] Estimated transaction size: 870
2016-12-01 15:27:39,917 [MainThread ] [DEBUG] rpc: estimatefee [3]
2016-12-01 15:27:39,917 [MainThread ] [DEBUG] got estimated tx bytes: 870
2016-12-01 15:27:39,917 [MainThread ] [INFO ] Based on initial guess: 30000, we estimated a miner fee of: 26100
2016-12-01 15:27:39,918 [MainThread ] [INFO ] fee breakdown for me totalin=200000000 my_txfee=23100 makers_txfee=3000 cjfee_total=66000 => changevalue=89910900
"""
t_obtained_tx = {'ins': [{'outpoint': {'hash': '03243f4a659e278a1333f8308f6aaf32db4692ee7df0340202750fd6c09150f6',
'index': 1},
'script': '',
'sequence': 4294967295},
{'outpoint': {'hash': '3f3ea820d706e08ad8dc1d2c392c98facb1b067ae4c671043ae9461057bd2a3c',
'index': 1},
'script': '',
'sequence': 4294967295},
{'outpoint': {'hash': '498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3',
'index': 0},
'script': '',
'sequence': 4294967295},
{'outpoint': {'hash': '0780d6e5e381bff01a3519997bb4fcba002493103a198fde334fd264f9835d75',
'index': 1},
'script': '',
'sequence': 4294967295}],
'locktime': 0,
'outs': [{'script': '76a914767c956efe6092a775fea39a06d1cac9aae956d788ac',
'value': 110000000},
{'script': '76a914cab20e3270988ac99651b8f079a3b4c93b996a6888ac',
'value': 89910900},
{'script': '76a914b91f75254b5fa1510cc944a2206ed72235d0d88188ac',
'value': 90021000},
{'script': '76a914a916707952c2df28a3abf3ee692dfbbd5a4d74dc88ac',
'value': 110000000},
{'script': '76a914e245b480b46bcbc9d13e68766ad19909decd135288ac',
'value': 90021000},
{'script': '76a91416af241bb1db02dfd7c65989bbab190ac489ccc188ac',
'value': 110000000},
{'script': '76a9142994295a9d4d083eb792e669e3211007dc78928888ac',
'value': 90021000},
{'script': '76a9145ece2dac945c8ff5b2b6635360ca0478ade305d488ac',
'value': 110000000}],
'version': 1}
#signatures from makers
"""
nick=J5CFffuuewjG44UJ message=!sig xH9IAMo2fvG+g+DAbLNOPsGsJCDm6r+ZY5QM7p+SRsixbqSwXcBQAn7Mnw1rS+uGlrJkM8ossX5VHKjdKDhTXQVLawR7XgiVFFFiO+/FjdFhqVuS4Q/NgOlb7nCBe/UaBebd9NpuURG+8u/V+46jtqKRtVsSO1+QZQBt2nSpYCqxWIjxMowRxS4O/zlrOVbyjv/AjchOajufKJwckkrkJDyQDYlUdW+eqs43tf0XsJ9k4NHRVVHAQQ== 036558f550b1d398d2325d892e50ef25b0f663ae13f70d0b304a15f07030061ace MEUCIQCE9MgU+HfcHkKE8zNzNeCEdDBJuQatA6C2sTJ9mVKK7wIgX4w9r0tz4s9qeuW0UjNliDatJ4X7pS3/atADSqPat0U=
nick=J559UPUSLLjHJpaB message=!sig geHTf1n88eKeUnVOj7bIrJF1KFCN03IQhZD0cR17Q7jPSn2DZrrvMaRNkjZRyF+zGnWFwd69kwLRU0ftCaMf/3lw+05UovVCREiyXWUtPJa7XAY2NW4iMmTnGTp8f9RLgDcDhiZayKXTpzBDC9r6WAt6wiD0lej5uw7dmluKSUyfXW8sOYPmLm4iJAPcbGeJiQfiR9zBeX8w+6Kz4bkaiue41SzQP/h9avPV2XIX4kVQQ3jLfQyHww== 038f90ab260df440cef82a981146b509eb9df019884e145158230e8babc17d7be4 MEQCIEo5Pau9zqW2lw+B2AYTYuTO5TDbBkgsOk0bqT+SQctKAiBO1nbsmYTy7E0Qd7jAxko1Gq6Yk0Q6DerByuEuk5IBSQ==
nick=J55z23xdjxJjC7er message=!sig A5CWvqmYCOiZBEEi9iHVpQL0oO9B7VIIzuU9QhkzXOw+iD916C9b+Yk3eTxrtf+qaLARQ7eui6zdPNek95EdmqCEqM/myeeuBVSy9KrcB9xU0sdnuCu4+g13jVe9Pkvd1iizZ8GCNP7SejEzeltNr0a1lR+M0kKtj4XI+nDTxhisSzL8PDXsqoOMcrDjegna3TZsJeKviu8r/1T/zWwTQtRCXqruLnflqXNLtZoyFmoaO1GurgkNHA== 029a8beadec242f04f2295787ac0175b960e2d68d115ec65c4310de7ce3fa2cec0 MEQCIHpTxVkwtvm7agbp47Z5V0We8jxXkfZDUFsW2tZwTZdHAiA9JnYvo74hF3RihzHw2l+ufTOmC/3ddBpxkB9+AdZvzA==
"""
"""
2016-12-01 15:27:39,921 [MainThread ] [DEBUG] INFO:Built tx, sending to counterparties.
2016-12-01 15:27:39,968 [MainThread ] [DEBUG] rpc: gettxout ['03243f4a659e278a1333f8308f6aaf32db4692ee7df0340202750fd6c09150f6', 1, False]
2016-12-01 15:27:39,968 [MainThread ] [DEBUG] rpc: gettxout ['3f3ea820d706e08ad8dc1d2c392c98facb1b067ae4c671043ae9461057bd2a3c', 1, False]
2016-12-01 15:27:39,969 [MainThread ] [DEBUG] rpc: gettxout ['498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3', 0, False]
2016-12-01 15:27:39,971 [MainThread ] [DEBUG] found good sig at index=1
2016-12-01 15:27:39,971 [MainThread ] [DEBUG] nick = J5CFffuuewjG44UJ sent all sigs, removing from nonrespondant list
2016-12-01 15:27:39,971 [MainThread ] [DEBUG] rpc: gettxout ['03243f4a659e278a1333f8308f6aaf32db4692ee7df0340202750fd6c09150f6', 1, False]
2016-12-01 15:27:39,972 [MainThread ] [DEBUG] rpc: gettxout ['498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3', 0, False]
2016-12-01 15:27:39,973 [MainThread ] [DEBUG] found good sig at index=0
2016-12-01 15:27:39,973 [MainThread ] [DEBUG] nick = J559UPUSLLjHJpaB sent all sigs, removing from nonrespondant list
2016-12-01 15:27:43,937 [MainThread ] [DEBUG] rpc: gettxout ['498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3', 0, False]
2016-12-01 15:27:43,938 [MainThread ] [DEBUG] found good sig at index=2
2016-12-01 15:27:43,938 [MainThread ] [DEBUG] nick = J55z23xdjxJjC7er sent all sigs, removing from nonrespondant list
2016-12-01 15:27:43,938 [MainThread ] [DEBUG] all makers have sent their signatures
2016-12-01 15:27:43,938 [MainThread ] [DEBUG] INFO:Transaction is valid, signing..
2016-12-01 15:27:43,943 [MainThread ] [DEBUG]
"""
t_raw_signed_tx = "0100000004f65091c0d60f75020234f07dee9246db32af6a8f30f833138a279e654a3f2403010000006b483045022100ad522388ce9eacf2760e4d6bd6a114a0e15b88879b430fbb2e60df947494df2402201f49338726599eb0980873aef268d8d890de2792967ff28f0c11eb35e54ff07a012103a2d1cbe977b1feaf8d0d5cc28c686859563d1520b28018be0c2661cf1ebe4857ffffffff3c2abd571046e93a0471c6e47a061bcbfa982c392c1ddcd88ae006d720a83e3f010000006a473044022012bbfa6ef7b0416e00001d90b022d6663f5fd57d9a07bb70b887510f7c44902d022059b382bfb1ff5588a518fc69c55b2ff67d3facc11088e984d7c09c336d4875330121023bcbafb4f68455e0d1d117c178b0e82a84e66414f0987453d78da034b299c3a9ffffffffc3d450f208681405a0c7f0b46816f2121ef302e20c6b3c443b4f53228baa8f49000000006b48304502210081019ea7b68130da4230fd748668c776043004843de50e07bb5fcb42e7632aed022000a34878274e583eec64815d1b587e7fbcd9ac714e722773ac1e69f1209f2e10012102b4b749d54e96b04066b0803e372a43d6ffa16e75a001ae0ed4b235674ab286beffffffff755d83f964d24f33de8f193a10932400bafcb47b9919351af0bf81e3e5d68007010000006b483045022100add68e9532a50ca5585999290531f26e515bdf3d001519b0de8dd6b981daec7f02200b34c58ce61e6673c9efc5bf82cacd4d02673bc1c6cbaba45b5c65579776b8180121025a2e04dc6bd5f58fe4eb13045b27f0dd17c39524264639f48607347cf6d69c4effffffff0880778e06000000001976a914767c956efe6092a775fea39a06d1cac9aae956d788ac74ee5b05000000001976a914cab20e3270988ac99651b8f079a3b4c93b996a6888ac889c5d05000000001976a914b91f75254b5fa1510cc944a2206ed72235d0d88188ac80778e06000000001976a914a916707952c2df28a3abf3ee692dfbbd5a4d74dc88ac889c5d05000000001976a914e245b480b46bcbc9d13e68766ad19909decd135288ac80778e06000000001976a91416af241bb1db02dfd7c65989bbab190ac489ccc188ac889c5d05000000001976a9142994295a9d4d083eb792e669e3211007dc78928888ac80778e06000000001976a9145ece2dac945c8ff5b2b6635360ca0478ade305d488ac00000000"
t_txid = "4d5bfad9bbfb93eb1e25fb2e6c832323d1bf39e63f6ed2319b65e85354c7ca70"
t_dummy_ext = {"used": [], "external": {
"79f1b8df7d0978f30028487c6c4e0eae96d1aa18e01f13bb4cba6788590cd431:1": {
"reveal": {
"1": {
"P2": "0329d4b4bb28c1a0747c1a5daad59763a9021b5e1fa957887a90c7849789a683b6",
"s": "a303cad939fb773dd16a81c44f210afe0b985a2cf9a63b033139455b70c77be6",
"e": "64f5b9861b95434ab84bd044b93a28f85ea94b474237992d899bd4302eef3820"
},
"0": {
"P2": "02681ed66595daf98b12d6d69d8afb8d14a531eeaea1161bce8b9f2666ea55f157",
"s": "ed994ad173431bd0f53c82fee70d202e9c2adce492b6226d3cb4116cc3a08383",
"e": "1dd7f56fe83ca66e89b3ec3b73fa44edacab0ef4524652c415065dbf91500c85"
},
"2": {
"P2": "02cdd5ced7e79bdb651d6d1883e0047509793a9a9e3da4ae516b8a853b9cdd8e98",
"s": "39a19287c4bacc823559d0e1b907e311c31d8a13f45fe30d10b133561113515c",
"e": "a0e7cd319c7e51c6f9e503e95d08c3d2398f9b546c2d64178b6c113c63c29d78"
}
},
"P": "033749d513d0e0239a75892556a6ce01c3e48f82e75169129abe8ef370ab992c94"
}}}

510
jmclient/test/test_taker.py

@ -0,0 +1,510 @@
#!/usr/bin/env python
from __future__ import print_function
from jmclient import AbstractWallet
from jmclient import BlockchainInterface
from jmclient import Taker
import jmbitcoin as bitcoin
import binascii
import os
import copy
import shutil
import pytest
import json
from base64 import b64encode
from jmclient import (load_program_config, jm_single, set_commitment_file,
get_commitment_file)
from taker_test_data import (t_utxos_by_mixdepth, t_selected_utxos, t_orderbook,
t_maker_response, t_chosen_orders, t_dummy_ext)
class DummyWallet(AbstractWallet):
def __init__(self):
super(DummyWallet, self).__init__()
self.max_mix_depth = 5
self.inject_addr_get_failure = False
def get_utxos_by_mixdepth(self):
return t_utxos_by_mixdepth
def select_utxos(self, mixdepth, amount):
if amount > self.get_balance_by_mixdepth()[mixdepth]:
raise Exception("Not enough funds")
return self.get_utxos_by_mixdepth()[mixdepth]
def get_internal_addr(self, mixing_depth):
if self.inject_addr_get_failure:
raise Exception("address get failure")
return "mxeLuX8PP7qLkcM8uarHmdZyvP1b5e1Ynf"
def sign_tx(self, tx, addrs):
print("Pretending to sign on addresses: " + str(addrs))
return tx
def get_key_from_addr(self, addr):
"""usable addresses: privkey all 1s, 2s, 3s, ... :"""
privs = [x*32 + "\x01" for x in [chr(y) for y in range(1,6)]]
addrs = {}
"""
mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ
n31WD8pkfAjg2APV78GnbDTdZb1QonBi5D
mmVEKH61BZbLbnVEmk9VmojreB4G4PmBPd
msxyyydNXTiBmt3SushXbH5Qh2ukBAThk3
musGZczug3BAbqobmYherywCwL9REgNaNm
"""
for p in privs:
addrs[p] = bitcoin.privkey_to_address(p, False, magicbyte=0x6f)
for p, a in addrs.iteritems():
if a == addr:
return binascii.hexlify(p)
raise ValueError("No such keypair")
class DummyBlockchainInterface(BlockchainInterface):
def __init__(self):
self.fake_query_results = None
self.qusfail = False
def sync_addresses(self, wallet):
pass
def sync_unspent(self, wallet):
pass
def add_tx_notify(self,
txd,
unconfirmfun,
confirmfun,
notifyaddr,
timeoutfun=None):
pass
def pushtx(self, txhex):
print("pushing: " + str(txhex))
return True
def insert_fake_query_results(self, fqr):
self.fake_query_results = fqr
def setQUSFail(self, state):
self.qusfail = state
def query_utxo_set(self, txouts,includeconf=False):
if self.qusfail:
#simulate failure to find the utxo
return [None]
if self.fake_query_results:
result = []
for x in self.fake_query_results:
for y in txouts:
if y == x['utxo']:
result.append(x)
return result
result = []
#external maker utxos
known_outs = {"03243f4a659e278a1333f8308f6aaf32db4692ee7df0340202750fd6c09150f6:1": "03a2d1cbe977b1feaf8d0d5cc28c686859563d1520b28018be0c2661cf1ebe4857",
"498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3:0": "02b4b749d54e96b04066b0803e372a43d6ffa16e75a001ae0ed4b235674ab286be",
"3f3ea820d706e08ad8dc1d2c392c98facb1b067ae4c671043ae9461057bd2a3c:1": "023bcbafb4f68455e0d1d117c178b0e82a84e66414f0987453d78da034b299c3a9"}
#our wallet utxos, faked, for podle tests: utxos are doctored (leading 'f'),
#and the lists are (amt, age)
wallet_outs = {'f34b635ed8891f16c4ec5b8236ae86164783903e8e8bb47fa9ef2ca31f3c2d7a:0': [10000000, 2],
'f780d6e5e381bff01a3519997bb4fcba002493103a198fde334fd264f9835d75:1': [20000000, 6],
'fe574db96a4d43a99786b3ea653cda9e4388f377848f489332577e018380cff1:0': [50000000, 3],
'fd9711a2ef340750db21efb761f5f7d665d94b312332dc354e252c77e9c48349:0': [50000000, 6]}
if includeconf and set(txouts).issubset(set(wallet_outs)):
#includeconf used as a trigger for a podle check;
#here we simulate a variety of amount/age returns
results = []
for to in txouts:
results.append({'value': wallet_outs[to][0],
'confirms': wallet_outs[to][1]})
return results
if txouts[0] in known_outs:
return [{'value': 200000000,
'address': bitcoin.pubkey_to_address(known_outs[txouts[0]], magicbyte=0x6f),
'confirms': 20}]
for t in txouts:
result_dict = {'value': 10000000000,
'address': "mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ"}
if includeconf:
result_dict['confirms'] = 20
result.append(result_dict)
return result
def estimate_fee_per_kb(self, N):
return 30000
def dummy_order_chooser():
return t_chosen_orders
def taker_finished(res, fromtx=False):
print("called taker finished callback")
def dummy_filter_orderbook(orders_fees, cjamount):
print("calling dummy filter orderbook")
return True
def get_taker(schedule=None, schedule_len=0, sign_method=None, on_finished=None,
filter_orders=None):
if not schedule:
schedule = ['a']*schedule_len #note, for taker.initalize() this will result in junk
print("Using schedule: " + str(schedule))
on_finished_callback = on_finished if on_finished else taker_finished
filter_orders_callback = filter_orders if filter_orders else dummy_filter_orderbook
return Taker(DummyWallet(), schedule,
callbacks=[filter_orders_callback, None, on_finished_callback],
sign_method=sign_method)
def test_filter_rejection(createcmtdata):
def filter_orders_reject(orders_feesl, cjamount):
print("calling filter orders rejection")
return False
taker = get_taker(filter_orders=filter_orders_reject)
taker.schedule = [(0, 20000000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")]
res = taker.initialize(t_orderbook)
assert not res[0]
taker = get_taker(filter_orders=filter_orders_reject)
taker.schedule = [(0, 0, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")]
res = taker.initialize(t_orderbook)
assert not res[0]
@pytest.mark.parametrize(
"failquery, external",
[
(False, False),
(True, False),
(False, True),
])
def test_make_commitment(createcmtdata, failquery, external):
def clean_up():
jm_single().config.set("POLICY", "taker_utxo_age", old_taker_utxo_age)
jm_single().config.set("POLICY", "taker_utxo_amtpercent", old_taker_utxo_amtpercent)
set_commitment_file(old_commitment_file)
jm_single().bc_interface.setQUSFail(False)
os.remove('dummyext')
old_commitment_file = get_commitment_file()
with open('dummyext', 'wb') as f:
f.write(json.dumps(t_dummy_ext, indent=4))
if external:
set_commitment_file('dummyext')
old_taker_utxo_age = jm_single().config.get("POLICY", "taker_utxo_age")
old_taker_utxo_amtpercent = jm_single().config.get("POLICY", "taker_utxo_amtpercent")
jm_single().config.set("POLICY", "taker_utxo_age", "5")
jm_single().config.set("POLICY", "taker_utxo_amtpercent", "20")
mixdepth = 0
amount = 110000000
taker = get_taker([(mixdepth, amount, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")])
taker.wallet.unspent = {'f34b635ed8891f16c4ec5b8236ae86164783903e8e8bb47fa9ef2ca31f3c2d7a:0':
{'address': u'n31WD8pkfAjg2APV78GnbDTdZb1QonBi5D',
'value': 10000000},
'f780d6e5e381bff01a3519997bb4fcba002493103a198fde334fd264f9835d75:1':
{'address': u'mmVEKH61BZbLbnVEmk9VmojreB4G4PmBPd',
'value': 20000000},
'fe574db96a4d43a99786b3ea653cda9e4388f377848f489332577e018380cff1:0':
{'address': u'msxyyydNXTiBmt3SushXbH5Qh2ukBAThk3',
'value': 500000000},
'fd9711a2ef340750db21efb761f5f7d665d94b312332dc354e252c77e9c48349:0':
{'address': u'musGZczug3BAbqobmYherywCwL9REgNaNm',
'value': 500000000}}
taker.cjamount = amount
taker.input_utxos = {'f34b635ed8891f16c4ec5b8236ae86164783903e8e8bb47fa9ef2ca31f3c2d7a:0':
{'address': u'n31WD8pkfAjg2APV78GnbDTdZb1QonBi5D',
'value': 10000000}}
if failquery:
jm_single().bc_interface.setQUSFail(True)
taker.make_commitment()
clean_up()
def test_not_found_maker_utxos(createcmtdata):
taker = get_taker([(0, 20000000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")])
orderbook = copy.deepcopy(t_orderbook)
res = taker.initialize(orderbook)
taker.orderbook = copy.deepcopy(t_chosen_orders) #total_cjfee unaffected, all same
maker_response = copy.deepcopy(t_maker_response)
jm_single().bc_interface.setQUSFail(True)
res = taker.receive_utxos(maker_response)
assert not res[0]
assert res[1] == "Not enough counterparties responded to fill, giving up"
jm_single().bc_interface.setQUSFail(False)
def test_auth_pub_not_found(createcmtdata):
taker = get_taker([(0, 20000000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")])
orderbook = copy.deepcopy(t_orderbook)
res = taker.initialize(orderbook)
taker.orderbook = copy.deepcopy(t_chosen_orders) #total_cjfee unaffected, all same
maker_response = copy.deepcopy(t_maker_response)
utxos = ["03243f4a659e278a1333f8308f6aaf32db4692ee7df0340202750fd6c09150f6:1",
"498faa8b22534f3b443c6b0ce202f31e12f21668b4f0c7a005146808f250d4c3:0",
"3f3ea820d706e08ad8dc1d2c392c98facb1b067ae4c671043ae9461057bd2a3c:1"]
fake_query_results = [{'value': 200000000,
'address': "blah",
'utxo': utxos[i],
'confirms': 20} for i in range(3)]
jm_single().bc_interface.insert_fake_query_results(fake_query_results)
res = taker.receive_utxos(maker_response)
assert not res[0]
assert res[1] == "Not enough counterparties responded to fill, giving up"
jm_single().bc_interface.insert_fake_query_results(None)
@pytest.mark.parametrize(
"schedule, highfee, toomuchcoins, minmakers, notauthed, ignored, nocommit",
[
([(0, 20000000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")], False, False,
2, False, None, None),
([(0, 0, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")], False, False,
2, False, None, None), #sweep
#edge case triggers that don't fail
([(0, 0, 4, "mxeLuX8PP7qLkcM8uarHmdZyvP1b5e1Ynf")], False, False,
2, False, None, None), #sweep rounding error case
([(0, 199850001, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")], False, False,
2, False, None, None), #trigger sub dust change for taker
#edge case triggers that do fail
([(0, 199850000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")], False, False,
2, False, None, None), #trigger negative change
([(0, 199599800, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")], False, False,
2, False, None, None), #trigger sub dust change for maker
([(0, 20000000, 3, "INTERNAL")], True, False,
2, False, None, None), #test high fee
([(0, 20000000, 3, "INTERNAL")], False, False,
7, False, None, None), #test not enough cp
([(0, 80000000, 3, "INTERNAL")], False, False,
2, False, None, "30000"), #test failed commit
([(0, 20000000, 3, "INTERNAL")], False, False,
2, True, None, None), #test unauthed response
([(0, 5000000000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")], False, True,
2, False, None, None), #test too much coins
([(0, 0, 5, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")], False, False,
2, False, ["J559UPUSLLjHJpaB", "J55z23xdjxJjC7er"], None), #test inadequate for sweep
])
def test_taker_init(createcmtdata, schedule, highfee, toomuchcoins, minmakers,
notauthed, ignored, nocommit):
#these tests do not trigger utxo_retries
oldtakerutxoretries = jm_single().config.get("POLICY", "taker_utxo_retries")
oldtakerutxoamtpercent = jm_single().config.get("POLICY", "taker_utxo_amtpercent")
jm_single().config.set("POLICY", "taker_utxo_retries", "20")
def clean_up():
jm_single().config.set("POLICY", "minimum_makers", oldminmakers)
jm_single().config.set("POLICY", "taker_utxo_retries", oldtakerutxoretries)
jm_single().config.set("POLICY", "taker_utxo_amtpercent", oldtakerutxoamtpercent)
oldminmakers = jm_single().config.get("POLICY", "minimum_makers")
jm_single().config.set("POLICY", "minimum_makers", str(minmakers))
taker = get_taker(schedule)
orderbook = copy.deepcopy(t_orderbook)
if highfee:
for o in orderbook:
#trigger high-fee warning; but reset in next step
o['cjfee'] = '1.0'
if ignored:
taker.ignored_makers = ignored
if nocommit:
jm_single().config.set("POLICY", "taker_utxo_amtpercent", nocommit)
res = taker.initialize(orderbook)
if toomuchcoins or ignored:
assert not res[0]
return clean_up()
if nocommit:
print(str(res))
assert not res[0]
return clean_up()
taker.orderbook = copy.deepcopy(t_chosen_orders) #total_cjfee unaffected, all same
maker_response = copy.deepcopy(t_maker_response)
if notauthed:
#Doctor one of the maker response data fields
maker_response["J559UPUSLLjHJpaB"][1] = "xx" #the auth pub
if schedule[0][1] == 199850000:
#triggers negative change
#makers offer 3000 txfee; we estimate ~ 147*10 + 2*34 + 10=1548 bytes
#times 30k = 46440, so we pay 43440, plus maker fees = 3*0.0002*200000000
#roughly, gives required selected = amt + 163k, hence the above =
#2btc - 150k sats = 199850000 (tweaked because of aggressive coin selection)
#simulate the effect of a maker giving us a lot more utxos
taker.utxos["dummy_for_negative_change"] = ["a", "b", "c", "d", "e"]
with pytest.raises(ValueError) as e_info:
res = taker.receive_utxos(maker_response)
return clean_up()
if schedule[0][1] == 199850001:
#our own change is greater than zero but less than dust
#use the same edge case as for negative change, don't add dummy inputs
#(because we need tx creation to complete), but trigger case by
#bumping dust threshold
jm_single().BITCOIN_DUST_THRESHOLD = 10000
res = taker.receive_utxos(maker_response)
#should have succeeded to build tx
assert res[0]
#change should be none
assert not taker.my_change_addr
return clean_up()
if schedule[0][1] == 199599800:
#need to force negative fees to make this feasible
for k, v in taker.orderbook.iteritems():
v['cjfee'] = '-0.002'
# change_amount = (total_input - self.cjamount -
# self.orderbook[nick]['txfee'] + real_cjfee)
#suppose change amount is 1000 (sub dust), then solve for x;
#given that real_cjfee = -0.002*x
#change = 200000000 - x - 1000 - 0.002*x
#x*1.002 = 1999999000; x = 199599800
res = taker.receive_utxos(maker_response)
assert not res[0]
assert res[1] == "Not enough counterparties responded to fill, giving up"
return clean_up()
if schedule[0][3] == "mxeLuX8PP7qLkcM8uarHmdZyvP1b5e1Ynf":
#to trigger rounding error for sweep (change non-zero),
#modify the total_input via the values in self.input_utxos;
#the amount to trigger a 2 satoshi change is found by trial-error.
#TODO note this test is not adequate, because the code is not;
#the code does not *DO* anything if a condition is unexpected.
taker.input_utxos = copy.deepcopy(t_utxos_by_mixdepth)[0]
for k,v in taker.input_utxos.iteritems():
v["value"] = int(0.999805228 * v["value"])
res = taker.receive_utxos(maker_response)
assert res[0]
return clean_up()
res = taker.receive_utxos(maker_response)
if minmakers != 2:
assert not res[0]
assert res[1] == "Not enough counterparties responded to fill, giving up"
return clean_up()
assert res[0]
#re-calling will trigger "finished" code, since schedule is "complete".
res = taker.initialize(orderbook)
assert not res[0]
#some exception cases: no coinjoin address, no change address:
#donations not yet implemented:
taker.my_cj_addr = None
with pytest.raises(NotImplementedError) as e_info:
taker.prepare_my_bitcoin_data()
with pytest.raises(NotImplementedError) as e_info:
taker.sign_tx("a", "b", "c")
with pytest.raises(NotImplementedError) as e_info:
a = taker.coinjoin_address()
taker.wallet.inject_addr_get_failure = True
taker.my_cj_addr = "dummy"
assert not taker.prepare_my_bitcoin_data()
#clean up
return clean_up()
@pytest.mark.parametrize(
"schedule_len",
[
(7),
])
def test_unconfirm_confirm(schedule_len):
"""These functions are: do-nothing by default (unconfirm, for Taker),
and merely update schedule index for confirm (useful for schedules/tumbles).
This tests that the on_finished callback correctly reports the fromtx
variable as "False" once the schedule is complete.
"""
test_unconfirm_confirm.txflag = True
def finished_for_confirms(res, fromtx=False):
assert res #confirmed should always send true
test_unconfirm_confirm.txflag = fromtx
taker = get_taker(schedule_len=schedule_len, on_finished=finished_for_confirms)
taker.unconfirm_callback("a", "b")
for i in range(schedule_len-1):
taker.schedule_index += 1
fromtx = taker.confirm_callback("a", "b", 1)
assert test_unconfirm_confirm.txflag
taker.schedule_index += 1
fromtx = taker.confirm_callback("a", "b", 1)
assert not test_unconfirm_confirm.txflag
@pytest.mark.parametrize(
"dummyaddr, signmethod, schedule",
[
("mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ", None,
[(0, 20000000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")]),
("mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ", "wallet",
[(0, 20000000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")]),
])
def test_on_sig(createcmtdata, dummyaddr, signmethod, schedule):
#plan: create a new transaction with known inputs and dummy outputs;
#then, create a signature with various inputs, pass in in b64 to on_sig.
#in order for it to verify, the DummyBlockchainInterface will have to
#return the right values in query_utxo_set
#create 2 privkey + utxos that are to be ours
privs = [x*32 + "\x01" for x in [chr(y) for y in range(1,6)]]
utxos = [str(x)*64+":1" for x in range(5)]
fake_query_results = [{'value': 200000000,
'utxo': utxos[x],
'address': bitcoin.privkey_to_address(privs[x], False, magicbyte=0x6f),
'script': bitcoin.mk_pubkey_script(
bitcoin.privkey_to_address(privs[x], False, magicbyte=0x6f)),
'confirms': 20} for x in range(5)]
dbci = DummyBlockchainInterface()
dbci.insert_fake_query_results(fake_query_results)
jm_single().bc_interface = dbci
#make a transaction with all the fake results above, and some outputs
outs = [{'value': 100000000, 'address': dummyaddr},
{'value': 899990000, 'address': dummyaddr}]
tx = bitcoin.mktx(utxos, outs)
de_tx = bitcoin.deserialize(tx)
#prepare the Taker with the right intermediate data
taker = get_taker(schedule=schedule, sign_method=signmethod)
taker.nonrespondants=["cp1", "cp2", "cp3"]
taker.latest_tx = de_tx
#my inputs are the first 2 utxos
taker.input_utxos = {utxos[0]:
{'address': bitcoin.privkey_to_address(privs[0], False, magicbyte=0x6f),
'value': 200000000},
utxos[1]:
{'address': bitcoin.privkey_to_address(privs[1], False, magicbyte=0x6f),
'value': 200000000}}
taker.utxos = {None: utxos[:2], "cp1": [utxos[2]], "cp2": [utxos[3]], "cp3":[utxos[4]]}
for i in range(2):
# placeholders required for my inputs
taker.latest_tx['ins'][i]['script'] = 'deadbeef'
#to prepare for my signing, need to mark cjaddr:
taker.my_cj_addr = dummyaddr
#make signatures for the last 3 fake utxos, considered as "not ours":
tx3 = bitcoin.sign(tx, 2, privs[2])
sig3 = b64encode(bitcoin.deserialize(tx3)['ins'][2]['script'].decode('hex'))
taker.on_sig("cp1", sig3)
tx4 = bitcoin.sign(tx, 3, privs[3])
sig4 = b64encode(bitcoin.deserialize(tx4)['ins'][3]['script'].decode('hex'))
taker.on_sig("cp2", sig4)
tx5 = bitcoin.sign(tx, 4, privs[4])
#Before completing with the final signature, which will trigger our own
#signing, try with an injected failure of query utxo set, which should
#prevent this signature being accepted.
dbci.setQUSFail(True)
sig5 = b64encode(bitcoin.deserialize(tx5)['ins'][4]['script'].decode('hex'))
taker.on_sig("cp3", sig5)
#allow it to succeed, and try again
dbci.setQUSFail(False)
#this should succeed and trigger the we-sign code
taker.on_sig("cp3", sig5)
@pytest.mark.parametrize(
"schedule",
[
([(0, 20000000, 3, "mnsquzxrHXpFsZeL42qwbKdCP2y1esN3qw")]),
])
def test_auth_counterparty(schedule):
taker = get_taker(schedule=schedule)
first_maker_response = t_maker_response["J559UPUSLLjHJpaB"]
utxo, auth_pub, cjaddr, changeaddr, sig, maker_pub = first_maker_response
auth_pub_tweaked = auth_pub[:8] + auth_pub[6:8] + auth_pub[10:]
sig_tweaked = sig[:8] + sig[6:8] + sig[10:]
assert taker.auth_counterparty(sig, auth_pub, maker_pub)
assert not taker.auth_counterparty(sig, auth_pub_tweaked, maker_pub)
assert not taker.auth_counterparty(sig_tweaked, auth_pub, maker_pub)
@pytest.fixture(scope="module")
def createcmtdata(request):
def cmtdatateardown():
shutil.rmtree("cmtdata")
request.addfinalizer(cmtdatateardown)
if not os.path.exists("cmtdata"):
os.makedirs("cmtdata")
load_program_config()
jm_single().bc_interface = DummyBlockchainInterface()
jm_single().config.set("BLOCKCHAIN", "network", "testnet")

3
jmdaemon/test/test_enc_wrapper.py

@ -4,7 +4,8 @@ import random
import pytest
from joinmarket import init_keypair, get_pubkey, init_pubkey, as_init_encryption, NaclError
from jmdaemon import (init_keypair, get_pubkey, init_pubkey, as_init_encryption,
NaclError)
@pytest.mark.parametrize("ab_message,ba_message,num_iterations",

24
scripts/sendpayment.py

@ -236,6 +236,27 @@ def main():
wallet = BitcoinCoreWallet(fromaccount=wallet_name)
sync_wallet(wallet, fast=options.fastsync)
def filter_orders_callback(orders_fees, cjamount):
orders, total_cj_fee = orders_fees
jlog.info("Chose these orders: " +pprint.pformat(orders))
jlog.info('total cj fee = ' + str(total_cj_fee))
total_fee_pc = 1.0 * total_cj_fee / cjamount
jlog.info('total coinjoin fee = ' + str(float('%.3g' % (
100.0 * total_fee_pc))) + '%')
WARNING_THRESHOLD = 0.02 # 2%
if total_fee_pc > WARNING_THRESHOLD:
jlog.info('\n'.join(['=' * 60] * 3))
jlog.info('WARNING ' * 6)
jlog.info('\n'.join(['=' * 60] * 1))
jlog.info('OFFERED COINJOIN FEE IS UNUSUALLY HIGH. DOUBLE/TRIPLE CHECK.')
jlog.info('\n'.join(['=' * 60] * 1))
jlog.info('WARNING ' * 6)
jlog.info('\n'.join(['=' * 60] * 3))
if not options.answeryes:
if raw_input('send with these orders? (y/n):')[0] != 'y':
return False
return True
def taker_finished(res, fromtx=False):
if fromtx:
if res:
@ -256,9 +277,8 @@ def main():
jm_single().bc_interface.tick_forward_chain_interval = 10
taker = Taker(wallet,
schedule,
options.answeryes,
order_chooser=chooseOrdersFunc,
callbacks=(None, None, taker_finished))
callbacks=(filter_orders_callback, None, taker_finished))
clientfactory = JMTakerClientProtocolFactory(taker)
start_reactor("localhost", options.daemonport, clientfactory)

0
jmclient/test/randomfunc_test.py → test/randomfunc_test.py

Loading…
Cancel
Save