Browse Source
Update blockchain interface notifier thread to account for multiple concurrent notification functions from multiple yieldgens in one process instance.master
6 changed files with 221 additions and 42 deletions
@ -0,0 +1,109 @@ |
|||||||
|
#! /usr/bin/env python |
||||||
|
from __future__ import absolute_import |
||||||
|
'''Some helper functions for testing''' |
||||||
|
|
||||||
|
import sys |
||||||
|
import os |
||||||
|
import time |
||||||
|
import binascii |
||||||
|
import random |
||||||
|
from decimal import Decimal |
||||||
|
|
||||||
|
data_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) |
||||||
|
sys.path.insert(0, os.path.join(data_dir)) |
||||||
|
|
||||||
|
from jmclient import SegwitWallet, Wallet, get_log, estimate_tx_fee, jm_single |
||||||
|
import jmbitcoin as btc |
||||||
|
from jmbase import chunks |
||||||
|
|
||||||
|
log = get_log() |
||||||
|
|
||||||
|
def make_sign_and_push(ins_full, |
||||||
|
wallet, |
||||||
|
amount, |
||||||
|
output_addr=None, |
||||||
|
change_addr=None, |
||||||
|
hashcode=btc.SIGHASH_ALL, |
||||||
|
estimate_fee = False): |
||||||
|
"""Utility function for easily building transactions |
||||||
|
from wallets |
||||||
|
""" |
||||||
|
total = sum(x['value'] for x in ins_full.values()) |
||||||
|
ins = ins_full.keys() |
||||||
|
#random output address and change addr |
||||||
|
output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr |
||||||
|
change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr |
||||||
|
fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000 |
||||||
|
outs = [{'value': amount, |
||||||
|
'address': output_addr}, {'value': total - amount - fee_est, |
||||||
|
'address': change_addr}] |
||||||
|
|
||||||
|
tx = btc.mktx(ins, outs) |
||||||
|
de_tx = btc.deserialize(tx) |
||||||
|
for index, ins in enumerate(de_tx['ins']): |
||||||
|
utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) |
||||||
|
addr = ins_full[utxo]['address'] |
||||||
|
priv = wallet.get_key_from_addr(addr) |
||||||
|
if index % 2: |
||||||
|
priv = binascii.unhexlify(priv) |
||||||
|
tx = btc.sign(tx, index, priv, hashcode=hashcode) |
||||||
|
#pushtx returns False on any error |
||||||
|
print btc.deserialize(tx) |
||||||
|
push_succeed = jm_single().bc_interface.pushtx(tx) |
||||||
|
if push_succeed: |
||||||
|
return btc.txhash(tx) |
||||||
|
else: |
||||||
|
return False |
||||||
|
|
||||||
|
def make_wallets(n, |
||||||
|
wallet_structures=None, |
||||||
|
mean_amt=1, |
||||||
|
sdev_amt=0, |
||||||
|
start_index=0, |
||||||
|
fixed_seeds=None, |
||||||
|
test_wallet=False, |
||||||
|
passwords=None): |
||||||
|
'''n: number of wallets to be created |
||||||
|
wallet_structure: array of n arrays , each subarray |
||||||
|
specifying the number of addresses to be populated with coins |
||||||
|
at each depth (for now, this will only populate coins into 'receive' addresses) |
||||||
|
mean_amt: the number of coins (in btc units) in each address as above |
||||||
|
sdev_amt: if randomness in amouts is desired, specify here. |
||||||
|
Returns: a dict of dicts of form {0:{'seed':seed,'wallet':Wallet object},1:..,} |
||||||
|
Default Wallet constructor is joinmarket.Wallet, else use TestWallet, |
||||||
|
which takes a password parameter as in the list passwords. |
||||||
|
''' |
||||||
|
if len(wallet_structures) != n: |
||||||
|
raise Exception("Number of wallets doesn't match wallet structures") |
||||||
|
if not fixed_seeds: |
||||||
|
seeds = chunks(binascii.hexlify(os.urandom(15 * n)), 15 * 2) |
||||||
|
else: |
||||||
|
seeds = fixed_seeds |
||||||
|
wallets = {} |
||||||
|
for i in range(n): |
||||||
|
if test_wallet: |
||||||
|
w = TestWallet(seeds[i], max_mix_depth=5, pwd=passwords[i]) |
||||||
|
else: |
||||||
|
w = SegwitWallet(seeds[i], pwd=None, max_mix_depth=5) |
||||||
|
wallets[i + start_index] = {'seed': seeds[i], |
||||||
|
'wallet': w} |
||||||
|
for j in range(5): |
||||||
|
for k in range(wallet_structures[i][j]): |
||||||
|
deviation = sdev_amt * random.random() |
||||||
|
amt = mean_amt - sdev_amt / 2.0 + deviation |
||||||
|
if amt < 0: amt = 0.001 |
||||||
|
amt = float(Decimal(amt).quantize(Decimal(10)**-8)) |
||||||
|
jm_single().bc_interface.grab_coins( |
||||||
|
wallets[i + start_index]['wallet'].get_external_addr(j), |
||||||
|
amt) |
||||||
|
#reset the index so the coins can be seen if running in same script |
||||||
|
wallets[i + start_index]['wallet'].index[j][0] -= wallet_structures[i][j] |
||||||
|
return wallets |
||||||
|
|
||||||
|
|
||||||
|
def interact(process, inputs, expected): |
||||||
|
if len(inputs) != len(expected): |
||||||
|
raise Exception("Invalid inputs to interact()") |
||||||
|
for i, inp in enumerate(inputs): |
||||||
|
process.expect(expected[i]) |
||||||
|
process.sendline(inp) |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
#! /usr/bin/env python |
||||||
|
from __future__ import absolute_import |
||||||
|
'''Creates wallets and yield generators in regtest. |
||||||
|
Provides seed for joinmarket-qt test. |
||||||
|
This should be run via pytest, even though |
||||||
|
it's NOT part of the test-suite, because that |
||||||
|
makes it much easier to handle start up and |
||||||
|
shut down of the environment. |
||||||
|
Run it like: |
||||||
|
PYTHONPATH=.:$PYTHONPATH py.test \ |
||||||
|
--btcroot=/path/to/bitcoin/bin/ \ |
||||||
|
--btcpwd=123456abcdef --btcconf=/blah/bitcoin.conf \ |
||||||
|
--nirc=2 -s test/ygrunner.py |
||||||
|
''' |
||||||
|
from commontest import make_wallets |
||||||
|
import os |
||||||
|
import pytest |
||||||
|
import sys |
||||||
|
import time |
||||||
|
from jmclient import (YieldGeneratorBasic, ygmain, load_program_config, |
||||||
|
jm_single, sync_wallet, JMClientProtocolFactory, |
||||||
|
start_reactor) |
||||||
|
|
||||||
|
@pytest.mark.parametrize( |
||||||
|
"num_ygs, wallet_structures, mean_amt", |
||||||
|
[ |
||||||
|
# 1sp 3yg, 2 mixdepths, sweep from depth1 |
||||||
|
(2, [[1, 3, 0, 0, 0]] * 3, 2), |
||||||
|
]) |
||||||
|
def test_start_ygs(setup_ygrunner, num_ygs, wallet_structures, mean_amt): |
||||||
|
"""Set up some wallets, for the ygs and 1 sp. |
||||||
|
Then start the ygs in background and publish |
||||||
|
the seed of the sp wallet for easy import into -qt |
||||||
|
""" |
||||||
|
wallets = make_wallets(num_ygs + 1, |
||||||
|
wallet_structures=wallet_structures, |
||||||
|
mean_amt=mean_amt) |
||||||
|
#the sendpayment bot uses the last wallet in the list |
||||||
|
wallet = wallets[num_ygs]['wallet'] |
||||||
|
print "Seed : " + wallets[num_ygs]['seed'] |
||||||
|
#useful to see the utxos on screen sometimes |
||||||
|
sync_wallet(wallet) |
||||||
|
print wallet.unspent |
||||||
|
txfee = 1000 |
||||||
|
cjfee_a = 4200 |
||||||
|
cjfee_r = '0.001' |
||||||
|
ordertype = 'swreloffer' |
||||||
|
minsize = 100000 |
||||||
|
for i in range(num_ygs): |
||||||
|
|
||||||
|
cfg = [txfee, cjfee_a, cjfee_r, ordertype, minsize] |
||||||
|
sync_wallet(wallets[i]["wallet"]) |
||||||
|
yg = YieldGeneratorBasic(wallets[i]["wallet"], cfg) |
||||||
|
clientfactory = JMClientProtocolFactory(yg, proto_type="MAKER") |
||||||
|
nodaemon = jm_single().config.getint("DAEMON", "no_daemon") |
||||||
|
daemon = True if nodaemon == 1 else False |
||||||
|
rs = True if i == num_ygs - 1 else False |
||||||
|
start_reactor(jm_single().config.get("DAEMON", "daemon_host"), |
||||||
|
jm_single().config.getint("DAEMON", "daemon_port"), |
||||||
|
clientfactory, daemon=daemon, rs=rs) |
||||||
|
time.sleep(2) #give it a chance |
||||||
|
|
||||||
|
@pytest.fixture(scope="module") |
||||||
|
def setup_ygrunner(): |
||||||
|
load_program_config() |
||||||
|
|
||||||
Loading…
Reference in new issue