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.
 
 
 
 

131 lines
5.4 KiB

#! /usr/bin/env python
'''Test of SNICKER functionality using Joinmarket
wallets as defined in jmclient.wallet.'''
import pytest
from unittest import IsolatedAsyncioTestCase
from unittest_parametrize import parametrize, ParametrizedTestCase
import jmclient # noqa: F401 install asyncioreactor
from commontest import make_wallets, dummy_accept_callback, dummy_info_callback
import jmbitcoin as btc
from jmbase import get_log, bintohex
from jmclient import (load_test_config, estimate_tx_fee, SNICKERReceiver,
direct_send, BaseWallet)
pytestmark = pytest.mark.usefixtures("setup_regtest_bitcoind")
log = get_log()
@pytest.mark.usefixtures("setup_snicker")
class AsyncioTestCase(IsolatedAsyncioTestCase, ParametrizedTestCase):
@parametrize(
"nw, wallet_structures, mean_amt, sdev_amt, amt, net_transfer",
[
(2, [[1, 0, 0, 0, 0]] * 2, 4, 0, 20000000, 1000),
])
async def test_snicker_e2e(self, nw, wallet_structures,
mean_amt, sdev_amt, amt, net_transfer):
""" Test strategy:
1. create two wallets.
2. with wallet 1 (Receiver), create a single transaction
tx1, from mixdepth 0 to 1.
3. with wallet 2 (Proposer), take pubkey of all inputs from tx1, and use
them to create snicker proposals to the non-change out of tx1,
in base64 and place in proposals.txt.
4. Receiver polls for proposals in the file manually (instead of twisted
LoopingCall) and processes them.
5. Check for valid final transaction with broadcast.
"""
# TODO: Make this test work with native segwit wallets
wallets = await make_wallets(nw, wallet_structures, mean_amt, sdev_amt)
for w in wallets.values():
await w['wallet'].sync_wallet(fast=True)
print(wallets)
wallet_r = wallets[0]['wallet']
wallet_p = wallets[1]['wallet']
# next, create a tx from the receiver wallet
our_destn_script = await wallet_r.get_new_script(
1, BaseWallet.ADDRESS_TYPE_INTERNAL)
tx = await direct_send(
wallet_r, 0,
[(await wallet_r.script_to_addr(our_destn_script),
btc.coins_to_satoshi(0.3))],
accept_callback=dummy_accept_callback,
info_callback=dummy_info_callback,
return_transaction=True)
assert tx, "Failed to spend from receiver wallet"
print("Parent transaction OK. It was: ")
print(btc.human_readable_transaction(tx))
await wallet_r.process_new_tx(tx)
# we must identify the receiver's output we're going to use;
# it can be destination or change, that's up to the proposer
# to guess successfully; here we'll just choose index 0.
txid1 = tx.GetTxid()[::-1]
txid1_index = 0
receiver_start_bal = sum(
[x['value'] for x in (await wallet_r.get_all_utxos()).values()])
# Now create a proposal for every input index in tx1
# (version 1 proposals mean we source keys from the/an
# ancestor transaction)
propose_keys = []
for i in range(len(tx.vin)):
# todo check access to pubkey
sig, pub = [a for a in iter(tx.wit.vtxinwit[i].scriptWitness)]
propose_keys.append(pub)
# the proposer wallet needs to choose a single
# utxo that is bigger than the output amount of tx1
prop_m_utxos = (await wallet_p.get_utxos_by_mixdepth())[0]
prop_utxo = prop_m_utxos[list(prop_m_utxos)[0]]
# get the private key for that utxo
addr = await wallet_p.script_to_addr(prop_utxo['script'])
priv = wallet_p.get_key_from_addr(addr)
prop_input_amt = prop_utxo['value']
# construct the arguments for the snicker proposal:
our_input = list(prop_m_utxos)[0] # should be (txid, index)
their_input = (txid1, txid1_index)
our_input_utxo = btc.CMutableTxOut(prop_utxo['value'],
prop_utxo['script'])
fee_est = estimate_tx_fee(len(tx.vin), 2,
txtype=wallet_p.get_txtype())
change_spk = await wallet_p.get_new_script(
0, BaseWallet.ADDRESS_TYPE_INTERNAL)
encrypted_proposals = []
for p in propose_keys:
# TODO: this can be a loop over all outputs,
# not just one guessed output, if desired.
encrypted_proposals.append(
await wallet_p.create_snicker_proposal(
[our_input], their_input,
[our_input_utxo],
tx.vout[txid1_index],
net_transfer,
fee_est,
priv,
p,
prop_utxo['script'],
change_spk,
version_byte=1) + b"," + bintohex(p).encode('utf-8'))
sR = SNICKERReceiver(wallet_r)
await sR.process_proposals(
[x.decode("utf-8") for x in encrypted_proposals])
assert len(sR.successful_txs) == 1
await wallet_r.process_new_tx(sR.successful_txs[0])
end_utxos = await wallet_r.get_all_utxos()
print("At end the receiver has these utxos: ", end_utxos)
receiver_end_bal = sum([x['value'] for x in end_utxos.values()])
assert receiver_end_bal == receiver_start_bal + net_transfer
@pytest.fixture(scope="module")
def setup_snicker(request):
load_test_config()