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.
130 lines
5.4 KiB
130 lines
5.4 KiB
#! /usr/bin/env python |
|
'''Some helper functions for testing''' |
|
|
|
import sys |
|
import os |
|
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 jmbase import get_log |
|
from jmclient import open_test_wallet_maybe, BIP32Wallet, SegwitWallet, \ |
|
estimate_tx_fee, jm_single, WalletService, BaseWallet, WALLET_IMPLEMENTATIONS |
|
from jmclient.wallet_utils import get_configured_wallet_type |
|
import jmbitcoin as btc |
|
from jmbase import chunks |
|
|
|
log = get_log() |
|
|
|
def make_sign_and_push(ins_full, |
|
wallet_service, |
|
amount, |
|
output_addr=None, |
|
change_addr=None, |
|
hashcode=btc.SIGHASH_ALL, |
|
estimate_fee = False): |
|
"""Utility function for easily building transactions |
|
from wallets. |
|
""" |
|
assert isinstance(wallet_service, WalletService) |
|
total = sum(x['value'] for x in ins_full.values()) |
|
ins = ins_full.keys() |
|
#random output address and change addr |
|
output_addr = wallet_service.get_new_addr(1, BaseWallet.ADDRESS_TYPE_INTERNAL) if not output_addr else output_addr |
|
change_addr = wallet_service.get_new_addr(0, BaseWallet.ADDRESS_TYPE_INTERNAL) 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_service.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, |
|
walletclass=SegwitWallet, |
|
mixdepths=5, |
|
fb_indices=[]): |
|
'''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. |
|
fb_indices: a list of integers in range(n), and for each of those we will |
|
use the fidelity bond wallet type (note: to actually *use* the FB feature |
|
the calling code will have to create the FB utxo, itself). Only supported |
|
if walletclass=SegwitWallet. |
|
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. |
|
''' |
|
# FIXME: this is basically the same code as test/jmclient/commontest.py |
|
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(BIP32Wallet.ENTROPY_BYTES * n)), |
|
BIP32Wallet.ENTROPY_BYTES * 2) |
|
else: |
|
seeds = fixed_seeds |
|
wallets = {} |
|
for i in range(n): |
|
assert len(seeds[i]) == BIP32Wallet.ENTROPY_BYTES * 2 |
|
|
|
# FIXME: pwd is ignored (but do we really need this anyway?) |
|
if test_wallet and passwords and i < len(passwords): |
|
pwd = passwords[i] |
|
else: |
|
pwd = None |
|
if i in fb_indices: |
|
assert walletclass == SegwitWallet, "Cannot use FB except for native segwit." |
|
wc = WALLET_IMPLEMENTATIONS[get_configured_wallet_type(True)] |
|
print("for index: {}, we got wallet type: {}".format(i, wc)) |
|
else: |
|
wc = walletclass |
|
w = open_test_wallet_maybe(seeds[i], seeds[i], mixdepths - 1, |
|
test_wallet_cls=wc) |
|
|
|
wallet_service = WalletService(w) |
|
wallets[i + start_index] = {'seed': seeds[i].decode('ascii'), |
|
'wallet': wallet_service} |
|
for j in range(mixdepths): |
|
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(wallet_service.get_new_addr( |
|
j, BaseWallet.ADDRESS_TYPE_INTERNAL), amt) |
|
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)
|
|
|