|
|
|
@ -6,140 +6,153 @@ from common import make_wallets |
|
|
|
from pprint import pformat |
|
|
|
from pprint import pformat |
|
|
|
import jmbitcoin as btc |
|
|
|
import jmbitcoin as btc |
|
|
|
import pytest |
|
|
|
import pytest |
|
|
|
|
|
|
|
from unittest_parametrize import parametrize, ParametrizedTestCase |
|
|
|
from jmbase import get_log, hextobin |
|
|
|
from jmbase import get_log, hextobin |
|
|
|
from jmclient import load_test_config, jm_single, LegacyWallet, BaseWallet |
|
|
|
from jmclient import load_test_config, jm_single, LegacyWallet, BaseWallet |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from common import TrialAsyncioTestCase |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pytestmark = pytest.mark.usefixtures("setup_regtest_bitcoind") |
|
|
|
|
|
|
|
|
|
|
|
log = get_log() |
|
|
|
log = get_log() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_segwit_valid_txs(setup_segwit): |
|
|
|
class SegWitTests(TrialAsyncioTestCase, ParametrizedTestCase): |
|
|
|
with open("test/tx_segwit_valid.json", "r") as f: |
|
|
|
|
|
|
|
json_data = f.read() |
|
|
|
async def asyncSetUp(self): |
|
|
|
valid_txs = json.loads(json_data) |
|
|
|
load_test_config() |
|
|
|
for j in valid_txs: |
|
|
|
jm_single().bc_interface.tick_forward_chain_interval = 1 |
|
|
|
if len(j) < 2: |
|
|
|
|
|
|
|
continue |
|
|
|
async def test_segwit_valid_txs(self): |
|
|
|
deserialized_tx = btc.CMutableTransaction.deserialize(hextobin(j[1])) |
|
|
|
with open("test/tx_segwit_valid.json", "r") as f: |
|
|
|
print(pformat(deserialized_tx)) |
|
|
|
json_data = f.read() |
|
|
|
assert deserialized_tx.serialize() == hextobin(j[1]) |
|
|
|
valid_txs = json.loads(json_data) |
|
|
|
#TODO use bcinterface to decoderawtransaction |
|
|
|
for j in valid_txs: |
|
|
|
#and compare the json values |
|
|
|
if len(j) < 2: |
|
|
|
|
|
|
|
continue |
|
|
|
@pytest.mark.parametrize( |
|
|
|
deserialized_tx = btc.CMutableTransaction.deserialize(hextobin(j[1])) |
|
|
|
"wallet_structure, in_amt, amount, segwit_amt, segwit_ins, o_ins", [ |
|
|
|
print(pformat(deserialized_tx)) |
|
|
|
([[1, 0, 0, 0, 0]], 1, 1000000, 1, [0, 1, 2], []), |
|
|
|
assert deserialized_tx.serialize() == hextobin(j[1]) |
|
|
|
([[4, 0, 0, 0, 1]], 3, 100000000, 1, [0, 2], [1, 3]), |
|
|
|
#TODO use bcinterface to decoderawtransaction |
|
|
|
([[4, 0, 0, 0, 1]], 3, 100000000, 1, [0, 5], [1, 2, 3, 4]), |
|
|
|
#and compare the json values |
|
|
|
([[4, 0, 0, 0, 0]], 2, 200000007, 0.3, [0, 1, 4, 5], [2, 3, 6]), |
|
|
|
|
|
|
|
]) |
|
|
|
@parametrize( |
|
|
|
def test_spend_p2sh_p2wpkh_multi(setup_segwit, wallet_structure, in_amt, amount, |
|
|
|
"wallet_structure, in_amt, amount, segwit_amt, segwit_ins, o_ins", [ |
|
|
|
segwit_amt, segwit_ins, o_ins): |
|
|
|
([[1, 0, 0, 0, 0]], 1, 1000000, 1, [0, 1, 2], []), |
|
|
|
"""Creates a wallet from which non-segwit inputs/ |
|
|
|
([[4, 0, 0, 0, 1]], 3, 100000000, 1, [0, 2], [1, 3]), |
|
|
|
outputs can be created, constructs one or more |
|
|
|
([[4, 0, 0, 0, 1]], 3, 100000000, 1, [0, 5], [1, 2, 3, 4]), |
|
|
|
p2wpkh in p2sh spendable utxos (by paying into the |
|
|
|
([[4, 0, 0, 0, 0]], 2, 200000007, 0.3, [0, 1, 4, 5], [2, 3, 6]), |
|
|
|
corresponding address) and tests spending them |
|
|
|
]) |
|
|
|
in combination. |
|
|
|
async def test_spend_p2sh_p2wpkh_multi(self, wallet_structure, in_amt, amount, |
|
|
|
wallet_structure is in accordance with commontest.make_wallets, see docs there |
|
|
|
segwit_amt, segwit_ins, o_ins): |
|
|
|
in_amt is the amount to pay into each address into the wallet (non-segwit adds) |
|
|
|
"""Creates a wallet from which non-segwit inputs/ |
|
|
|
amount (in satoshis) is how much we will pay to the output address |
|
|
|
outputs can be created, constructs one or more |
|
|
|
segwit_amt in BTC is the amount we will fund each new segwit address with |
|
|
|
p2wpkh in p2sh spendable utxos (by paying into the |
|
|
|
segwit_ins is a list of input indices (where to place the funding segwit utxos) |
|
|
|
corresponding address) and tests spending them |
|
|
|
other_ins is a list of input indices (where to place the funding non-sw utxos) |
|
|
|
in combination. |
|
|
|
""" |
|
|
|
wallet_structure is in accordance with commontest.make_wallets, see docs there |
|
|
|
MIXDEPTH = 0 |
|
|
|
in_amt is the amount to pay into each address into the wallet (non-segwit adds) |
|
|
|
|
|
|
|
amount (in satoshis) is how much we will pay to the output address |
|
|
|
# set up wallets and inputs |
|
|
|
segwit_amt in BTC is the amount we will fund each new segwit address with |
|
|
|
nsw_wallet_service = make_wallets(1, wallet_structure, in_amt, |
|
|
|
segwit_ins is a list of input indices (where to place the funding segwit utxos) |
|
|
|
walletclass=LegacyWallet)[0]['wallet'] |
|
|
|
other_ins is a list of input indices (where to place the funding non-sw utxos) |
|
|
|
nsw_wallet_service.sync_wallet(fast=True) |
|
|
|
""" |
|
|
|
sw_wallet_service = make_wallets(1, [[len(segwit_ins), 0, 0, 0, 0]], segwit_amt)[0]['wallet'] |
|
|
|
MIXDEPTH = 0 |
|
|
|
sw_wallet_service.sync_wallet(fast=True) |
|
|
|
|
|
|
|
|
|
|
|
# set up wallets and inputs |
|
|
|
nsw_utxos = nsw_wallet_service.get_utxos_by_mixdepth()[MIXDEPTH] |
|
|
|
wallet_services = await make_wallets( |
|
|
|
sw_utxos = sw_wallet_service.get_utxos_by_mixdepth()[MIXDEPTH] |
|
|
|
1, wallet_structure, in_amt, walletclass=LegacyWallet) |
|
|
|
assert len(o_ins) <= len(nsw_utxos), "sync failed" |
|
|
|
nsw_wallet_service = wallet_services[0]['wallet'] |
|
|
|
assert len(segwit_ins) <= len(sw_utxos), "sync failed" |
|
|
|
await nsw_wallet_service.sync_wallet(fast=True) |
|
|
|
|
|
|
|
sw_wallet_services = await make_wallets( |
|
|
|
total_amt_in_sat = 0 |
|
|
|
1, [[len(segwit_ins), 0, 0, 0, 0]], segwit_amt) |
|
|
|
|
|
|
|
sw_wallet_service = sw_wallet_services[0]['wallet'] |
|
|
|
nsw_ins = {} |
|
|
|
await sw_wallet_service.sync_wallet(fast=True) |
|
|
|
for nsw_in_index in o_ins: |
|
|
|
|
|
|
|
total_amt_in_sat += in_amt * 10**8 |
|
|
|
nsw_utxos_by_md = await nsw_wallet_service.get_utxos_by_mixdepth() |
|
|
|
nsw_ins[nsw_in_index] = nsw_utxos.popitem() |
|
|
|
nsw_utxos = nsw_utxos_by_md[MIXDEPTH] |
|
|
|
|
|
|
|
sw_utxos_by_md = await sw_wallet_service.get_utxos_by_mixdepth() |
|
|
|
sw_ins = {} |
|
|
|
sw_utxos = sw_utxos_by_md[MIXDEPTH] |
|
|
|
for sw_in_index in segwit_ins: |
|
|
|
assert len(o_ins) <= len(nsw_utxos), "sync failed" |
|
|
|
total_amt_in_sat += int(segwit_amt * 10**8) |
|
|
|
assert len(segwit_ins) <= len(sw_utxos), "sync failed" |
|
|
|
sw_ins[sw_in_index] = sw_utxos.popitem() |
|
|
|
|
|
|
|
|
|
|
|
total_amt_in_sat = 0 |
|
|
|
all_ins = {} |
|
|
|
|
|
|
|
all_ins.update(nsw_ins) |
|
|
|
nsw_ins = {} |
|
|
|
all_ins.update(sw_ins) |
|
|
|
for nsw_in_index in o_ins: |
|
|
|
|
|
|
|
total_amt_in_sat += in_amt * 10**8 |
|
|
|
# sanity checks |
|
|
|
nsw_ins[nsw_in_index] = nsw_utxos.popitem() |
|
|
|
assert len(all_ins) == len(nsw_ins) + len(sw_ins), \ |
|
|
|
|
|
|
|
"test broken, duplicate index" |
|
|
|
sw_ins = {} |
|
|
|
for k in all_ins: |
|
|
|
for sw_in_index in segwit_ins: |
|
|
|
assert 0 <= k < len(all_ins), "test broken, missing input index" |
|
|
|
total_amt_in_sat += int(segwit_amt * 10**8) |
|
|
|
|
|
|
|
sw_ins[sw_in_index] = sw_utxos.popitem() |
|
|
|
# FIXME: encoding mess, mktx should accept binary input formats |
|
|
|
|
|
|
|
tx_ins = [] |
|
|
|
all_ins = {} |
|
|
|
for i, (txin, data) in sorted(all_ins.items(), key=lambda x: x[0]): |
|
|
|
all_ins.update(nsw_ins) |
|
|
|
tx_ins.append(txin) |
|
|
|
all_ins.update(sw_ins) |
|
|
|
|
|
|
|
|
|
|
|
# create outputs |
|
|
|
# sanity checks |
|
|
|
FEE = 50000 |
|
|
|
assert len(all_ins) == len(nsw_ins) + len(sw_ins), \ |
|
|
|
assert FEE < total_amt_in_sat - amount, "test broken, not enough funds" |
|
|
|
"test broken, duplicate index" |
|
|
|
|
|
|
|
for k in all_ins: |
|
|
|
cj_script = nsw_wallet_service.get_new_script(MIXDEPTH + 1, |
|
|
|
assert 0 <= k < len(all_ins), "test broken, missing input index" |
|
|
|
BaseWallet.ADDRESS_TYPE_INTERNAL) |
|
|
|
|
|
|
|
change_script = nsw_wallet_service.get_new_script(MIXDEPTH, |
|
|
|
# FIXME: encoding mess, mktx should accept binary input formats |
|
|
|
BaseWallet.ADDRESS_TYPE_INTERNAL) |
|
|
|
tx_ins = [] |
|
|
|
change_amt = total_amt_in_sat - amount - FEE |
|
|
|
for i, (txin, data) in sorted(all_ins.items(), key=lambda x: x[0]): |
|
|
|
|
|
|
|
tx_ins.append(txin) |
|
|
|
tx_outs = [ |
|
|
|
|
|
|
|
{'address': nsw_wallet_service.script_to_addr(cj_script), |
|
|
|
# create outputs |
|
|
|
'value': amount}, |
|
|
|
FEE = 50000 |
|
|
|
{'address': nsw_wallet_service.script_to_addr(change_script), |
|
|
|
assert FEE < total_amt_in_sat - amount, "test broken, not enough funds" |
|
|
|
'value': change_amt}] |
|
|
|
|
|
|
|
tx = btc.mktx(tx_ins, tx_outs) |
|
|
|
cj_script = await nsw_wallet_service.get_new_script( |
|
|
|
|
|
|
|
MIXDEPTH + 1, BaseWallet.ADDRESS_TYPE_INTERNAL) |
|
|
|
# import new addresses to bitcoind |
|
|
|
change_script = await nsw_wallet_service.get_new_script( |
|
|
|
jm_single().bc_interface.import_addresses( |
|
|
|
MIXDEPTH, BaseWallet.ADDRESS_TYPE_INTERNAL) |
|
|
|
[nsw_wallet_service.script_to_addr(x) |
|
|
|
change_amt = total_amt_in_sat - amount - FEE |
|
|
|
for x in [cj_script, change_script]], nsw_wallet_service.get_wallet_name()) |
|
|
|
|
|
|
|
|
|
|
|
tx_outs = [ |
|
|
|
# sign tx |
|
|
|
{'address': await nsw_wallet_service.script_to_addr(cj_script), |
|
|
|
scripts = {} |
|
|
|
'value': amount}, |
|
|
|
for nsw_in_index in o_ins: |
|
|
|
{'address': await nsw_wallet_service.script_to_addr(change_script), |
|
|
|
inp = nsw_ins[nsw_in_index][1] |
|
|
|
'value': change_amt}] |
|
|
|
scripts[nsw_in_index] = (inp['script'], inp['value']) |
|
|
|
tx = btc.mktx(tx_ins, tx_outs) |
|
|
|
success, msg = nsw_wallet_service.sign_tx(tx, scripts) |
|
|
|
|
|
|
|
assert success, msg |
|
|
|
# import new addresses to bitcoind |
|
|
|
|
|
|
|
jm_single().bc_interface.import_addresses( |
|
|
|
scripts = {} |
|
|
|
[(await nsw_wallet_service.script_to_addr(x)) |
|
|
|
for sw_in_index in segwit_ins: |
|
|
|
for x in [cj_script, change_script]], |
|
|
|
inp = sw_ins[sw_in_index][1] |
|
|
|
nsw_wallet_service.get_wallet_name()) |
|
|
|
scripts[sw_in_index] = (inp['script'], inp['value']) |
|
|
|
|
|
|
|
success, msg = sw_wallet_service.sign_tx(tx, scripts) |
|
|
|
# sign tx |
|
|
|
assert success, msg |
|
|
|
scripts = {} |
|
|
|
|
|
|
|
for nsw_in_index in o_ins: |
|
|
|
print(tx) |
|
|
|
inp = nsw_ins[nsw_in_index][1] |
|
|
|
|
|
|
|
scripts[nsw_in_index] = (inp['script'], inp['value']) |
|
|
|
# push and verify |
|
|
|
success, msg = await nsw_wallet_service.sign_tx(tx, scripts) |
|
|
|
txid = jm_single().bc_interface.pushtx(tx.serialize()) |
|
|
|
assert success, msg |
|
|
|
assert txid |
|
|
|
|
|
|
|
|
|
|
|
scripts = {} |
|
|
|
balances = jm_single().bc_interface.get_received_by_addr( |
|
|
|
for sw_in_index in segwit_ins: |
|
|
|
[nsw_wallet_service.script_to_addr(cj_script), |
|
|
|
inp = sw_ins[sw_in_index][1] |
|
|
|
nsw_wallet_service.script_to_addr(change_script)])['data'] |
|
|
|
scripts[sw_in_index] = (inp['script'], inp['value']) |
|
|
|
assert balances[0]['balance'] == amount |
|
|
|
success, msg = await sw_wallet_service.sign_tx(tx, scripts) |
|
|
|
assert balances[1]['balance'] == change_amt |
|
|
|
assert success, msg |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(tx) |
|
|
|
@pytest.fixture(scope="module") |
|
|
|
|
|
|
|
def setup_segwit(): |
|
|
|
# push and verify |
|
|
|
load_test_config() |
|
|
|
txid = jm_single().bc_interface.pushtx(tx.serialize()) |
|
|
|
jm_single().bc_interface.tick_forward_chain_interval = 1 |
|
|
|
assert txid |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
balances = jm_single().bc_interface.get_received_by_addr( |
|
|
|
|
|
|
|
[(await nsw_wallet_service.script_to_addr(cj_script)), |
|
|
|
|
|
|
|
(await nsw_wallet_service.script_to_addr(change_script))])['data'] |
|
|
|
|
|
|
|
assert balances[0]['balance'] == amount |
|
|
|
|
|
|
|
assert balances[1]['balance'] == change_amt |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
''' |
|
|
|
''' |
|
|
|
|