Files
palladum-lightning/tests/test_wallet.py
Rusty Russell f284489c96 common: don't abort() if wally_psbt_output_taproot_keypath_add() fails.
It fails on duplicates.  It would ideally succeed, but bug reported:

	https://github.com/ElementsProject/libwally-core/issues/509

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: JSON-RPC: `signpsbt` no longer crashes if asked to sign an already-signed PSBT with taproot paths.
2025-11-19 07:23:39 +10:30

2495 lines
210 KiB
Python

from bitcoin.rpc import JSONRPCError
from decimal import Decimal
from fixtures import * # noqa: F401,F403
from fixtures import TEST_NETWORK
from pyln.client import RpcError, Millisatoshi
from utils import (
only_one, wait_for, sync_blockheight,
VALGRIND, check_coin_moves, TailableProc, scriptpubkey_addr,
check_utxos_channel, check_feerate, did_short_sig
)
import os
import pytest
import subprocess
import sys
import unittest
WAIT_TIMEOUT = 60 # Wait timeout for processes
# Errors codes
HSM_GENERIC_ERROR = 20
HSM_ERROR_IS_ENCRYPT = 21
HSM_BAD_PASSWORD = 22
def good_addrtype():
"""Elements doesn't support p2tr"""
if TEST_NETWORK == 'regtest':
return "p2tr"
return "bech32"
@unittest.skipIf(TEST_NETWORK != 'regtest', "Test relies on a number of example addresses valid only in regtest")
def test_withdraw(node_factory, bitcoind):
amount = 1000000
# Don't get any funds from previous runs.
l1 = node_factory.get_node(random_hsm=True, options={'log-level': 'io'})
l2 = node_factory.get_node(random_hsm=True)
addr = l1.rpc.newaddr('p2tr')['p2tr']
# Add some funds to withdraw later
for i in range(10):
l1.bitcoin.rpc.sendtoaddress(addr, amount / 10**8 + 0.01)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
# Reach around into the db to check that outputs were added
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 10
waddr = l1.bitcoin.rpc.getnewaddress()
# Now attempt to withdraw some (making sure we collect multiple inputs)
# These violate schemas!
l1.rpc.check_request_schemas = False
with pytest.raises(RpcError):
l1.rpc.withdraw('not an address', amount)
with pytest.raises(RpcError):
l1.rpc.withdraw(waddr, 'not an amount')
with pytest.raises(RpcError):
l1.rpc.withdraw(waddr, -amount)
with pytest.raises(RpcError, match=r'Could not afford'):
l1.rpc.withdraw(waddr, amount * 100)
l1.rpc.check_request_schemas = True
out = l1.rpc.withdraw(waddr, 2 * amount)
# Side note: sendrawtransaction will trace back to withdrawl
myname = os.path.splitext(os.path.basename(sys.argv[0]))[0]
l1.daemon.wait_for_log(r': "{}:withdraw#[0-9]*/cln:withdraw#[0-9]*/txprepare:sendpsbt#[0-9]*/cln:sendrawtransaction#[0-9]*"\[OUT\]'.format(myname))
# Make sure bitcoind received the withdrawal
unspent = l1.bitcoin.rpc.listunspent(0)
withdrawal = [u for u in unspent if u['txid'] == out['txid']]
assert(withdrawal[0]['amount'] == Decimal('0.02'))
l1.bitcoin.generate_block(1)
sync_blockheight(l1.bitcoin, [l1])
# Check that there are no unconfirmed outputs (change should be confirmed)
for o in l1.rpc.listfunds()['outputs']:
assert o['status'] == 'confirmed'
# Now make sure two of them were marked as spent
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 2
# Now send some money to l2.
# BIP86 wallets use P2TR addresses
waddr = l2.rpc.newaddr('p2tr')['p2tr']
l1.rpc.withdraw(waddr, 2 * amount)
# Now make sure an additional two of them were marked as reserved
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 2
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=1')[0]['c'] == 2
# They're turned into spent once the node sees them mined.
bitcoind.generate_block(1)
sync_blockheight(l1.bitcoin, [l1, l2])
# Make sure l2 received the withdrawal.
assert len(l2.rpc.listfunds()['outputs']) == 1
outputs = l2.db_query('SELECT value FROM outputs WHERE status=0;')
assert only_one(outputs)['value'] == 2 * amount
# Now make sure an additional two of them were marked as spent
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 4
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=1')[0]['c'] == 0
# Simple test for withdrawal to P2WPKH
# Address from: https://bc-2.jp/tools/bech32demo/index.html
waddr = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080'
with pytest.raises(RpcError):
l1.rpc.withdraw('xx1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx', 2 * amount)
with pytest.raises(RpcError):
l1.rpc.withdraw('tb1pw508d6qejxtdg4y5r3zarvary0c5xw7kdl9fad', 2 * amount)
with pytest.raises(RpcError):
l1.rpc.withdraw('tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxxxxxx', 2 * amount)
l1.rpc.withdraw(waddr, 2 * amount)
bitcoind.generate_block(1)
sync_blockheight(l1.bitcoin, [l1])
# Now make sure additional two of them were marked as spent
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 6
# Simple test for withdrawal to P2WSH
# Address from: https://bc-2.jp/tools/bech32demo/index.html
waddr = 'bcrt1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qzf4jry'
with pytest.raises(RpcError):
l1.rpc.withdraw('xx1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7', 2 * amount)
with pytest.raises(RpcError):
l1.rpc.withdraw('tb1prp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qsm03tq', 2 * amount)
with pytest.raises(RpcError):
l1.rpc.withdraw('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qxxxxxx', 2 * amount)
l1.rpc.withdraw(waddr, 2 * amount)
bitcoind.generate_block(1)
sync_blockheight(l1.bitcoin, [l1])
# Now make sure additional two of them were marked as spent
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 8
# failure testing for invalid SegWit addresses, from BIP173
# HRP character out of range
with pytest.raises(RpcError):
l1.rpc.withdraw(' 1nwldj5', 2 * amount)
# overall max length exceeded
with pytest.raises(RpcError):
l1.rpc.withdraw('an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx', 2 * amount)
# No separator character
with pytest.raises(RpcError):
l1.rpc.withdraw('pzry9x0s0muk', 2 * amount)
# Empty HRP
with pytest.raises(RpcError):
l1.rpc.withdraw('1pzry9x0s0muk', 2 * amount)
# Invalid witness version
with pytest.raises(RpcError):
l1.rpc.withdraw('BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2', 2 * amount)
# Invalid program length for witness version 0 (per BIP141)
with pytest.raises(RpcError):
l1.rpc.withdraw('BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P', 2 * amount)
# Mixed case
with pytest.raises(RpcError):
l1.rpc.withdraw('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7', 2 * amount)
# Non-zero padding in 8-to-5 conversion
with pytest.raises(RpcError):
l1.rpc.withdraw('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv', 2 * amount)
# Should have 6 outputs available: 2 original unspent + 4 change outputs from withdrawals
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 6
# Test withdrawal to self.
l1.rpc.withdraw(l1.rpc.newaddr('p2tr')['p2tr'], 'all', minconf=0)
bitcoind.generate_block(1)
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 1
l1.rpc.withdraw(waddr, 'all', minconf=0)
assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 0
# This should fail, can't even afford fee.
with pytest.raises(RpcError, match=r'Could not afford'):
l1.rpc.withdraw(waddr, 'all')
# Add some funds to withdraw
for i in range(12):
l1.bitcoin.rpc.sendtoaddress(addr, amount / 10**8 + 0.01)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 12)
# Try passing in a utxo set
utxos = [utxo["txid"] + ":" + str(utxo["output"]) for utxo in l1.rpc.listfunds()["outputs"]][:4]
withdrawal = l1.rpc.withdraw(waddr, 2 * amount, utxos=utxos)
decode = bitcoind.rpc.decoderawtransaction(withdrawal['tx'])
assert decode['txid'] == withdrawal['txid']
# Check that correct utxos are included
assert len(decode['vin']) == 4
vins = ["{}:{}".format(v['txid'], v['vout']) for v in decode['vin']]
for utxo in utxos:
assert utxo in vins
# Try passing unconfirmed utxos
unconfirmed_utxos = [l1.rpc.withdraw(l1.rpc.newaddr("p2tr")["p2tr"], 10**5)
for _ in range(5)]
uutxos = [u["txid"] + ":0" for u in unconfirmed_utxos]
l1.rpc.withdraw(waddr, "all", minconf=0, utxos=uutxos)
# Try passing minimum feerates (for relay)
l1.rpc.withdraw(l1.rpc.newaddr("p2tr")["p2tr"], 10**5, feerate="253perkw")
l1.rpc.withdraw(l1.rpc.newaddr("p2tr")["p2tr"], 10**5, feerate="1000perkb")
def test_minconf_withdraw(node_factory, bitcoind):
"""Issue 2518: ensure that ridiculous confirmation levels don't overflow
The number of confirmations is used to compute a maximum height that is to
be accepted. If the current height is smaller than the number of
confirmations we wrap around and just select everything. The fix is to
clamp the maxheight parameter to a positive small number.
"""
amount = 1000000
# Don't get any funds from previous runs.
l1 = node_factory.get_node(random_hsm=True)
addr = l1.rpc.newaddr(good_addrtype())[good_addrtype()]
# Add some funds to withdraw later
for i in range(10):
l1.bitcoin.rpc.sendtoaddress(addr, amount / 10**8 + 0.01)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
# This violates the request schema!
l1.rpc.check_request_schemas = False
with pytest.raises(RpcError):
l1.rpc.withdraw(destination=addr, satoshi=10000, feerate='normal', minconf=9999999)
@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "BIP86 random_hsm not compatible with liquid-regtest bech32")
def test_addfunds_from_block(node_factory, bitcoind):
"""Send funds to the daemon without telling it explicitly
"""
# Previous runs with same bitcoind can leave funds!
coin_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')
l1 = node_factory.get_node(random_hsm=True, options={'plugin': coin_plugin})
addr = l1.rpc.newaddr(good_addrtype())[good_addrtype()]
bitcoind.rpc.sendtoaddress(addr, 0.1)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1)
outputs = l1.db_query('SELECT value FROM outputs WHERE status=0;')
assert only_one(outputs)['value'] == 10000000
# The address we detect must match what was paid to.
output = only_one(l1.rpc.listfunds()['outputs'])
assert output['address'] == addr
# Send all our money to a P2WPKH address this time.
addr = l1.rpc.newaddr("bech32")['bech32']
l1.rpc.withdraw(addr, "all")
bitcoind.generate_block(1)
sync_blockheight(bitcoind, [l1])
# The address we detect must match what was paid to.
output = only_one(l1.rpc.listfunds()['outputs'])
assert output['address'] == addr
# We don't print a 'external deposit' event
# for funds that come back to our own wallet
expected_utxos = {
'0': [('wallet', ['deposit'], ['withdrawal'], 'A')],
'A': [('wallet', ['deposit'], None, None)],
}
check_utxos_channel(l1, [], expected_utxos)
def test_txprepare_multi(node_factory, bitcoind):
amount = 10000000
l1 = node_factory.get_node(random_hsm=True)
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('all')[good_addrtype()], amount / 10**8)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1)
outputs = []
for i in range(9):
outputs.append({l1.rpc.newaddr('bech32')['bech32']: Millisatoshi(amount * 100)})
prep = l1.rpc.txprepare(outputs=outputs)
l1.rpc.txdiscard(prep['txid'])
def feerate_from_psbt(chainparams, bitcoind, node, psbt):
# signpsbt insists they are reserved!
node.rpc.reserveinputs(psbt, exclusive=False)
final = node.rpc.dev_finalizepsbt(node.rpc.signpsbt(psbt)['signed_psbt'])
node.rpc.unreserveinputs(psbt)
if chainparams['elements']:
# Already v1
psbt = final['psbt']
else:
psbt = node.rpc.setpsbtversion(final['psbt'], 0)['psbt']
# analyzepsbt gives a vsize, but not a weight!
# e.g. 'estimated_vsize': 356, 'estimated_feerate': Decimal('0.00030042'), 'fee': Decimal('0.00010695')
fee = int(bitcoind.rpc.analyzepsbt(psbt)['fee'] * 100_000_000)
weight = bitcoind.rpc.decoderawtransaction(final['tx'])['weight']
return fee / weight * 1000
@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "BIP86 random_hsm not compatible with liquid-regtest bech32")
def test_txprepare(node_factory, bitcoind, chainparams):
amount = 1000000
l1 = node_factory.get_node(random_hsm=True, options={'dev-warn-on-overgrind': None},
broken_log='overgrind: short signature length')
addr = chainparams['example_addr']
# Add some funds to withdraw later
for i in range(10):
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr(good_addrtype())[good_addrtype()],
amount / 10**8)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
for est in l1.rpc.feerates('perkw')['perkw']['estimates']:
if est['blockcount'] == 12:
normal_feerate_perkw = est['feerate']
prep = l1.rpc.txprepare(outputs=[{addr: Millisatoshi(amount * 3 * 1000)}])
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
assert decode['txid'] == prep['txid']
# 4 inputs, 2 outputs (3 if we have a fee output).
assert len(decode['vin']) == 4
assert len(decode['vout']) == 2 if not chainparams['feeoutput'] else 3
if not chainparams['elements']: # FIXME
check_feerate([l1], feerate_from_psbt(chainparams, bitcoind, l1, prep['psbt']), normal_feerate_perkw)
# One output will be correct.
outnum = [i for i, o in enumerate(decode['vout']) if o['value'] == Decimal(amount * 3) / 10**8][0]
for i, o in enumerate(decode['vout']):
if i == outnum:
assert o['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert scriptpubkey_addr(o['scriptPubKey']) == addr
else:
if chainparams['elements']:
o['scriptPubKey']['type'] in ['witness_v0_keyhash', 'fee']
else:
assert o['scriptPubKey']['type'] in ['witness_v1_taproot', 'fee']
# Now prepare one with no change.
prep2 = l1.rpc.txprepare([{addr: 'all'}])
decode = bitcoind.rpc.decoderawtransaction(prep2['unsigned_tx'])
assert decode['txid'] == prep2['txid']
# 6 inputs, 1 outputs.
assert len(decode['vin']) == 6
assert len(decode['vout']) == 1 if not chainparams['feeoutput'] else 2
# Some fees will be paid.
assert decode['vout'][0]['value'] < Decimal(amount * 6) / 10**8
assert decode['vout'][0]['value'] > Decimal(amount * 6) / 10**8 - Decimal(0.0002)
assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert scriptpubkey_addr(decode['vout'][0]['scriptPubKey']) == addr
if not chainparams['elements']: # FIXME
check_feerate([l1], feerate_from_psbt(chainparams, bitcoind, l1, prep2['psbt']), normal_feerate_perkw)
# If I cancel the first one, I can get those first 4 outputs.
discard = l1.rpc.txdiscard(prep['txid'])
assert discard['txid'] == prep['txid']
assert discard['unsigned_tx'] == prep['unsigned_tx']
prep3 = l1.rpc.txprepare([{addr: 'all'}])
decode = bitcoind.rpc.decoderawtransaction(prep3['unsigned_tx'])
assert decode['txid'] == prep3['txid']
# 4 inputs, 1 outputs.
assert len(decode['vin']) == 4
assert len(decode['vout']) == 1 if not chainparams['feeoutput'] else 2
# Some fees will be taken
assert decode['vout'][0]['value'] < Decimal(amount * 4) / 10**8
assert decode['vout'][0]['value'] > Decimal(amount * 4) / 10**8 - Decimal(0.0002)
assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert scriptpubkey_addr(decode['vout'][0]['scriptPubKey']) == addr
if not chainparams['elements']: # FIXME
check_feerate([l1], feerate_from_psbt(chainparams, bitcoind, l1, prep3['psbt']), normal_feerate_perkw)
# Cannot discard twice.
with pytest.raises(RpcError, match=r'not an unreleased txid'):
l1.rpc.txdiscard(prep['txid'])
# Discard everything, we should now spend all inputs.
l1.rpc.txdiscard(prep2['txid'])
l1.rpc.txdiscard(prep3['txid'])
prep4 = l1.rpc.txprepare([{addr: 'all'}])
decode = bitcoind.rpc.decoderawtransaction(prep4['unsigned_tx'])
assert decode['txid'] == prep4['txid']
# 10 inputs, 1 outputs.
assert len(decode['vin']) == 10
assert len(decode['vout']) == 1 if not chainparams['feeoutput'] else 2
# Some fees will be taken
assert decode['vout'][0]['value'] < Decimal(amount * 10) / 10**8
assert decode['vout'][0]['value'] > Decimal(amount * 10) / 10**8 - Decimal(0.0003)
assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert scriptpubkey_addr(decode['vout'][0]['scriptPubKey']) == addr
if not chainparams['elements']: # FIXME
check_feerate([l1], feerate_from_psbt(chainparams, bitcoind, l1, prep4['psbt']), normal_feerate_perkw)
l1.rpc.txdiscard(prep4['txid'])
# Try passing in a utxo set
utxos = [utxo["txid"] + ":" + str(utxo["output"])
for utxo in l1.rpc.listfunds()["outputs"]][:4]
prep5 = l1.rpc.txprepare([{addr:
Millisatoshi(amount * 3.5 * 1000)}], utxos=utxos)
if not chainparams['elements']: # FIXME
check_feerate([l1], feerate_from_psbt(chainparams, bitcoind, l1, prep3['psbt']), normal_feerate_perkw)
# Try passing unconfirmed utxos
unconfirmed_utxo = l1.rpc.withdraw(l1.rpc.newaddr("bech32")["bech32"], 10**5)
uutxos = [unconfirmed_utxo["txid"] + ":0"]
with pytest.raises(RpcError, match=r"Could not afford"):
l1.rpc.txprepare([{addr: Millisatoshi(amount * 3.5 * 1000)}],
utxos=uutxos)
# Feerate should be ~ as we asked for
unconfirmed_tx = bitcoind.rpc.getrawmempool(True)[unconfirmed_utxo["txid"]]
feerate_perkw = int(unconfirmed_tx['fees']['base'] * 100_000_000) * 1000 / unconfirmed_tx['weight']
if not chainparams['elements']: # FIXME
check_feerate([l1], feerate_perkw, normal_feerate_perkw)
decode = bitcoind.rpc.decoderawtransaction(prep5['unsigned_tx'])
assert decode['txid'] == prep5['txid']
# Check that correct utxos are included
assert len(decode['vin']) == 4
vins = ["{}:{}".format(v['txid'], v['vout']) for v in decode['vin']]
for utxo in utxos:
assert utxo in vins
# We should have a change output, so this is exact
assert len(decode['vout']) == 3 if chainparams['feeoutput'] else 2
# Change output pos is random.
for vout in decode['vout']:
if vout['scriptPubKey']['type'] == 'fee':
continue
if scriptpubkey_addr(vout['scriptPubKey']) == addr:
changeout = vout
assert changeout['value'] == Decimal(amount * 3.5) / 10**8
assert changeout['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert scriptpubkey_addr(changeout['scriptPubKey']) == addr
# Discard prep4 and get all funds again
l1.rpc.txdiscard(prep5['txid'])
# You can have one which is all, but not two.
prep5 = l1.rpc.txprepare([{addr: Millisatoshi(amount * 3 * 1000)},
{addr: 'all'}])
# Feerate should be ~ as we asked for
if not chainparams['elements']: # FIXME
check_feerate([l1], feerate_from_psbt(chainparams, bitcoind, l1, prep5['psbt']), normal_feerate_perkw)
l1.rpc.txdiscard(prep5['txid'])
with pytest.raises(RpcError, match=r"'all'"):
prep5 = l1.rpc.txprepare([{addr: 'all'}, {addr: 'all'}])
prep5 = l1.rpc.txprepare([{addr: Millisatoshi(amount * 3 * 500 + 100000)},
{addr: Millisatoshi(amount * 3 * 500 - 100000)}])
# Feerate should be ~ as we asked for
if not chainparams['elements']: # FIXME
check_feerate([l1], feerate_from_psbt(chainparams, bitcoind, l1, prep5['psbt']), normal_feerate_perkw)
decode = bitcoind.rpc.decoderawtransaction(prep5['unsigned_tx'])
assert decode['txid'] == prep5['txid']
# 4 inputs, 3 outputs(include change).
assert len(decode['vin']) == 4
assert len(decode['vout']) == 4 if chainparams['feeoutput'] else 3
# One output will be correct.
for i in range(3 + chainparams['feeoutput']):
if decode['vout'][i - 1]['value'] == Decimal('0.01500100'):
outnum1 = i - 1
elif decode['vout'][i - 1]['value'] == Decimal('0.01499900'):
outnum2 = i - 1
else:
changenum = i - 1
assert decode['vout'][outnum1]['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert scriptpubkey_addr(decode['vout'][outnum1]['scriptPubKey']) == addr
assert decode['vout'][outnum2]['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert scriptpubkey_addr(decode['vout'][outnum2]['scriptPubKey']) == addr
if chainparams['elements']:
assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v0_keyhash'
else:
assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v1_taproot'
l1.rpc.txdiscard(prep5['txid'])
def test_txprepare_feerate(node_factory, bitcoind, chainparams):
# Make sure it works at different feerates!
l1, l2 = node_factory.get_nodes(2, opts={'dev-warn-on-overgrind': None,
'broken_log': 'overgrind: short signature length'})
# Add some funds to withdraw later
for i in range(20):
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('bech32')['bech32'],
1000 / 10**8)
bitcoind.generate_block(1)
out_addrs = l2.rpc.newaddr('all')
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 20)
for addrtype in ('bech32', 'p2tr'):
for feerate in range(255, 1000, 250):
prep = l1.rpc.txprepare([{out_addrs[addrtype]: Millisatoshi(9000)}], f"{feerate}perkw")
actual_feerate = feerate_from_psbt(chainparams, bitcoind, l1, prep['psbt'])
assert feerate - 2 < actual_feerate
# Feerate can be larger, if it chose not to give change output.
if chainparams['elements']:
fee_output = 1
else:
fee_output = 0
if len(bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])['vout']) == 1 + 1 + fee_output and not did_short_sig(l1):
assert actual_feerate < feerate + 2
l1.rpc.txdiscard(prep['txid'])
@pytest.mark.parametrize("addrtype", ["bech32", "p2tr"])
@unittest.skipIf(TEST_NETWORK != 'regtest', "FIXME: Elements fees are not quite right")
def test_fundpsbt_feerates(node_factory, bitcoind, chainparams, addrtype):
l1 = node_factory.get_node(options={'dev-warn-on-overgrind': None},
broken_log='overgrind: short signature length')
# Add some funds to withdraw later
for i in range(20):
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr(addrtype)[addrtype],
5000 / 10**8)
# See utxo_spend_weight()
if addrtype == 'bech32':
witness_weight = 1 + 71 + 1 + 33
elif addrtype == 'p2tr':
witness_weight = 1 + 64
else:
assert False
input_weight = 1 + witness_weight + (32 + 4 + 4 + 1) * 4
if chainparams['elements']:
input_weight += 6
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 20)
# version, input count, output count, locktime, segwit marker, flag
base_weight = (4 + 1 + 1 + 4) * 4 + 1 + 1
if chainparams['elements']:
# Elements has empty surjection and rangeproof
base_weight += 2 * 4
# And fee output (bitcoin_tx_output_weight(0)):
base_weight += (8 + 1 + 0) * 4 + (32 + 1 + 1 + 1) * 4
# Bech32 change output
change_weight = (8 + 1 + (1 + 1 + 20)) * 4
else:
# P2TR output
change_weight = (8 + 1 + (1 + 1 + 32)) * 4
# Both minimal and higher feerate
for feerate in (253, 1000):
# Try with both 1 and 2 inputs
for amount, num_inputs in ((260, 1), (5000, 2)):
prep = l1.rpc.fundpsbt(amount, f"{feerate}perkw", base_weight, excess_as_change=True)
assert prep['estimated_final_weight'] == base_weight + change_weight + input_weight * num_inputs
signed = l1.rpc.signpsbt(prep['psbt'])['signed_psbt']
sent = l1.rpc.sendpsbt(signed)
txinfo = bitcoind.rpc.getmempoolentry(sent['txid'])
if did_short_sig(l1):
assert txinfo['weight'] <= prep['estimated_final_weight']
else:
assert txinfo['weight'] == prep['estimated_final_weight']
# We never actually added that `amount` output to PSBT, so that appears as "fee"
fee = int(txinfo['fees']['base'] * 100_000_000) - amount
actual_feerate = fee / (txinfo['weight'] / 1000)
check_feerate([l1], actual_feerate, feerate)
def test_reserveinputs(node_factory, bitcoind, chainparams):
amount = 1000000
total_outs = 12
l1 = node_factory.get_node(feerates=(7500, 7500, 7500, 7500))
outputs = []
# Add a medley of funds to withdraw
for i in range(total_outs):
txid = bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('bech32')['bech32'],
amount / 10**8)
outputs.append((txid, bitcoind.rpc.gettransaction(txid)['details'][0]['vout']))
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs)
assert not any(o['reserved'] for o in l1.rpc.listfunds()['outputs'])
# Try reserving one at a time.
for out in outputs:
psbt = bitcoind.rpc.createpsbt([{'txid': out[0], 'vout': out[1]}], [])
l1.rpc.reserveinputs(psbt)
assert all(o['reserved'] for o in l1.rpc.listfunds()['outputs'])
reserveheight = bitcoind.rpc.getblockchaininfo()['blocks'] + 72
assert all(o['reserved_to_block'] == reserveheight for o in l1.rpc.listfunds()['outputs'])
# Unreserve as a batch.
psbt = bitcoind.rpc.createpsbt([{'txid': out[0], 'vout': out[1]} for out in outputs], [])
l1.rpc.unreserveinputs(psbt)
assert not any(o['reserved'] for o in l1.rpc.listfunds()['outputs'])
assert not any('reserved_to_block' in o for o in l1.rpc.listfunds()['outputs'])
# Reserve twice fails unless exclusive.
l1.rpc.reserveinputs(psbt)
with pytest.raises(RpcError, match=r"already reserved"):
l1.rpc.reserveinputs(psbt)
l1.rpc.reserveinputs(psbt, False)
assert all(o['reserved_to_block'] == reserveheight + 72 for o in l1.rpc.listfunds()['outputs'])
l1.rpc.unreserveinputs(psbt)
assert all(o['reserved'] for o in l1.rpc.listfunds()['outputs'])
assert all(o['reserved_to_block'] == reserveheight for o in l1.rpc.listfunds()['outputs'])
# Stays reserved across restarts.
l1.restart()
assert all(o['reserved'] for o in l1.rpc.listfunds()['outputs'])
assert all(o['reserved_to_block'] == reserveheight for o in l1.rpc.listfunds()['outputs'])
# Final unreserve works.
l1.rpc.unreserveinputs(psbt)
assert not any(o['reserved'] for o in l1.rpc.listfunds()['outputs'])
assert not any('reserved_to_block' in o for o in l1.rpc.listfunds()['outputs'])
def test_fundpsbt(node_factory, bitcoind, chainparams):
amount = 1000000
total_outs = 4
l1 = node_factory.get_node()
# CLN returns PSBTv0 and PSETv2, for now
is_psbt_v2 = chainparams['elements']
outputs = []
# Add a medley of funds to withdraw later
for i in range(total_outs):
txid = bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('bech32')['bech32'],
amount / 10**8)
outputs.append((txid, bitcoind.rpc.gettransaction(txid)['details'][0]['vout']))
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs)
feerate = '7500perkw'
# Should get one input, plus some excess
funding = l1.rpc.fundpsbt(amount // 2, feerate, 0, reserve=0)
psbt = bitcoind.rpc.decodepsbt(funding['psbt'])
# We can fuzz this up to 99 blocks back.
assert funding['excess_msat'] > Millisatoshi(0)
assert funding['excess_msat'] < Millisatoshi(amount // 2 * 1000)
assert funding['feerate_per_kw'] == 7500
assert 'estimated_final_weight' in funding
assert 'reservations' not in funding
if is_psbt_v2:
assert psbt['fallback_locktime'] > bitcoind.rpc.getblockcount() - 100
assert psbt['fallback_locktime'] <= bitcoind.rpc.getblockcount()
assert psbt['input_count'] == 1
else:
assert psbt['tx']['locktime'] > bitcoind.rpc.getblockcount() - 100
assert psbt['tx']['locktime'] <= bitcoind.rpc.getblockcount()
assert len(psbt['tx']['vin']) == 1
# This should add 99 to the weight, but otherwise be identical (might choose different inputs though!) except for locktime.
funding2 = l1.rpc.fundpsbt(amount // 2, feerate, 99, reserve=0, locktime=bitcoind.rpc.getblockcount() + 1)
psbt2 = bitcoind.rpc.decodepsbt(funding2['psbt'])
if is_psbt_v2:
assert psbt2['fallback_locktime'] == bitcoind.rpc.getblockcount() + 1
assert psbt2['input_count'] == 1
else:
assert psbt2['tx']['locktime'] == bitcoind.rpc.getblockcount() + 1
assert len(psbt2['tx']['vin']) == 1
assert funding2['excess_msat'] < funding['excess_msat']
assert funding2['feerate_per_kw'] == 7500
# Naively you'd expect this to be +99, but it might have selected a non-p2sh output...
assert funding2['estimated_final_weight'] > funding['estimated_final_weight']
# Cannot afford this one (too much)
with pytest.raises(RpcError, match=r"not afford"):
l1.rpc.fundpsbt(amount * total_outs, feerate, 0)
# Nor this (depth insufficient)
with pytest.raises(RpcError, match=r"not afford"):
l1.rpc.fundpsbt(amount // 2, feerate, 0, minconf=2)
funding3 = l1.rpc.fundpsbt(amount // 2, feerate, 0, reserve=0, excess_as_change=True)
assert funding3['excess_msat'] == Millisatoshi(0)
# Should have the excess msat as the output value (minus fee for change)
psbt = bitcoind.rpc.decodepsbt(funding3['psbt'])
if is_psbt_v2:
change = Millisatoshi("{}btc".format(psbt["outputs"][funding3['change_outnum']]["amount"]))
else:
change = Millisatoshi("{}btc".format(psbt['tx']['vout'][funding3['change_outnum']]['value']))
# The weight should be greater (now includes change output)
change_weight = funding3['estimated_final_weight'] - funding['estimated_final_weight']
assert change_weight > 0
# Check that the amount is ok (equal to excess minus change fee)
change_fee = Millisatoshi(7500 * change_weight)
assert funding['excess_msat'] == change + change_fee
# Should get two inputs.
psbt = bitcoind.rpc.decodepsbt(l1.rpc.fundpsbt(amount, feerate, 0, reserve=0)['psbt'])
if is_psbt_v2:
assert psbt['input_count'] == 2
else:
assert len(psbt['tx']['vin']) == 2
# Should not use reserved outputs.
psbt = bitcoind.rpc.createpsbt([{'txid': out[0], 'vout': out[1]} for out in outputs], [])
l1.rpc.reserveinputs(psbt)
with pytest.raises(RpcError, match=r"not afford"):
l1.rpc.fundpsbt(amount // 2, feerate, 0)
# Will use first one if unreserved.
l1.rpc.unreserveinputs(bitcoind.rpc.createpsbt([{'txid': outputs[0][0], 'vout': outputs[0][1]}], []))
psbt = l1.rpc.fundpsbt(amount // 2, feerate, 0)['psbt']
# Should have passed to reserveinputs.
with pytest.raises(RpcError, match=r"already reserved"):
l1.rpc.reserveinputs(psbt)
# And now we can't afford any more.
with pytest.raises(RpcError, match=r"not afford"):
l1.rpc.fundpsbt(amount // 2, feerate, 0)
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
def test_addpsbtoutput(node_factory, bitcoind, chainparams):
amount1 = 1000000
amount2 = 3333333
locktime = 111
l1 = node_factory.get_node()
result = l1.rpc.addpsbtoutput(amount1, locktime=locktime)
assert result['outnum'] == 0
psbt_info = bitcoind.rpc.decodepsbt(l1.rpc.setpsbtversion(result['psbt'], 0)['psbt'])
assert len(psbt_info['tx']['vout']) == 1
assert psbt_info['tx']['vout'][0]['n'] == result['outnum']
assert psbt_info['tx']['vout'][0]['value'] * 100000000 == amount1
assert psbt_info['tx']['locktime'] == locktime
result = l1.rpc.addpsbtoutput(amount2, result['psbt'])
n = result['outnum']
psbt_info = bitcoind.rpc.decodepsbt(l1.rpc.setpsbtversion(result['psbt'], 0)['psbt'])
assert len(psbt_info['tx']['vout']) == 2
assert psbt_info['tx']['vout'][n]['value'] * 100000000 == amount2
assert psbt_info['tx']['vout'][n]['n'] == result['outnum']
dest = l1.rpc.newaddr('p2tr')['p2tr']
result = l1.rpc.addpsbtoutput(amount2, result['psbt'], destination=dest)
n = result['outnum']
psbt_info = bitcoind.rpc.decodepsbt(l1.rpc.setpsbtversion(result['psbt'], 0)['psbt'])
assert len(psbt_info['tx']['vout']) == 3
assert psbt_info['tx']['vout'][n]['value'] * 100000000 == amount2
assert psbt_info['tx']['vout'][n]['n'] == result['outnum']
assert psbt_info['tx']['vout'][n]['scriptPubKey']['address'] == dest
def test_utxopsbt(node_factory, bitcoind, chainparams):
amount = 1000000
l1 = node_factory.get_node()
# CLN returns PSBTv0 and PSETv2, for now
is_psbt_v2 = chainparams['elements']
outputs = []
# Add a funds to withdraw later
for _ in range(2):
txid = bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('bech32')['bech32'],
amount / 10**8)
outputs.append((txid, bitcoind.rpc.gettransaction(txid)['details'][0]['vout']))
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == len(outputs))
fee_val = 7500
feerate = '{}perkw'.format(fee_val)
# Explicitly spend the first output above.
funding = l1.rpc.utxopsbt(amount // 2, feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1])],
reserve=0)
psbt = bitcoind.rpc.decodepsbt(funding['psbt'])
# We can fuzz this up to 99 blocks back.
assert funding['excess_msat'] > Millisatoshi(0)
assert funding['excess_msat'] < Millisatoshi(amount // 2 * 1000)
assert funding['feerate_per_kw'] == 7500
assert 'estimated_final_weight' in funding
assert 'reservations' not in funding
if is_psbt_v2:
assert psbt['fallback_locktime'] > bitcoind.rpc.getblockcount() - 100
assert psbt['fallback_locktime'] <= bitcoind.rpc.getblockcount()
assert psbt['input_count'] == 1
else:
assert psbt['tx']['locktime'] > bitcoind.rpc.getblockcount() - 100
assert psbt['tx']['locktime'] <= bitcoind.rpc.getblockcount()
assert len(psbt['tx']['vin']) == 1
# This should add 99 to the weight, but otherwise be identical except for locktime.
start_weight = 99
funding2 = l1.rpc.utxopsbt(amount // 2, feerate, start_weight,
['{}:{}'.format(outputs[0][0], outputs[0][1])],
reserve=0, locktime=bitcoind.rpc.getblockcount() + 1)
psbt2 = bitcoind.rpc.decodepsbt(funding2['psbt'])
if is_psbt_v2:
assert psbt2['fallback_locktime'] == bitcoind.rpc.getblockcount() + 1
assert psbt2['inputs'] == psbt['inputs']
else:
assert psbt2['tx']['locktime'] == bitcoind.rpc.getblockcount() + 1
assert psbt2['tx']['vin'] == psbt['tx']['vin']
if chainparams['elements']:
assert is_psbt_v2
# elements includes the fee as an output
addl_fee = Millisatoshi((fee_val * start_weight + 999) // 1000 * 1000)
assert psbt2['outputs'][0]['amount'] == psbt['outputs'][0]['amount'] + addl_fee.to_btc()
else:
assert psbt2['tx']['vout'] == psbt['tx']['vout']
assert funding2['excess_msat'] < funding['excess_msat']
assert funding2['feerate_per_kw'] == 7500
assert funding2['estimated_final_weight'] == funding['estimated_final_weight'] + 99
assert 'reservations' not in funding2
# Cannot afford this one (too much)
with pytest.raises(RpcError, match=r"not afford"):
l1.rpc.utxopsbt(amount, feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1])])
# Nor this (even with both)
with pytest.raises(RpcError, match=r"not afford"):
l1.rpc.utxopsbt(amount * 2, feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1]),
'{}:{}'.format(outputs[1][0], outputs[1][1])])
funding3 = l1.rpc.utxopsbt(amount // 2, feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1])],
reserve=0,
excess_as_change=True)
assert funding3['excess_msat'] == Millisatoshi(0)
# Should have the excess msat as the output value (minus fee for change)
psbt = bitcoind.rpc.decodepsbt(funding3['psbt'])
if is_psbt_v2:
change = Millisatoshi("{}btc".format(psbt['outputs'][funding3['change_outnum']]['amount']))
else:
change = Millisatoshi("{}btc".format(psbt['tx']['vout'][funding3['change_outnum']]['value']))
# The weight should be greater (now includes change output)
change_weight = funding3['estimated_final_weight'] - funding['estimated_final_weight']
assert change_weight > 0
# Check that the amount is ok (equal to excess minus change fee)
change_fee = Millisatoshi(fee_val * change_weight // 1000 * 1000)
assert funding['excess_msat'] == change + change_fee
# Do it again, but without enough for change!
funding4 = l1.rpc.utxopsbt(amount - 3500,
feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1])],
reserve=0,
excess_as_change=True)
assert 'change_outnum' not in funding4
# Should get two inputs (and reserve!)
funding = l1.rpc.utxopsbt(amount, feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1]),
'{}:{}'.format(outputs[1][0], outputs[1][1])])
psbt = bitcoind.rpc.decodepsbt(funding['psbt'])
if is_psbt_v2:
assert psbt['input_count'] == 2
else:
assert len(psbt['tx']['vin']) == 2
assert len(funding['reservations']) == 2
assert funding['reservations'][0]['txid'] == outputs[0][0]
assert funding['reservations'][0]['vout'] == outputs[0][1]
assert funding['reservations'][0]['was_reserved'] is False
assert funding['reservations'][0]['reserved'] is True
assert funding['reservations'][1]['txid'] == outputs[1][0]
assert funding['reservations'][1]['vout'] == outputs[1][1]
assert funding['reservations'][1]['was_reserved'] is False
assert funding['reservations'][1]['reserved'] is True
# Should refuse to use reserved outputs.
with pytest.raises(RpcError, match=r"already reserved"):
l1.rpc.utxopsbt(amount, feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1]),
'{}:{}'.format(outputs[1][0], outputs[1][1])])
# Unless we tell it that's ok.
l1.rpc.utxopsbt(amount, feerate, 0,
['{}:{}'.format(outputs[0][0], outputs[0][1]),
'{}:{}'.format(outputs[1][0], outputs[1][1])],
reservedok=True)
def test_sign_external_psbt(node_factory, bitcoind, chainparams):
"""
A PSBT w/ one of our inputs should be signable (we can fill
in the required UTXO data).
"""
l1 = node_factory.get_node(feerates=(7500, 7500, 7500, 7500))
amount = 1000000
total_outs = 4
# Add a medley of funds to withdraw later
for i in range(total_outs):
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('bech32')['bech32'],
amount / 10**8)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs)
# Build a PSBT using all our inputs, externally
inputs = []
for inp in l1.rpc.listfunds()['outputs']:
inputs.append({'txid': inp['txid'], 'vout': inp['output']})
addr = l1.rpc.newaddr('bech32')['bech32']
psbt = bitcoind.rpc.createpsbt(inputs, [{addr: (amount * 3) / 10**8}])
l1.rpc.reserveinputs(psbt)
l1.rpc.signpsbt(psbt)
def test_sign_signed_psbt(node_factory, bitcoind, chainparams):
l1 = node_factory.get_node()
l1.fundwallet(10**6)
psbt = l1.rpc.txprepare([{l1.rpc.newaddr('bech32')['bech32']: 10000}])['psbt']
signed_psbt = l1.rpc.signpsbt(psbt)['signed_psbt']
if TEST_NETWORK != 'liquid-regtest':
# FIXME: ideally this would succeed, as a noop. But it shouldn't crash
with pytest.raises(RpcError):
l1.rpc.signpsbt(signed_psbt)['signed_psbt']
else:
# Non-taproot works fine.
assert l1.rpc.signpsbt(signed_psbt)['signed_psbt'] == signed_psbt
def test_psbt_version(node_factory, bitcoind, chainparams):
sats_amount = 10**8
# CLN returns PSBTv0 and PSETv2, for now
is_elements = chainparams['elements']
l1 = node_factory.get_node()
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('bech32')['bech32'],
sats_amount / 100000000)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1)
funding = l1.rpc.fundpsbt(satoshi=int(sats_amount / 2),
feerate=7500,
startweight=42)['psbt']
# Short elements test
if is_elements:
# Only v2 is allowed, and is a no-op
for i in [0, 1, 3, 4, 5]:
with pytest.raises(RpcError, match=r"Could not set PSBT version"):
l1.rpc.setpsbtversion(funding, i)
assert funding == l1.rpc.setpsbtversion(funding, 2)['psbt']
# And elementsd can understand it
bitcoind.rpc.decodepsbt(funding)
return
# Non-elements test
v2_funding = l1.rpc.setpsbtversion(funding, 2)['psbt']
# Bitcoind cannot understand PSBTv2 yet
with pytest.raises(JSONRPCError, match=r"TX decode failed Unsupported version number"):
bitcoind.rpc.decodepsbt(v2_funding)
# But it round-trips fine enough
v0_funding = l1.rpc.setpsbtversion(v2_funding, 0)['psbt']
# CLN returns v0 for now
assert funding == v0_funding
# And we reject non-0/2 args
for i in [1, 3, 4, 5]:
with pytest.raises(RpcError, match=r"Could not set PSBT version"):
l1.rpc.setpsbtversion(v2_funding, i)
@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', 'Core/Elements need joinpsbt support for v2')
def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
"""
Tests for the sign + send psbt RPCs
"""
# CLN returns PSBTv0 and PSETv2, for now
is_psbt_v2 = chainparams['elements']
# Once support for v2 joinpsbt is added, below test should work verbatim
assert not is_psbt_v2
amount = 1000000
total_outs = 12
coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')
l1 = node_factory.get_node(options={'plugin': coin_mvt_plugin},
feerates=(7500, 7500, 7500, 7500))
l2 = node_factory.get_node()
addr = chainparams['example_addr']
out_total = Millisatoshi(amount * 3 * 1000)
# Add a medley of funds to withdraw later
for i in range(total_outs):
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('bech32')['bech32'],
amount / 10**8)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs)
# Make a PSBT out of our inputs
funding = l1.rpc.fundpsbt(satoshi=out_total,
feerate=7500,
startweight=42)
assert len([x for x in l1.rpc.listfunds()['outputs'] if x['reserved']]) == 4
psbt = bitcoind.rpc.decodepsbt(funding['psbt'])
if is_psbt_v2:
saved_input = psbt['inputs'][0]
else:
saved_input = psbt['tx']['vin'][0]
# Go ahead and unreserve the UTXOs, we'll use a smaller
# set of them to create a second PSBT that we'll attempt to sign
# and broadcast (to disastrous results)
l1.rpc.unreserveinputs(funding['psbt'])
# Re-reserve one of the utxos we just unreserved
if is_psbt_v2:
psbt = bitcoind.rpc.createpsbt([{'txid': saved_input['previous_txid'],
'vout': saved_input['previous_vout']}], [])
else:
psbt = bitcoind.rpc.createpsbt([{'txid': saved_input['txid'],
'vout': saved_input['vout']}], [])
l1.rpc.reserveinputs(psbt)
# We require the utxos be reserved before signing them
with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO .* is not reserved"):
l1.rpc.signpsbt(funding['psbt'])['signed_psbt']
# Now we unreserve the singleton, so we can reserve it again
l1.rpc.unreserveinputs(psbt)
# Now add an output. Note, we add the 'excess msat' to the output so
# that our feerate is 'correct'. This is of particular importance to elementsd,
# who requires that every satoshi be accounted for in a tx.
out_1_ms = Millisatoshi(funding['excess_msat'])
output_psbt = bitcoind.rpc.createpsbt([],
[{addr: float((out_total + out_1_ms).to_btc())}])
fullpsbt = bitcoind.rpc.joinpsbts([funding['psbt'], output_psbt])
# We re-reserve the first set...
l1.rpc.reserveinputs(fullpsbt)
# Sign + send the PSBT we've created
signed_psbt = l1.rpc.signpsbt(fullpsbt)['signed_psbt']
broadcast_tx = l1.rpc.sendpsbt(signed_psbt)
# Check that it was broadcast successfully
l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx']))
bitcoind.generate_block(1)
# We didn't add a change output.
expected_outs = total_outs - 4
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == expected_outs)
# Let's try *sending* a PSBT that can't be finalized (it's unsigned)
with pytest.raises(RpcError, match=r"PSBT not finalizeable"):
l1.rpc.sendpsbt(fullpsbt)
# Now we try signing a PSBT with an output that's already been spent
with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO .* is not reserved"):
l1.rpc.signpsbt(fullpsbt)
# Queue up another node, to make some PSBTs for us
for i in range(total_outs):
bitcoind.rpc.sendtoaddress(l2.rpc.newaddr('bech32')['bech32'],
amount / 10**8)
# Create a PSBT using L2
bitcoind.generate_block(1)
wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == total_outs)
l2_funding = l2.rpc.fundpsbt(satoshi=out_total,
feerate=7500,
startweight=42)
# Try to get L1 to sign it
with pytest.raises(RpcError, match=r"No wallet inputs to sign"):
l1.rpc.signpsbt(l2_funding['psbt'])
# With signonly it will fail if it can't sign it.
with pytest.raises(RpcError, match=r"is unknown"):
l1.rpc.signpsbt(l2_funding['psbt'], signonly=[0])
# Add some of our own PSBT inputs to it
l1_funding = l1.rpc.fundpsbt(satoshi=out_total,
feerate=7500,
startweight=42)
if is_psbt_v2:
l1_num_inputs = bitcoind.rpc.decodepsbt(l1_funding['psbt'])["input_count"]
l2_num_inputs = bitcoind.rpc.decodepsbt(l2_funding['psbt'])["input_count"]
else:
l1_num_inputs = len(bitcoind.rpc.decodepsbt(l1_funding['psbt'])['tx']['vin'])
l2_num_inputs = len(bitcoind.rpc.decodepsbt(l2_funding['psbt'])['tx']['vin'])
# Join and add an output (reorders!)
out_2_ms = Millisatoshi(l1_funding['excess_msat'])
out_amt = out_2_ms + Millisatoshi(l2_funding['excess_msat']) + out_total + out_total
output_psbt = bitcoind.rpc.createpsbt([],
[{addr: float(out_amt.to_btc())}])
joint_psbt = bitcoind.rpc.joinpsbts([l1_funding['psbt'], l2_funding['psbt'],
output_psbt])
# Ask it to sign inputs it doesn't know, it will fail.
with pytest.raises(RpcError, match=r"is unknown"):
l1.rpc.signpsbt(joint_psbt,
signonly=list(range(l1_num_inputs + 1)))
# Similarly, it can't sign inputs it doesn't know.
sign_success = []
for i in range(l1_num_inputs + l2_num_inputs):
try:
l1.rpc.signpsbt(joint_psbt, signonly=[i])
except RpcError:
continue
sign_success.append(i)
assert len(sign_success) == l1_num_inputs
# But it can sign all the valid ones at once.
half_signed_psbt = l1.rpc.signpsbt(joint_psbt, signonly=sign_success)['signed_psbt']
for s in sign_success:
assert bitcoind.rpc.decodepsbt(half_signed_psbt)['inputs'][s]['partial_signatures'] is not None
totally_signed = l2.rpc.signpsbt(half_signed_psbt)['signed_psbt']
broadcast_tx = l1.rpc.sendpsbt(totally_signed)
l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx']))
# Send a PSBT that's not ours
l2_funding = l2.rpc.fundpsbt(satoshi=out_total,
feerate=7500,
startweight=42)
out_amt = Millisatoshi(l2_funding['excess_msat'])
output_psbt = bitcoind.rpc.createpsbt([],
[{addr: float((out_total + out_amt).to_btc())}])
psbt = bitcoind.rpc.joinpsbts([l2_funding['psbt'], output_psbt])
l2_signed_psbt = l2.rpc.signpsbt(psbt)['signed_psbt']
l1.rpc.sendpsbt(l2_signed_psbt)
# Re-try sending the same tx?
bitcoind.generate_block(1)
sync_blockheight(bitcoind, [l1])
# Expect an error here (bitcoind > 28 gives the UTXO set message)
with pytest.raises(JSONRPCError, match=r"Transaction already in block chain|Transaction outputs already in utxo set"):
bitcoind.rpc.sendrawtransaction(broadcast_tx['tx'])
# Try an empty PSBT
with pytest.raises(RpcError, match=r"psbt: Expected a PSBT: invalid token"):
l1.rpc.signpsbt('')
with pytest.raises(RpcError, match=r"psbt: Expected a PSBT: invalid token"):
l1.rpc.sendpsbt('')
# Try an invalid PSBT string
invalid_psbt = 'cHNidP8BAM0CAAAABJ9446mTRp/ml8OxSLC1hEvrcxG1L02AG7YZ4syHon2sAQAAAAD9////JFJH/NjKwjwrP9myuU68G7t8Q4VIChH0KUkZ5hSAyqcAAAAAAP3///8Uhrj0XDRhGRno8V7qEe4hHvZcmEjt3LQSIXWc+QU2tAEAAAAA/f///wstLikuBrgZJI83VPaY8aM7aPe5U6TMb06+jvGYzQLEAQAAAAD9////AcDGLQAAAAAAFgAUyQltQ/QI6lJgICYsza18hRa5KoEAAAAAAAEBH0BCDwAAAAAAFgAUqc1Qh7Q5kY1noDksmj7cJmHaIbQAAQEfQEIPAAAAAAAWABS3bdYeQbXvBSryHNoyYIiMBwu5rwABASBAQg8AAAAAABepFD1r0NuqAA+R7zDiXrlP7J+/PcNZhwEEFgAUKvGgVL/ThjWE/P1oORVXh/ObucYAAQEgQEIPAAAAAAAXqRRsrE5ugA1VJnAith5+msRMUTwl8ocBBBYAFMrfGCiLi0ZnOCY83ERKJ1sLYMY8A='
with pytest.raises(RpcError, match=r"psbt: Expected a PSBT: invalid token"):
l1.rpc.signpsbt(invalid_psbt)
wallet_coin_mvts = [
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']},
{'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']},
{'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']},
{'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']},
{'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']},
{'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']},
{'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']},
{'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']},
]
check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams)
@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "BIP86 random_hsm not compatible with liquid-regtest bech32")
def test_txsend(node_factory, bitcoind, chainparams):
amount = 1000000
l1 = node_factory.get_node(random_hsm=True)
addr = chainparams['example_addr']
# Add some funds to withdraw later
for i in range(10):
bitcoind.rpc.sendtoaddress(l1.rpc.newaddr(good_addrtype())[good_addrtype()],
amount / 10**8)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
prep = l1.rpc.txprepare([{addr: Millisatoshi(amount * 3 * 1000)}])
out = l1.rpc.txsend(prep['txid'])
# Cannot discard after send!
with pytest.raises(RpcError, match=r'not an unreleased txid'):
l1.rpc.txdiscard(prep['txid'])
wait_for(lambda: prep['txid'] in bitcoind.rpc.getrawmempool())
# Signed tx should have same txid
decode = bitcoind.rpc.decoderawtransaction(out['tx'])
assert decode['txid'] == prep['txid']
bitcoind.generate_block(1)
# Change output should appear.
if decode['vout'][0]['value'] == Decimal(amount * 3) / 10**8:
changenum = 1
elif decode['vout'][1]['value'] == Decimal(amount * 3) / 10**8:
changenum = 0
else:
assert False
# Those spent outputs are gone, but change output has arrived.
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10 - len(decode['vin']) + 1)
# Change address should appear in listfunds()
assert scriptpubkey_addr(decode['vout'][changenum]['scriptPubKey']) in [f['address'] for f in l1.rpc.listfunds()['outputs']]
def write_all(fd, bytestr):
"""Wrapper, since os.write can do partial writes"""
off = 0
while off < len(bytestr):
off += os.write(fd, bytestr[off:])
class HsmTool(TailableProc):
"""Helper for testing the hsmtool as a subprocess"""
def __init__(self, directory, *args):
self.prefix = "hsmtool"
TailableProc.__init__(self, os.path.join(directory, "hsmtool"))
assert hasattr(self, "env")
self.cmd_line = ["tools/hsmtool", *args]
@unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.")
def test_hsmtool_secret_decryption(node_factory):
"""Test that we can encrypt and decrypt hsm_secret using hsmtool"""
l1 = node_factory.get_node(start=False) # Don't start the node
password = "test_password\n"
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
# Write a known 32-byte key to hsm_secret
known_secret = b'\x01' * 32 # 32 bytes of 0x01
with open(hsm_path, 'wb') as f:
f.write(known_secret)
# Read the hsm_secret to verify it's what we expect
with open(hsm_path, 'rb') as f:
content = f.read()
assert content == known_secret, f"Expected {known_secret}, got {content}"
assert len(content) == 32, f"Expected 32 bytes, got {len(content)}"
# Encrypt it using hsmtool
master_fd, slave_fd = os.openpty()
hsmtool = HsmTool(node_factory.directory, "encrypt", hsm_path)
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Enter hsm_secret password:")
write_all(master_fd, password.encode("utf-8"))
hsmtool.wait_for_log(r"Confirm hsm_secret password:")
write_all(master_fd, password.encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
hsmtool.is_in_log(r"Successfully encrypted")
# Read the hsm_secret again - it should now be encrypted (73 bytes)
with open(hsm_path, 'rb') as f:
encrypted_content = f.read()
assert len(encrypted_content) == 73, f"Expected 73 bytes after encryption, got {len(encrypted_content)}"
assert encrypted_content != known_secret, "File should be encrypted and different from original"
# Decrypt it using hsmtool
master_fd, slave_fd = os.openpty()
hsmtool = HsmTool(node_factory.directory, "decrypt", hsm_path)
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Enter hsm_secret password:")
write_all(master_fd, password.encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
hsmtool.is_in_log(r"Successfully decrypted")
# Read the hsm_secret again - it should now be back to the original 32 bytes
with open(hsm_path, 'rb') as f:
decrypted_content = f.read()
assert decrypted_content == known_secret, f"Expected {known_secret}, got {decrypted_content}"
assert len(decrypted_content) == 32, f"Expected 32 bytes after decryption, got {len(decrypted_content)}"
@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', '')
def test_hsmtool_dump_descriptors(node_factory, bitcoind):
l1 = node_factory.get_node()
l1.fundwallet(10**6)
# Get a tpub descriptor of lightningd's wallet
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
cmd_line = ["tools/hsmtool", "dumponchaindescriptors", hsm_path, "testnet"]
descriptors = subprocess.check_output(cmd_line).decode("utf8").split("\n")
# Deprecated or empty line
descriptors = [desc for desc in descriptors if not (desc.startswith("sh(wpkh(") or desc == '')]
withdraw_addr = None
index_offset = 2 # index starts handing out addrs at 2
# Generate twenty addresses for all known descriptors
cln_addrs = [l1.rpc.newaddr('all') for _ in range(20)]
for descriptor in descriptors:
for i, cln_addr in enumerate(cln_addrs):
computed_addr = bitcoind.rpc.deriveaddresses(descriptor, [i + index_offset, i + index_offset])[0]
if descriptor.startswith("wpkh"):
assert cln_addr["bech32"] == computed_addr
withdraw_addr = cln_addr["bech32"]
elif descriptor.startswith("tr"):
assert cln_addr["p2tr"] == computed_addr
withdraw_addr = cln_addr["p2tr"]
else:
raise Exception('Unexpected descriptor!')
# For last address per type:
# Funds sent to lightningd can be retrieved by bitcoind
txid = l1.rpc.withdraw(withdraw_addr, 10**3)["txid"]
bitcoind.generate_block(1, txid, bitcoind.rpc.getnewaddress())
l1.daemon.wait_for_log('Owning output .* txid {} CONFIRMED'.format(txid))
actual_index = len(cln_addrs) - 1 + index_offset
res = bitcoind.rpc.scantxoutset("start", [{"desc": descriptor, "range": [actual_index, actual_index]}])
assert res["total_amount"] == Decimal('0.00001000')
@pytest.mark.parametrize("mnemonic,passphrase,expected_format", [
("ritual idle hat sunny universe pluck key alpha wing cake have wedding", "test_passphrase", "mnemonic with passphrase"),
("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", "", "mnemonic without passphrase"),
])
def test_hsmtool_generatehsm_variants(node_factory, mnemonic, passphrase, expected_format):
"""Test generating mnemonic-based hsm_secret with various configurations"""
# Only set hsm-passphrase option if there's actually a passphrase
node_options = {'hsm-passphrase': None} if passphrase else {}
l1 = node_factory.get_node(start=False, options=node_options)
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
os.remove(hsm_path) # Remove the auto-generated one
# Generate hsm_secret with mnemonic and passphrase
hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Introduce your BIP39 word list separated by space")
write_all(master_fd, f"{mnemonic}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Enter your passphrase:")
write_all(master_fd, f"{passphrase}\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
hsmtool.is_in_log(r"New hsm_secret file created")
hsmtool.is_in_log(f"Format: {expected_format}")
# Verify file format
with open(hsm_path, 'rb') as f:
content = f.read()
if passphrase:
# First 32 bytes should NOT be zeros (has passphrase hash)
assert content[:32] != b'\x00' * 32
assert mnemonic.encode('utf-8') in content[32:]
else:
# First 32 bytes should be zeros (no passphrase)
assert content[:32] == b'\x00' * 32
# Rest should be the mnemonic
mnemonic_part = content[32:].decode('utf-8')
assert mnemonic in mnemonic_part
# Verify Lightning node can use it
if passphrase:
# For passphrase case, start with hsm-passphrase option and handle prompt
master_fd, slave_fd = os.openpty()
l1.daemon.start(stdin=slave_fd, wait_for_initialized=False)
# Wait for the passphrase prompt
l1.daemon.wait_for_log("Enter hsm_secret passphrase:")
write_all(master_fd, f"{passphrase}\n".encode("utf-8"))
l1.daemon.wait_for_log("Server started with public key")
else:
# For no passphrase case, start normally without expecting a prompt
l1.daemon.start(wait_for_initialized=False)
l1.daemon.wait_for_log("Server started with public key")
node_id = l1.rpc.getinfo()['id']
print(f"Node ID for mnemonic '{mnemonic}' with passphrase '{passphrase}': {node_id}")
assert len(node_id) == 66 # Valid node ID
# Expected node IDs for deterministic testing
expected_node_ids = {
("ritual idle hat sunny universe pluck key alpha wing cake have wedding", "test_passphrase"): "039020371fb803cd4ce1e9a909b502d7b0a9e0f10cccc35c3e9be959c52d3ba6bd",
("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", ""): "03653e90c1ce4660fd8505dd6d643356e93cfe202af109d382787639dd5890e87d",
}
expected_id = expected_node_ids.get((mnemonic, passphrase))
if expected_id:
assert node_id == expected_id, f"Expected node ID {expected_id}, got {node_id}"
else:
print(f"No expected node ID found for this combination, got: {node_id}")
@pytest.mark.parametrize("test_case", [
pytest.param({
"name": "with_passphrase",
"mnemonic": "ritual idle hat sunny universe pluck key alpha wing cake have wedding",
"passphrase": "secret_passphrase",
"check_passphrase": "secret_passphrase",
"check_mnemonic": "ritual idle hat sunny universe pluck key alpha wing cake have wedding",
"expected_exit": 0,
"expected_log": "OK"
}, id="correct_mnemonic_with_passphrase"),
pytest.param({
"name": "no_passphrase",
"mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
"passphrase": "",
"check_passphrase": "",
"check_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
"expected_exit": 0,
"expected_log": "OK"
}, id="correct_mnemonic_no_passphrase"),
pytest.param({
"name": "wrong_passphrase",
"mnemonic": "ritual idle hat sunny universe pluck key alpha wing cake have wedding",
"passphrase": "correct_passphrase",
"check_passphrase": "wrong_passphrase",
"check_mnemonic": "ritual idle hat sunny universe pluck key alpha wing cake have wedding",
"expected_exit": 5, # ERROR_KEYDERIV
"expected_log": "resulting hsm_secret did not match"
}, id="wrong_passphrase_should_fail"),
pytest.param({
"name": "wrong_mnemonic",
"mnemonic": "ritual idle hat sunny universe pluck key alpha wing cake have wedding",
"passphrase": "",
"check_passphrase": "",
"check_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
"expected_exit": 5, # ERROR_KEYDERIV
"expected_log": "resulting hsm_secret did not match"
}, id="wrong_mnemonic_should_fail")
])
def test_hsmtool_checkhsm_variants(node_factory, test_case):
"""Test checkhsm with various configurations"""
l1 = node_factory.get_node(start=False)
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
os.remove(hsm_path)
# Create hsm_secret with known mnemonic and passphrase
hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Introduce your BIP39 word list separated by space")
write_all(master_fd, f"{test_case['mnemonic']}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Enter your passphrase:")
write_all(master_fd, f"{test_case['passphrase']}\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
# Test checkhsm with credentials
hsmtool = HsmTool(node_factory.directory, "checkhsm", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
# If the original had a passphrase, we need to unlock the file first
if test_case['passphrase']:
hsmtool.wait_for_log(r"Enter hsm_secret password:") # Decrypt file
write_all(master_fd, f"{test_case['passphrase']}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Enter your mnemonic passphrase:") # Backup verification
write_all(master_fd, f"{test_case['check_passphrase']}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Introduce your BIP39 word list separated by space")
write_all(master_fd, f"{test_case['check_mnemonic']}\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == test_case['expected_exit']
hsmtool.is_in_log(test_case['expected_log'])
def test_hsmtool_checkhsm_legacy_encrypted_with_mnemonic_no_passphrase(node_factory):
"""Test checkhsm with legacy encrypted hsm_secret containing mnemonic without passphrase"""
l1 = node_factory.get_node(start=False)
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
os.remove(hsm_path)
seed_hex = "31bb58d1180831868fd5f562bb74659dca1e9673d034af635df53d677b9e5f03"
seed_bytes = bytes.fromhex(seed_hex)
# Write the 32-byte seed directly to file (simulating old generatehsm output)
# Make sure we write exactly 32 bytes with no newline
assert len(seed_bytes) == 32, f"Seed should be exactly 32 bytes, got {len(seed_bytes)}"
with open(hsm_path, 'wb') as f:
f.write(seed_bytes)
# Verify it's exactly 32 bytes
with open(hsm_path, 'rb') as f:
content = f.read()
print(content)
assert content == seed_bytes, "File content doesn't match expected seed"
# Now encrypt it using the legacy encrypt command
encryption_password = "encryption_password"
hsmtool = HsmTool(node_factory.directory, "encrypt", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Enter hsm_secret password:")
write_all(master_fd, f"{encryption_password}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Confirm hsm_secret password:")
write_all(master_fd, f"{encryption_password}\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
hsmtool.is_in_log(r"Successfully encrypted")
# Verify the file is now encrypted (73 bytes)
with open(hsm_path, 'rb') as f:
content = f.read()
assert len(content) == 73, f"Expected 73 bytes after encryption, got {len(content)}"
# Test checkhsm - should prompt for encryption password first, then mnemonic passphrase
hsmtool = HsmTool(node_factory.directory, "checkhsm", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Enter hsm_secret password:") # Encryption password
write_all(master_fd, f"{encryption_password}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Enter your mnemonic passphrase:") # Mnemonic passphrase (empty)
write_all(master_fd, "\n".encode("utf-8")) # Empty passphrase
hsmtool.wait_for_log(r"Introduce your BIP39 word list separated by space")
write_all(master_fd, "blame expire peanut sell door zoo bundle motor truth outside artist siren\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
hsmtool.is_in_log(r"OK")
def test_hsmtool_checkhsm_legacy_encrypted_with_mnemonic_passphrase(node_factory):
"""Test checkhsm with legacy encrypted hsm_secret containing mnemonic with passphrase"""
l1 = node_factory.get_node(start=False)
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
os.remove(hsm_path)
# Directly write the 32-byte seed from mnemonic with passphrase
# Mnemonic: "blame expire peanut sell door zoo bundle motor truth outside artist siren"
# Passphrase: "passphrase"
# Expected BIP39 seed (first 32 bytes): 161d740bcfd3c5e2a1769159bee86868ab35e7544e83e825042a43b929ad950c
seed_hex = "161d740bcfd3c5e2a1769159bee86868ab35e7544e83e825042a43b929ad950c"
seed_bytes = bytes.fromhex(seed_hex)
# Write the 32-byte seed directly to file (simulating old generatehsm output)
with open(hsm_path, 'wb') as f:
f.write(seed_bytes)
# Verify it's 32 bytes
with open(hsm_path, 'rb') as f:
content = f.read()
assert len(content) == 32, f"Expected 32 bytes, got {len(content)}"
# Now encrypt it using the legacy encrypt command
encryption_password = "encryption_password"
hsmtool = HsmTool(node_factory.directory, "encrypt", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Enter hsm_secret password:")
write_all(master_fd, f"{encryption_password}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Confirm hsm_secret password:")
write_all(master_fd, f"{encryption_password}\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
hsmtool.is_in_log(r"Successfully encrypted")
# Verify the file is now encrypted (73 bytes)
with open(hsm_path, 'rb') as f:
content = f.read()
assert len(content) == 73, f"Expected 73 bytes after encryption, got {len(content)}"
# Test checkhsm - should prompt for encryption password first, then mnemonic passphrase
hsmtool = HsmTool(node_factory.directory, "checkhsm", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Enter hsm_secret password:") # Encryption password
write_all(master_fd, f"{encryption_password}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Enter your mnemonic passphrase:") # Mnemonic passphrase
write_all(master_fd, "passphrase\n".encode("utf-8"))
hsmtool.wait_for_log(r"Introduce your BIP39 word list separated by space")
write_all(master_fd, "blame expire peanut sell door zoo bundle motor truth outside artist siren\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
hsmtool.is_in_log(r"OK")
def test_hsmtool_generatehsm_file_exists_error(node_factory):
"""Test that generatehsm fails if file already exists"""
l1 = node_factory.get_node(start=False)
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
# File already exists from node creation
hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 2 # ERROR_USAGE
hsmtool.is_in_log(r"hsm_secret file.*already exists")
def test_hsmtool_all_commands_work_with_mnemonic_formats(node_factory):
"""Test that all hsmtool commands work with mnemonic formats"""
l1 = node_factory.get_node(start=False)
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
os.remove(hsm_path)
# Create a mnemonic-based hsm_secret (no passphrase for simplicity)
hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Introduce your BIP39 word list")
write_all(master_fd, "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\n".encode("utf-8"))
hsmtool.wait_for_log(r"Enter your passphrase:")
write_all(master_fd, "\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
# Test various commands work with mnemonic format
test_commands = [
(["getnodeid", hsm_path], "03653e90c1ce4660fd8505dd6d643356e93cfe202af109d382787639dd5890e87d"),
(["getcodexsecret", hsm_path, "test"], "cl10testst6cqh0wu7p5ssjyf4z4ez42ks9jlt3zneju9uuypr2hddak6tlqsghuxusm6m6azq"),
(["makerune", hsm_path], "6VkrWMI2hm2a2UTkg-EyUrrBJN0RcuPB80I1pCVkTD89MA=="),
(["dumponchaindescriptors", hsm_path], "wpkh(xpub661MyMwAqRbcG9kjo3mdWQuSDbtdJzsd3K2mvifyeUMF3GhLcBAfELqjuxCvxUkYqQVe6rJ9SzmpipoUedb5MD79MJaLL8RME2A3J3Fw6Zd/0/0/*)#2jtshmk0\nsh(wpkh(xpub661MyMwAqRbcG9kjo3mdWQuSDbtdJzsd3K2mvifyeUMF3GhLcBAfELqjuxCvxUkYqQVe6rJ9SzmpipoUedb5MD79MJaLL8RME2A3J3Fw6Zd/0/0/*))#u6am4was\ntr(xpub661MyMwAqRbcG9kjo3mdWQuSDbtdJzsd3K2mvifyeUMF3GhLcBAfELqjuxCvxUkYqQVe6rJ9SzmpipoUedb5MD79MJaLL8RME2A3J3Fw6Zd/0/0/*)#v9hf4756"),
]
for cmd_args, expected_output in test_commands:
cmd_line = ["tools/hsmtool"] + cmd_args
out = subprocess.check_output(cmd_line).decode("utf8")
actual_output = out.strip()
assert actual_output == expected_output, f"Command {cmd_args[0]} output mismatch"
def test_hsmtool_deterministic_node_ids(node_factory):
"""Test that HSM daemon creates deterministic node IDs in new mnemonic format"""
# Create a node and start it to trigger HSM daemon to create new format
l1 = node_factory.get_node(start=False)
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
# Delete any existing hsm_secret so HSM daemon creates it in new format
if os.path.exists(hsm_path):
os.remove(hsm_path)
# Start the node to get its node ID (this will create a new hsm_secret in new format)
l1.start()
normal_node_id = l1.rpc.getinfo()['id']
l1.stop()
# Verify the hsm_secret was created in the new mnemonic format
with open(hsm_path, 'rb') as f:
content = f.read()
# Should be longer than 32 bytes (new format has 32-byte hash + mnemonic)
assert len(content) > 32, f"Expected new mnemonic format, got {len(content)} bytes"
# First 32 bytes should be the passphrase hash (likely zeros for no passphrase)
passphrase_hash = content[:32]
assert passphrase_hash == b'\x00' * 32
mnemonic_bytes = content[32:]
# Decode the mnemonic bytes
mnemonic = mnemonic_bytes.decode('utf-8').strip()
# Verify it's a valid mnemonic (should be 12 words)
words = mnemonic.split()
assert len(words) == 12, f"Expected 12 words, got {len(words)}: {mnemonic}"
# Create a second node and use generatehsm with the mnemonic from the first node
l2 = node_factory.get_node(start=False)
hsm_path2 = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
# Delete any existing hsm_secret for the second node
if os.path.exists(hsm_path2):
os.remove(hsm_path2)
# Generate hsm_secret with the mnemonic from the first node
hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path2)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Introduce your BIP39 word list")
write_all(master_fd, f"{mnemonic}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Enter your passphrase:")
write_all(master_fd, "\n".encode("utf-8"))
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
# Get the node ID from the generated hsm_secret
cmd_line = ["tools/hsmtool", "getnodeid", hsm_path2]
generated_node_id = subprocess.check_output(cmd_line).decode("utf8").strip()
# Verify both node IDs are identical
assert normal_node_id == generated_node_id, f"Node IDs don't match: {normal_node_id} != {generated_node_id}"
def setup_bip86_node(node_factory, mnemonic="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"):
"""Helper function to set up a node with BIP86 support using a mnemonic-based HSM secret"""
l1 = node_factory.get_node(start=False)
# Set up node with a mnemonic HSM secret
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
if os.path.exists(hsm_path):
os.remove(hsm_path)
# Generate hsm_secret with the specified mnemonic
hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path)
master_fd, slave_fd = os.openpty()
hsmtool.start(stdin=slave_fd)
hsmtool.wait_for_log(r"Introduce your BIP39 word list")
write_all(master_fd, f"{mnemonic}\n".encode("utf-8"))
hsmtool.wait_for_log(r"Enter your passphrase:")
write_all(master_fd, "\n".encode("utf-8")) # No passphrase
assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0
os.close(master_fd)
os.close(slave_fd)
l1.start()
return l1
@unittest.skipIf(TEST_NETWORK != 'regtest', "BIP86 tests are regtest-specific")
def test_bip86_newaddr_rpc(node_factory, chainparams):
"""Test that BIP86 addresses can be generated via newaddr RPC"""
l1 = setup_bip86_node(node_factory)
# Test BIP86 address generation
bip86_addr = l1.rpc.newaddr(addresstype="p2tr")
assert 'p2tr' in bip86_addr
assert 'bech32' not in bip86_addr
# Verify address format (taproot addresses are longer)
p2tr_addr = bip86_addr['p2tr']
assert len(p2tr_addr) > 50
# In regtest, should start with bcrt1p (or appropriate prefix)
assert p2tr_addr.startswith('bcrt1p')
# Test that we're using the correct 64-byte seed from the mnemonic
# Expected seed for "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about":
# "5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4"
# Test that our BIP86 implementation follows the correct derivation path m/86'/0'/0'/0/index
# Generate the same address again and verify it's identical
bip86_addr2 = l1.rpc.newaddr(addresstype="p2tr")
p2tr_addr2 = bip86_addr2['p2tr']
# The second address should be different (next index)
assert p2tr_addr != p2tr_addr2, "Consecutive BIP86 addresses should be different"
# Test against known test vectors for the exact derivation path
# The mainnet test vectors are:
# m/86'/0'/0'/0/0: bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr
# m/86'/0'/0'/0/1: bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh
# m/86'/0'/0'/0/2: bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8
# For regtest, the addresses should be the same but with bcrt1p prefix
# Our addresses are for indices 1 and 2, so they should match the regtest versions
expected_regtest_addr_1 = "bcrt1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0waslcutpz" # index 1
expected_regtest_addr_2 = "bcrt1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzsl8t0dj" # index 2
# Assert on the exact test vectors since we have the correct seed
assert p2tr_addr == expected_regtest_addr_1, f"First address should match test vector for index 1. Expected: {expected_regtest_addr_1}, Got: {p2tr_addr}"
assert p2tr_addr2 == expected_regtest_addr_2, f"Second address should match test vector for index 2. Expected: {expected_regtest_addr_2}, Got: {p2tr_addr2}"
@unittest.skipIf(TEST_NETWORK != 'regtest', "BIP86 tests are regtest-specific")
def test_bip86_listaddresses(node_factory, chainparams):
"""Test that listaddresses includes BIP86 addresses and verifies first 10 addresses"""
l1 = setup_bip86_node(node_factory)
# Expected addresses for the first 10 indices (m/86'/0'/0'/0/1 through m/86'/0'/0'/0/10)
# These are derived from the test mnemonic "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
# Note: newaddr starts from index 1, not 0
# Actual regtest addresses generated by the implementation
expected_addrs = [
"bcrt1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0waslcutpz", # index 1
"bcrt1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzsl8t0dj", # index 2
"bcrt1py0vryk8aqusz65yzuudypggvswzkcpwtau8q0sjm0stctwup0xlqv86kkk", # index 3
"bcrt1pjpp8nwqvhkx6kdna6vpujdqglvz2304twfd308ve5ppyxpmcjufsy8x0tk", # index 4
"bcrt1pl4frjws098l3nslfjlnry6jxt46w694kuexvs5ar0cmkvxyahfkq42fumu", # index 5
"bcrt1p5sxs429uz2s2yn6tt98sf67qwytwvffae4dqnzracq586cu0t6zsn63pre", # index 6
"bcrt1pxsvy7ep2awd5x9lg90tgm4xre8wxcuj5cpgun8hmzwqnltqha8pqv84cl7", # index 7
"bcrt1ptk8pqtszta5pv5tymccfqkezf3f2q39765q4fj8zcr79np6wmj6qeek4z3", # index 8
"bcrt1p7pkeudt8wq7fc6nzj6yxkqmnrjg25fu4s9l777ca3w3qrxanjehq4tphz0", # index 9
"bcrt1pzhnqyfvxe08zl0d9e592t62pwvp3l2xwhau5a8dsfjcker6xmjuqppvxka", # index 10
]
# Generate the first 10 BIP86 addresses and verify they match expected values
for i in range(10):
addr_result = l1.rpc.newaddr('p2tr')
assert addr_result['p2tr'] == expected_addrs[i]
# Use listaddresses with start and limit parameters to verify the addresses were generated
addrs = l1.rpc.listaddresses(start=1, limit=10)
assert len(addrs['addresses']) == 10, f"Expected 10 addresses, got {len(addrs['addresses'])}"
# Verify that listaddresses returns the correct addresses and key indices
for i, addr_info in enumerate(addrs['addresses']):
assert addr_info['keyidx'] == i + 1, f"Expected keyidx {i + 1}, got {addr_info['keyidx']}"
# BIP86 addresses should have a p2tr field with the correct address
assert 'p2tr' in addr_info, f"BIP86 address should have p2tr field, got: {addr_info}"
assert addr_info['p2tr'] == expected_addrs[i], f"Address mismatch at index {i + 1}: expected {expected_addrs[i]}, got {addr_info['p2tr']}"
# BIP86 addresses should NOT have a bech32 field (they're P2TR only)
assert 'bech32' not in addr_info, f"BIP86 address should not have bech32 field, got: {addr_info}"
@unittest.skipIf(TEST_NETWORK != 'regtest', "BIP86 tests are regtest-specific")
def test_bip86_deterministic_addresses(node_factory):
"""Test that BIP86 addresses are deterministic and unique"""
# Create two nodes with the same mnemonic
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
l1 = setup_bip86_node(node_factory, mnemonic)
l2 = setup_bip86_node(node_factory, mnemonic)
# Generate addresses with the same index
addr1_0 = l1.rpc.newaddr('p2tr')['p2tr']
addr2_0 = l2.rpc.newaddr('p2tr')['p2tr']
addr1_1 = l1.rpc.newaddr('p2tr')['p2tr']
addr2_1 = l2.rpc.newaddr('p2tr')['p2tr']
# Addresses should be identical for the same index
assert addr1_0 == addr2_0, f"Addresses for index 0 don't match: {addr1_0} != {addr2_0}"
assert addr1_1 == addr2_1, f"Addresses for index 1 don't match: {addr1_1} != {addr2_1}"
# Addresses should be different for different indices
assert addr1_0 != addr1_1, f"Addresses for different indices should be different"
@unittest.skipIf(TEST_NETWORK != 'regtest', "BIP86 tests are regtest-specific")
def test_bip86_vs_regular_p2tr(node_factory):
"""Test that BIP86 addresses are different from regular P2TR addresses"""
l1 = setup_bip86_node(node_factory)
# Generate addresses of both types
bip86_addr = l1.rpc.newaddr('p2tr')['p2tr']
p2tr_addr = l1.rpc.newaddr('p2tr')['p2tr']
# They should be different
assert bip86_addr != p2tr_addr, "BIP86 and regular P2TR addresses should be different"
# Both should be valid Taproot addresses (start with bcrt1p for regtest)
assert bip86_addr.startswith('bcrt1p')
assert p2tr_addr.startswith('bcrt1p')
@unittest.skipIf(TEST_NETWORK != 'regtest', "BIP86 tests are regtest-specific")
def test_bip86_full_bitcoin_integration(node_factory, bitcoind):
"""Test full Bitcoin integration: generate addresses, receive funds, list outputs"""
l1 = setup_bip86_node(node_factory)
# Generate a BIP86 address
bip86_addr = l1.rpc.newaddr('p2tr')['p2tr']
# Send funds to the BIP86 address
amount = 1000000 # 0.01 BTC
bitcoind.rpc.sendtoaddress(bip86_addr, amount / 10**8)
# Mine a block to confirm the transaction
bitcoind.generate_block(1)
# Wait for the node to see the transaction
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) > 0)
# Check that the funds are visible
funds = l1.rpc.listfunds()
bip86_outputs = [out for out in funds['outputs'] if out['address'] == bip86_addr]
assert len(bip86_outputs) == 1, f"Expected 1 output, got {len(bip86_outputs)}"
assert bip86_outputs[0]['amount_msat'] == amount * 1000, f"Amount mismatch: {bip86_outputs[0]['amount_msat']} != {amount * 1000}"
assert bip86_outputs[0]['status'] == 'confirmed'
# Test withdrawal from BIP86 address
# Use bitcoind to generate withdrawal address since this node only supports BIP86
withdraw_addr = bitcoind.rpc.getnewaddress()
withdraw_amount = 500000 # 0.005 BTC
l1.rpc.withdraw(withdraw_addr, withdraw_amount)
# Mine another block
bitcoind.generate_block(1)
# Check that the withdrawal worked
wait_for(lambda: len([out for out in l1.rpc.listfunds()['outputs'] if out['address'] == bip86_addr and out['status'] == 'confirmed']) == 0)
@unittest.skipIf(TEST_NETWORK != 'regtest', "BIP86 tests are regtest-specific")
def test_bip86_mnemonic_recovery(node_factory, bitcoind):
"""Test that funds can be recovered using the same mnemonic in a new wallet"""
# Use a known mnemonic for predictable recovery
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
# Create first node and fund it
l1 = setup_bip86_node(node_factory, mnemonic)
bip86_addr = l1.rpc.newaddr('p2tr')['p2tr']
# Send funds
amount = 1000000 # 0.01 BTC
bitcoind.rpc.sendtoaddress(bip86_addr, amount / 10**8)
bitcoind.generate_block(1)
# Wait for funds to be visible
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) > 0)
# Create a second node with the same mnemonic
l2 = setup_bip86_node(node_factory, mnemonic)
# Wait for it to sync and see the funds
wait_for(lambda: len(l2.rpc.listfunds()['outputs']) > 0)
# Check that the second node can see the same funds
funds2 = l2.rpc.listfunds()
bip86_outputs2 = [out for out in funds2['outputs'] if out['address'] == bip86_addr]
assert len(bip86_outputs2) == 1, f"Expected 1 output in recovered wallet, got {len(bip86_outputs2)}"
assert bip86_outputs2[0]['amount_msat'] == amount * 1000, f"Amount mismatch in recovered wallet: {bip86_outputs2[0]['amount_msat']} != {amount * 1000}"
@unittest.skipIf(TEST_NETWORK != 'regtest', "BIP86 tests are regtest-specific")
def test_bip86_index_boundaries(node_factory):
"""Test BIP86 behavior at index boundaries"""
l1 = setup_bip86_node(node_factory)
# Test that we can generate multiple addresses in sequence
# This tests the internal index management
addresses = []
for i in range(5):
addr = l1.rpc.newaddr('p2tr')['p2tr']
addresses.append(addr)
# Each address should be unique
assert addr not in addresses[:-1], f"Duplicate address generated: {addr}"
# Test that addresses are deterministic - same node should generate same sequence
l2 = setup_bip86_node(node_factory) # Same mnemonic
addresses2 = []
for i in range(5):
addr = l2.rpc.newaddr('p2tr')['p2tr']
addresses2.append(addr)
# Should generate the same addresses in the same order
assert addresses == addresses2, "BIP86 addresses not deterministic across nodes with same mnemonic"
# Test generating a large number of addresses to check for any overflow issues
# Generate 100 more addresses to test higher indices
for i in range(100):
addr = l1.rpc.newaddr('p2tr')['p2tr']
assert addr.startswith('bcrt1p'), f"Invalid BIP86 address format: {addr}"
assert len(addr) > 50, f"BIP86 address too short: {addr}"
@unittest.skipIf(TEST_NETWORK != 'regtest', "BIP86 tests are regtest-specific")
def test_bip86_psbt_integration(node_factory, bitcoind, chainparams):
"""Test BIP86 addresses in PSBT workflows"""
l1 = setup_bip86_node(node_factory)
# Fund BIP86 address
bip86_addr = l1.rpc.newaddr('p2tr')['p2tr']
amount_sats = 1000000 # 0.01 BTC
# Send funds to the BIP86 address
bitcoind.rpc.sendtoaddress(bip86_addr, amount_sats / 10**8)
bitcoind.generate_block(1)
# Wait for the node to see the transaction
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) > 0)
# Verify the funds are visible and confirmed
funds = l1.rpc.listfunds()
bip86_outputs = [out for out in funds['outputs'] if out['address'] == bip86_addr]
assert len(bip86_outputs) == 1, f"Expected 1 BIP86 output, got {len(bip86_outputs)}"
assert bip86_outputs[0]['amount_msat'] == amount_sats * 1000
assert bip86_outputs[0]['status'] == 'confirmed'
# Create PSBT with BIP86 input
dest_addr = bitcoind.rpc.getnewaddress()
send_amount = 500000 # 0.005 BTC
# Use txprepare to create a PSBT
psbt_result = l1.rpc.txprepare([{dest_addr: send_amount}])
psbt_str = psbt_result['psbt']
# Verify PSBT was created successfully
assert psbt_str is not None and len(psbt_str) > 0, "PSBT creation failed"
# Sign the PSBT
signed_result = l1.rpc.signpsbt(psbt_str)
assert 'signed_psbt' in signed_result, "PSBT signing failed - no signed_psbt returned"
assert len(signed_result['signed_psbt']) > 0, "PSBT signing failed - empty signed_psbt"
# Send the signed PSBT
send_result = l1.rpc.sendpsbt(signed_result['signed_psbt'])
sent_txid = send_result['txid']
# Mine the transaction
bitcoind.generate_block(1)
# Wait for the transaction to be confirmed (blockheight > 0)
wait_for(lambda: len([tx for tx in l1.rpc.listtransactions()['transactions']
if tx['hash'] == sent_txid and tx['blockheight'] > 0]) > 0)
# Verify the transaction exists in the blockchain and is confirmed
transactions = l1.rpc.listtransactions()['transactions']
sent_tx = [tx for tx in transactions if tx['hash'] == sent_txid][0]
assert sent_tx['blockheight'] > 0, "Transaction should be confirmed in a block"
@unittest.skipIf(TEST_NETWORK != 'regtest', "BIP86 tests are regtest-specific")
def test_bip86_address_type_validation(node_factory):
"""Test address type validation for BIP86 addresses"""
l1 = setup_bip86_node(node_factory)
# Test that 'p2tr' automatically uses BIP86 for mnemonic wallets
bip86_addr = l1.rpc.newaddr('p2tr')['p2tr']
# Test that we can list addresses
addrs = l1.rpc.listaddresses()
assert len(addrs['addresses']) >= 1, "No addresses found in listaddresses"
# Verify the address structure
for addr in addrs['addresses']:
assert 'keyidx' in addr
assert isinstance(addr['keyidx'], int)
# We can find our address right?
assert bip86_addr in [a.get('p2tr') for a in addrs['addresses']]
# this test does a 'listtransactions' on a yet unconfirmed channel
def test_fundchannel_listtransaction(node_factory, bitcoind):
l1, l2 = node_factory.get_nodes(2)
l1.fundwallet(10**6)
l1.connect(l2)
txid = l1.rpc.fundchannel(l2.info['id'], 10**5)['txid']
# next call warned about SQL Accessing a null column
# and crashed the daemon for accessing random memory or null
txs = l1.rpc.listtransactions()['transactions']
tx = [t for t in txs if t['hash'] == txid][0]
assert tx['blockheight'] == 0
def test_withdraw_nlocktime(node_factory):
"""
Test that we don't set the nLockTime to 0 for withdrawal and
txprepare transactions.
"""
l1 = node_factory.get_node(1)
l1.fundwallet(10**4)
l1.fundwallet(10**4)
# withdraw
addr = l1.rpc.newaddr("bech32")["bech32"]
tx = l1.rpc.withdraw(addr, 10**3)["tx"]
nlocktime = node_factory.bitcoind.rpc.decoderawtransaction(tx)["locktime"]
tip = node_factory.bitcoind.rpc.getblockcount()
assert nlocktime > 0 and nlocktime <= tip
# txprepare
txid = l1.rpc.txprepare([{addr: 10**3}])["txid"]
tx = l1.rpc.txsend(txid)["tx"]
nlocktime = node_factory.bitcoind.rpc.decoderawtransaction(tx)["locktime"]
tip = node_factory.bitcoind.rpc.getblockcount()
assert nlocktime > 0 and nlocktime <= tip
@unittest.skipIf(VALGRIND, "A big loop is used to check fuzz.")
def test_withdraw_nlocktime_fuzz(node_factory, bitcoind):
"""
Test that we eventually fuzz nLockTime for withdrawal transactions.
"""
l1 = node_factory.get_node(1)
l1.fundwallet(10**8)
for i in range(100):
addr = l1.rpc.newaddr("bech32")["bech32"]
withdraw = l1.rpc.withdraw(addr, 10**3)
bitcoind.generate_block(1)
l1.daemon.wait_for_log('Owning output .* txid {} CONFIRMED'.
format(withdraw["txid"]))
decoded = bitcoind.rpc.decoderawtransaction(withdraw["tx"])
tip = node_factory.bitcoind.rpc.getblockcount()
assert decoded["locktime"] > 0
if decoded["locktime"] < tip:
return
else:
raise Exception("No transaction with fuzzed nLockTime !")
def test_multiwithdraw_simple(node_factory, bitcoind, chainparams):
"""
Test simple multiwithdraw usage.
"""
coin_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')
l1, l2, l3 = node_factory.get_nodes(3, opts=[{'plugin': coin_plugin},
{}, {}])
l1.fundwallet(10**8)
addr2 = l2.rpc.newaddr('bech32')['bech32']
amount2 = Millisatoshi(2222 * 1000)
addr3 = l3.rpc.newaddr('bech32')['bech32']
amount3 = Millisatoshi(3333 * 1000)
# Multiwithdraw!
txid = l1.rpc.multiwithdraw([{addr2: amount2}, {addr3: amount3}])["txid"]
bitcoind.generate_block(1)
sync_blockheight(bitcoind, [l1, l2, l3])
# l2 shoulda gotten money.
funds2 = l2.rpc.listfunds()['outputs']
assert only_one(funds2)["txid"] == txid
assert only_one(funds2)["address"] == addr2
assert only_one(funds2)["status"] == "confirmed"
assert only_one(funds2)["amount_msat"] == amount2
# l3 shoulda gotten money.
funds3 = l3.rpc.listfunds()['outputs']
assert only_one(funds3)["txid"] == txid
assert only_one(funds3)["address"] == addr3
assert only_one(funds3)["status"] == "confirmed"
assert only_one(funds3)["amount_msat"] == amount3
expected_utxos = {
'0': [('wallet', ['deposit'], ['withdrawal'], 'A')],
'A': [('wallet', ['deposit'], None, None),
('external', ['deposit'], None, None),
('external', ['deposit'], None, None)],
}
check_utxos_channel(l1, [], expected_utxos)
@unittest.skipIf(
TEST_NETWORK == 'liquid-regtest',
'Blinded elementsd addresses are not recognized')
def test_repro_4258(node_factory, bitcoind):
"""Reproduces issue #4258, invalid output encoding for txprepare.
"""
l1 = node_factory.get_node()
addr = l1.rpc.newaddr('bech32')['bech32']
bitcoind.rpc.sendtoaddress(addr, 1)
bitcoind.generate_block(1)
wait_for(lambda: l1.rpc.listfunds()['outputs'] != [])
out = l1.rpc.listfunds()['outputs'][0]
addr = bitcoind.rpc.getnewaddress()
# These violate the request schema!
l1.rpc.check_request_schemas = False
# Missing array parentheses for outputs
with pytest.raises(RpcError, match=r"Expected an array of outputs"):
l1.rpc.txprepare(
outputs="{addr}:all".format(addr=addr),
feerate="slow",
minconf=1,
utxos=["{txid}:{output}".format(**out)]
)
# Missing parentheses on the utxos array
with pytest.raises(RpcError, match=r"Could not decode the outpoint array for utxos"):
l1.rpc.txprepare(
outputs=[{addr: "all"}],
feerate="slow",
minconf=1,
utxos="{txid}:{output}".format(**out)
)
l1.rpc.check_request_schemas = True
tx = l1.rpc.txprepare(
outputs=[{addr: "all"}],
feerate="slow",
minconf=1,
utxos=["{txid}:{output}".format(**out)]
)
tx = bitcoind.rpc.decoderawtransaction(tx['unsigned_tx'])
assert(len(tx['vout']) == 1)
o0 = tx['vout'][0]
assert(scriptpubkey_addr(o0['scriptPubKey']) == addr)
assert(len(tx['vin']) == 1)
i0 = tx['vin'][0]
assert([i0['txid'], i0['vout']] == [out['txid'], out['output']])
@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "Uses regtest addresses")
def test_withdraw_bech32m(node_factory, bitcoind):
l1 = node_factory.get_node()
l1.fundwallet(10000000)
# Based on BIP-350, but all changed to valid regtest.
addrs = ("BCRT1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KYGT080",
"bcrt1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qzf4jry",
"bcrt1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k0ylj56",
"BCRT1SW50QT2UWHA",
"bcrt1zw508d6qejxtdg4y5r3zarvaryv2wuatf",
"bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7",
"bcrt1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesyga46z",
"bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqc8gma6")
for addr in addrs:
l1.rpc.withdraw(addr, 10**3)
bitcoind.generate_block(1, wait_for_mempool=1)
print(l1.rpc.listfunds()['outputs'])
wait_for(lambda: [o for o in l1.rpc.listfunds()['outputs'] if o['status'] == 'confirmed' and not o['reserved']] != [])
# Test multiwithdraw
args = []
for addr in addrs:
args += [{addr: 10**3}]
res = l1.rpc.multiwithdraw(args)
# Let's check to make sure outputs are as expected (plus change)
outputs = bitcoind.rpc.decoderawtransaction(res['tx'])["vout"]
assert set([output['scriptPubKey']['address'] for output in outputs]).issuperset([addr.lower() for addr in addrs])
@unittest.skipIf(TEST_NETWORK != 'regtest', "Elements-based schnorr is not yet supported")
def test_p2tr_deposit_withdrawal(node_factory, bitcoind):
# Don't get any funds from previous runs.
# Use BIP86 node to ensure consistent derivation for both P2TR and P2WPKH
l1 = setup_bip86_node(node_factory)
# Can fetch p2tr addresses through 'all' or specifically
deposit_addrs = [l1.rpc.newaddr('all')] * 3
withdrawal_addr = l1.rpc.newaddr('p2tr')
# Add some funds to withdraw - only use P2TR to avoid derivation conflicts
for i in range(6):
if i < 3:
l1.bitcoin.rpc.sendtoaddress(deposit_addrs[i]['p2tr'], 1)
else:
# Create additional P2TR addresses for more inputs
addr = l1.rpc.newaddr('p2tr')
l1.bitcoin.rpc.sendtoaddress(addr['p2tr'], 1)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 6)
# Verify we have P2TR outputs
funds = l1.rpc.listfunds()
for output in funds['outputs']:
assert 'address' in output
l1.rpc.withdraw(withdrawal_addr['p2tr'], 100000000 * 5)
wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 1)
raw_tx = bitcoind.rpc.getrawtransaction(bitcoind.rpc.getrawmempool()[0], 1)
assert len(raw_tx['vin']) == 6
assert len(raw_tx['vout']) == 2
# Change goes to p2tr
for output in raw_tx['vout']:
assert output["scriptPubKey"]["type"] == "witness_v1_taproot"
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listtransactions()['transactions']) == 7)
# Only self-send + change is left
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 2)
# make sure tap derivation is embedded in PSBT output
@unittest.skipIf(TEST_NETWORK != 'regtest', "Elements-based schnorr is not yet supported")
def test_p2tr_deposit_withdrawal_with_bip86(node_factory, bitcoind):
"""Test P2TR deposit and withdrawal with BIP86 derivation (default for mnemonic nodes)"""
# Set up a node with BIP86 support (mnemonic-based HSM secret)
l1 = setup_bip86_node(node_factory)
# Generate a BIP86 P2TR address for deposit
deposit_addr = l1.rpc.newaddr('p2tr')
# Send some funds to the P2TR address (uses BIP86 for mnemonic wallets)
l1.bitcoin.rpc.sendtoaddress(deposit_addr['p2tr'], 1)
bitcoind.generate_block(1)
# Wait for the deposit to be visible
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1)
# Check that we have the deposit
funds = l1.rpc.listfunds()
assert len(funds['outputs']) == 1
assert funds['outputs'][0]['amount_msat'] == 100000000000 # 1 BTC in msat
# Generate another P2TR address for withdrawal (uses BIP86 for mnemonic wallets)
withdrawal_addr = l1.rpc.newaddr('p2tr')
# Withdraw to the new P2TR address
l1.rpc.withdraw(withdrawal_addr['p2tr'], 50000000) # 0.5 BTC
wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 1)
# Check the withdrawal transaction
raw_tx = bitcoind.rpc.getrawtransaction(bitcoind.rpc.getrawmempool()[0], 1)
assert len(raw_tx['vin']) == 1 # Should use our 1 BTC input
assert len(raw_tx['vout']) == 2 # Withdrawal output + change
# Both outputs should be P2TR (BIP86)
for output in raw_tx['vout']:
assert output["scriptPubKey"]["type"] == "witness_v1_taproot"
bitcoind.generate_block(1)
# After withdrawal, we should have 2 outputs: the withdrawal destination + change
# Both belong to the same node since we withdrew to our own BIP86 address
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 2)
funds = l1.rpc.listfunds()
# Check that we have exactly the addresses we expect
fund_addresses = [output['address'] for output in funds['outputs']]
assert withdrawal_addr['p2tr'] in fund_addresses, f"Withdrawal address {withdrawal_addr['p2tr']} not found in {fund_addresses}"
# Find the withdrawal and change outputs
withdrawal_output = next(output for output in funds['outputs'] if output['address'] == withdrawal_addr['p2tr'])
change_output = next(output for output in funds['outputs'] if output['address'] != withdrawal_addr['p2tr'])
# Verify amounts
assert withdrawal_output['amount_msat'] == 50000000000 # Exactly 0.5 BTC
assert change_output['amount_msat'] < 50000000000 # Less than 0.5 BTC due to fees
assert change_output['amount_msat'] > 49000000000 # But more than 0.49 BTC
# Verify total is close to original 1 BTC minus fees
total_amount = sum(output['amount_msat'] for output in funds['outputs'])
assert total_amount < 100000000000 # Less than 1 BTC due to fees
assert total_amount > 99000000000 # But more than 0.99 BTC
@unittest.skipIf(TEST_NETWORK != 'regtest', "Address is network specific")
def test_upgradewallet(node_factory, bitcoind):
# Make sure bitcoind doesn't think it's going backwards
bitcoind.generate_block(104)
l1 = node_factory.get_node()
# Write the data/p2sh_wallet_hsm_secret to the hsm_path,
# so node can spend funds at p2sh_wrapped_addr
p2sh_wrapped_addr = '2N2V4ee2vMkiXe5FSkRqFjQhiS9hKqNytv3'
# No funds in wallet, upgrading does nothing
upgrade = l1.rpc.upgradewallet()
assert upgrade['upgraded_outs'] == 0
l1.fundwallet(10000000, addrtype="bech32")
# Funds are in wallet but they're already native segwit
upgrade = l1.rpc.upgradewallet()
assert upgrade['upgraded_outs'] == 0
# Send funds to wallet-compatible p2sh-segwit funds
txid = bitcoind.rpc.sendtoaddress(p2sh_wrapped_addr, 20000000 / 10 ** 8)
bitcoind.generate_block(1)
l1.daemon.wait_for_log('Owning output .* txid {} CONFIRMED'.format(txid))
upgrade = l1.rpc.upgradewallet()
assert upgrade['upgraded_outs'] == 1
assert bitcoind.rpc.getmempoolinfo()['size'] == 1
# Should be reserved!
res_funds = only_one([out for out in l1.rpc.listfunds()['outputs'] if out['reserved']])
assert 'redeemscript' in res_funds
# Running it again should be no-op because reservedok is false
upgrade = l1.rpc.upgradewallet()
assert upgrade['upgraded_outs'] == 0
# Doing it with 'reserved ok' should have 1
# We use a big feerate so we can get over the RBF hump
upgrade = l1.rpc.upgradewallet(feerate="urgent", reservedok=True)
assert upgrade['upgraded_outs'] == 1
assert bitcoind.rpc.getmempoolinfo()['size'] == 1
# Mine it, nothing to upgrade
l1.bitcoin.generate_block(1)
sync_blockheight(l1.bitcoin, [l1])
upgrade = l1.rpc.upgradewallet(feerate="urgent", reservedok=True)
assert upgrade['upgraded_outs'] == 0
def test_hsmtool_getnodeid(node_factory):
l1 = node_factory.get_node()
cmd_line = ["tools/hsmtool", "getnodeid", os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")]
out = subprocess.check_output(cmd_line).decode('utf-8').strip()
assert out == l1.info['id']
@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db")
@unittest.skipIf(TEST_NETWORK != 'regtest', "elementsd doesn't use p2tr anyway")
def test_onchain_missing_no_p2tr_migrate(node_factory, bitcoind):
"""l1 and l2's db is from test_closing.py::test_onchain_p2tr_missed_txs before the fix"""
blocks = ['0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f121ed0ffdd5a197230c7b58dd7512177ba827afeca6c2c82ecda3519566ac83d3d45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025100ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002056e8e4767b39e3f2a4e98139da05354cc1430f6cebc315cdb3258f26db8cb67d29444f7b2b6f5c1fcf13acbbc101d29f0840f373cd16b85f1f92ed670614fbfa3e45e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025200ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020a88aa9d26fa6e14d9019e8942e201ba2e4435d29fca4e30e84aaeda35b85f13c54d0cd7ee7880080019227edc9782d42d719794f995419197c254417c740abfa3e45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025300ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020d98753686d7d90f18a7fed6eef810e367dd4eb5d9c950d32b2735a151aa89e27ec5ca82ec5538a841fe634fc178f73247db5e17adb48913e55d33b1155b9df753f45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025400ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ec249c91bf3f5cb46f48c14bee378bebf1591fa5ed15809c164170754a8f1e2daac112a6d9cd3e9c22a9b49142fc8314a7dffb8ace5bc9d7a273f9aba5ac18dd3f45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025500ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020c257c44ac8551b3b238ea57d21944f4614ce6956065074f2f473a6df9718887ef379a56ccc20580e4774874c3e1b1120a1adae13a47fdc6004946f404743c6113f45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025600ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020e4e5a33bd5e84f1077de3843d2e480f7026e363872df300d5ce335208eea81537604d1903f67ddeeed3e6d9dfe107124b79d655d9c65c47a8baf97cee805d6793f45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025700ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000208a7461c453c09c8d3110e9b5dd3c5f438e3c760c9513655fe49fdf1edd4a4134f28bd7fc2be0d0719cbf109ff4aaf2317deb44c58a4f9131ffd87e06b150e5de4045e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025800ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000204f54cb2bc317899e636a616175936910336da0f733ed0165dd6084e61210d075a1cb573f8c475e11e0abbeb593575ca8d35a8b902f36d725d93382041bf8f2434045e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025900ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020dee1077d156feb2dac8be750ec1f6c0c8cc6148e98df6bf28e1f1d87369da70bc0bbbd7b242da54cabd90b3a9a10d86974615819d45fa3cb8aa97cdb488cc4ea4045e767ffff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025a00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002097cec18a4ef852e3d32e46ff8af809518e075dc050bc6d95ccf2c4ffee53dd51f429948b42f534e8adc8f9ce9ab5dff9d84b54c58085f78d7646ec99c9d8a5f94045e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025b00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000202417a4c440947c1081326d24f4bfcd6eb4545759262603baf94a23befed47728756afebf5b153e8e609348dfa70fd288b0f981e50b37e08c51c9d5df6b53d4574045e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025c00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002045a0ff48b45af0766329cecd5c07a912dbcdcd547facb15f7acb3af5241e011b3aa7271b58360c7ccdbfc80f01de88b822924701fb4b21315d6a20ad212c6b234045e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025d00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ac1719324f9032c419aa4a7d993bd9f1cf34e5fcfa3aeefc1554cf2a37cd5c2771b97403a26c5b147e04544bf0cdde758dfbb0634a05a17c00324a3ccd6619904145e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025e00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020622c40d273474af2ee1afbc67777a1212b07132e07f7e89e462687131931e4601d42312ac50a0cde27393ffa2071d74f8995c6a4873a14cf968ccf7e8f654c3b4145e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025f00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020f8d147ccbbec397afd787241fab2b4ebde4fabf72c808b7cafd87fd54a82ac71fd4d27ac7efcdac809fa30b7a8b4801b9fb69a11a3511ffc78aeebd2dbd8c6d84145e767ffff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff026000ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020003dbb1e275d32254e34837b4c9b743be7d71f84779e4519a86ecf46fa6a5d506650b1d56c1a49a68206669ddfc0bafd899097dd4d1b8b31f6aa56afc7143c574145e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011100ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020e24ee6f5ca1e5bf5ddf9b08d9c63c51df30308debc631243e7244560ddbee524d781b8ec7c6585ea570435c1e8d61cb064db79e0e37a85db01517db8bc3db9534145e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011200ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ee545b3b41992c9194459ea63cbca7c6a3079d9d160760856344390ebd76394546b9a9f1bc1060a127cfd6ee2fb00dd18e190d2fcf5476d229b026fe6ebd2a964145e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011300ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ce65af7fae3cc1191ba3520ce19e793d8747d6f70df4e29bbfcd830882c2e45688dd607cf34bd52a1f37837304b1345e5f9bf1ad901db0ff527d4fdabc7bb4f74245e767ffff7f200600000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011400ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020f1f2036de5c385f42e6c3da3b54cc94ce81527939cc1ec243387a3939d9fed7ef5e2d409cf025faf43dd43f120629d62a1f9ae8ef655dbb22fd2916ae51d32db4245e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011500ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002027f7dadd90d2f5cc5ae451e12702f37e2b2dbc5aa8f01af6399183f74cab8663901757e211691030c4fde536d26cb992a0c3c149822273cb2630cd20922ff2694245e767ffff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011600ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002072253009326feaa2cf0a87af69a071e0fdbdcb052f82afbc0708768d7e42e502957696fea9a86ad913c32972ee7e76047678d83fba95ff78e920954850d293854245e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011700ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020805c0de280de48b539ef2e656d013247c24a2f9e72b45ec0d35b9b0a67712730990b38c53dbe4b000aaf0f8fde41739e488a0651af1eafd0c8c1154462426e474245e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011800ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000205580a32fa092f795359ef4fd8cf5683135ab94d338906ac2ca30d83b9224885cb33ac2bef57fe7b7b62bcd567147749870481d35732426bb70b3d3e09cb03d654245e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011900ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002043b881c5077dd5c49c80cf652ab96265f045fb8a4160baa1a1959456f1365674f5b5545b6a08283699c2f648e8822b4bfa151cc92e85f23096091bb217bec00c4345e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011a00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000202cbe3c8d54a8acbd395d3f34f794c608c594f2933187dfab5798ae884976ba126639f0d15b87f22b52b22a2db662dc18073d73278a9f03dcafa11deaaea393d64345e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011b00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002052d8f545cd0cdb4098917c59f464bb06169f62100715c1f0e20261cc205a0721dcfa2b9623d8506335f6cfad8172e0a585f3d1499ff48ab09c63b4d8da17dc4e4345e767ffff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011c00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000203777dc93b80490c7d73c223d32b6f8e355bf4e1bc7682b126815dd6cf68c7402fc9ad8f5f17705057e81bbd26ee244b9919a66e493ba6d1f7b16a7ae742ef7464345e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011d00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002029d7ce4e03327a016d967b82832c1821a451b6c32daaae8063ddcfdcbe3c396bb97d9802063e424e198b252f1b4e3ecb45f15425ed7b96082394a5a8ba37e16f4345e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011e00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000203223f332a0ca3da6d9f16400f2e301d043b028453b0be9d046ea1a79bd787919affb4d62e0ce40708279c7c89b450fa5edbbf036add302889c7746c52393bbfb4345e767ffff7f200800000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03011f00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020d740d4b227990a1ffd287929dc6d0c05b9a1da43f2c08bac3dc415921afcf86a52811ce56d03b94582212ef8338e98171aeffd0ca280898648031724074ef3654445e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012000ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ec7e7181e747198b39f3c6b031dace6cd4994a036082dd3e1989ecea3d01b005f6e35a9d2c2c9586dfeeb20a894ccc2231040e1c8576d9bd74d1ef4941ea66444445e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012100ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000207d83089eb2a775b1c8dafc0af8118a20e0ea1d78f099522e2512c5355310fb5969c1daa9ed040c0ab3fe68ad802e637e69ffc777a8e0d77393a6049086e9bbe14445e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012200ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000208c22dc5cdd4ac9dee3a7a1c20a2257c6fcea405f4eac404f7727d2848332da2ecb7f4811333231279dc6db9103df46e2452e1b995cf984240c3f157aabb581ee4445e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012300ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000201409a49dc6c1dcb74e1593d0df46e3c945487ad1c3e2cb3b3e72ba7bfe615d222be670b9cefbaac48b50e034dccd065606d5f4f1658dd4bcd8893e51586cc3414445e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012400ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000203778a6471cd5892165f4ab9a06ec218e2e1ce24b9a705522b1bf6c2e20d5e9336ef2c5e22236835b35824f0a65b948e67e8f7acf612ae0c70a53e1b34da9357c4445e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012500ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000202ce848e935faeea98f38578fd04cae046277a2c2b8ce084809d0b38a22ca7f2745c08ea35070ca4b721625c70dbe78b9b0e5f981e9651450a27adbb2a43e5bad4545e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012600ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000205c086ca4598faea39c90dee6a511201141351df9b30a8535d2c3505a18294d38221e9d2b65f07b51ea8ed1a7d033f6a3b22f9ff5a1c8a866d42b9a2a03b0cf3d4545e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012700ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000202cbe285d2d66722078a3fd6a51db34dc9e33246bbf76c5a7d04d2208998ba6225d755a5811c13a18decbba116c392422dad2ba32d51581c0d0a4dd891a3081ad4545e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012800ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020273b8d89d4c162df351089915bde50615b8e914fc158a97c0e38783e674ff36f1823c18340823c3a6b7f19ba786029dfb53aeca6cbf01235d7151de95fd5d1844545e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012900ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ccf17cebf95ff2f44c4f64fb514614494ee15630eb87c279825462195e9ca1294270f349315dab92f1f2311297c70e5353c635d368ef1e8ce37bfe64b15e6ce34545e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012a00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020b1b0c0ac7b9e1d2a3cbf43ddf6e0a34968735fac65a95a9db98e9211d5b1820408619e4c9c71540ad6b952d4c5224602fa45af3fec4bc3aaaf41661b09bd62e54545e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012b00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002040ef97fd829af29a9e0a2c4ec3327b71da31a58bae4a8b2c3b0b74426dd83022823f16e2ada411bbb6dc3ad9335572d5ce7fe0ced9e4c4b485463c4931073e494645e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012c00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020523d20de744cab12a12ac8c71bc930e65c88e15db6e0c88f9f68e4e97473d578e60e8fb9e3c50f5dbd70a9113fdf50ede58b0f25c8db3bcd6b0d7befb49d2ce24645e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012d00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000205457d8f0682df35420c76853489f9b2d092ffe4a73bef96033216028138a0272a911ca2b3b45841c1eaac2df1e7af13c5b89fd2892b1594e24d39003a4027ac34645e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012e00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020a93b1446dad94fad245046d7777541a31cf75b3e1dc565c5899130b1627a6a65192c114e3cf1e91c337475804434f949a931bf1b55bea2dd5f4b698c7b92f84e4645e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03012f00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020008db5f78bf19eb494df3bc95b4809f368d9f8c157affc76b9612e30061adb54de1ad09146535a6b0ef832eb1d79bfbb13784016c18258356ef67dd249574d3f4645e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013000ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020bf876a730f6b15c3f766aaf96312efa1208778487b8dd4a3ed28d331ff6ff369192df6ed3a7d289b4c37d6fa6c2b63dbe1f4b2e4562e263e577a15f7d69278744645e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013100ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002039ba6324e15c56ca6590989bb1e6d7426a5c8cc770e967015bd01dcb4d2a95181132dfafa4132f447f63fb999726fea98767e887cf98a259ef7ea7fd07dbb64e4745e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013200ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020cd1dce463cd6b0fa6ba3500d380a1327200fae56efec37a2c5e435ff244a65443f1024e9b6ce06b809df3ec46e4a834e80e934190d590b62b6999378ca93ef0e4745e767ffff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013300ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020a44cc61dcaf4738c637bf540680665364b71d1c71fe09089b9c8ef8cf68c9e2db0749b3331bfa43b0d458f46f01204781a44c4830f7a08c7c050f87c67ee441c4745e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013400ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020e61836b534a239642a830d7c029b85e1b1b754da2ce559e152d3ef2b60fe346b356fb9c2e9f571c39cbe561969058e889484262080fce71d7c6dc9851f91a41c4745e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013500ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002068524ed04b0180e71d83fe6ae5efeb141e622de706e2b413b1862bb6df8fad66eef99ff3f436f643c8596cffd52f70254018b21839e10a4819e73d78a9fee8104745e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013600ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000204d7c2f69eee5030e3e62cb3c4c498a8b804b5b048c7c460fee7421867bf6c5565f2b1ef4127dc4ad3f139362eefe24b7a28b00a446c093281920a0833b1b5f7e4745e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013700ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000203fb86b9c03e204833f4f2fa5d0bead1004ba4b0ebce1d41ec9a32ed76eefd54a3ce9ed4547ff51cb18c4f1f1e81d5275157b86852ca23e567aa970417c04731b4845e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013800ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002087fba8d6923025aa62a03e0181231ae3b378fef2b91c705f74a28f66e0651255f13e4a7f7c478a7721d137ad8dbf356ef57d119e857123939a4506aed215d16a4845e767ffff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013900ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000208358beaeb2fc8e9275f4004e67a87bca7c5915dcda0dcafca2f8fe9f6a90cd6b07b5e8d76cf2bbdab39b4706587b76d2f2d0068a63317a6abc1f2538b1db3d4f4845e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013a00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000209bcaa71b05385e5a0768fcddda0987e04751dddd5fa413b2be83f608f2f122610f94d40dc90ccca8e76fda5b7d0d49b4ed77fa8af29342a4a92e0065a531d2374845e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013b00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020cce44e08c9f2ceb0dc8c3243a9798b7d5e56177dab496beafc6c76cd179f1e69665c029fb980568403dd86b5d7ec77862abe828d0fb5ef02f28b5b4d373d2b054845e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013c00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000202982c819e34a06d753f840e23f11031170bab285b319e859a6414e163f081b30afa2940431691af1258a548bd71477cf66c01c884c110da0d7e1c175568e41974845e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013d00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020d51d963aac276f16eda2ac14c9d229d9cc7f21edc5d6c84d92db757eb9cb491c6ee7239b57fc1ea359c2fd3c28292976bd095a6ac0e4540c7eb70c46de8339fb4945e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013e00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002088a273f1306813096a9870330f1a0db64db14ca501249d8d49639ac1a593fc4c804815be3e4114c5fffa96aad26c75cbedcbc854c8bac1ed3b3f9c16b13968e24945e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03013f00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020b47c3ad1c5b0669701eeb6d04181e08f9a910d06b98153f6daa4eed4d480160a3d9bed639fdf277a9a768f5c3c71b220281bb24d77f7cf42ba38e2d432f641e54945e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014000ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020a0ae01d6a9bccd9d4b014efa2808d266eeec9d76b597985711ab2f2d7300873b2e039380332fe0ee2922cfc85934546ca0487943e7bc3c1d053294a4e28d23d24945e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014100ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020b513e6b905f59ce167741ad3483c7a659bb66c28f0e893f61be48e7f1d0aa464cb52a1d2d0b7feb43e3707ab01883ecde8de9cd799d7b4554dfed6603e5a06784945e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014200ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020cf7c32f21b0f8b5d1705027fce8a26e2c73233cc47cd149be4d5c87bdeec764eb03f8d3382d4dc3debe75cbda087b24367d7bbf843acd1759fd5f203d443f1574945e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014300ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000201366a9defec3e481f7d6b791bbec45586c8e375efa68a4d771ed0b9e9fb7a21b353d154182ddfaa565e6ca531324acc621cb9ee3b2286a075ed5921b02e5c03c4a45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014400ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020e227ff11ff22192eb7b7e4e45e7701427af5e6d4d6fce1469cc31a0f702829044206ac1718f4bc2bf712402b4b73317a4dc81d193ffdaeae84514a690cbedb834a45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014500ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020d49f5867985e23f97862c1335391d7070a7cd263565c9059a75d0a1ed33c4775731604dd425ce7ebec709b1c55ca1e28733ec589f024893ee02915964996e19c4a45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014600ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000200795c11d7aa402ab08599130953256038cba99027e992984c03f6eeb93532e6122675f9f5445b99caa2412d7a48d773813230fc5237a73851a7595e5eea813d64a45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014700ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000209461da337678c07e079d6bb700cd77ffa9ab6a17f983027c81f8fcb6120f8d654561d571492e2399e2cd0042ab552b33354c62642b489190d12d7a4e7c79c5414a45e767ffff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014800ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000204f28c8b5f7f75a74ee2c5c21cacaf34c4710e5d50104664d0ad6b21e6e90b570398c63d309b5fd1854ad095f2926a66692cbb0b75985648240feed65b9076e4f4a45e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014900ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020b34f81a7e63a3d553f7fcc75651bff5e399746363caf7ba6a515e6e81fe93c7309b05d9b40df1c48e5c0b4aa8093dcb809b8b9e95b695d6d00e4011744455e9e4b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014a00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002009239cc6231ba824fc9c84d25ff93ce4a1ade148eb58120bb6fe8e9694d948774d0507e1a56dd3cf62edfcd362a90369f9b553a4052bc2820d3c15d13bd964d24b45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014b00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020e1a0dc94ff484b1350a1fc45746111bfed0ca796b59ce537c847da1376183b4b3eb2e6938000f23a4b353692532deab714a9a905968b8e45c8f29775974766f34b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014c00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020f838d5ebd5ecc3d149b449607e93eda3afb9ca40e5efffb275c7cbf3255bc53d097d2824834a9dd2bc4906f1c3abf0ebf62548119ec8c63b709ce6f86531121e4b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014d00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000203e70699fb54df9b478ef032f7c7910da732953cf9c4b46a10882834b15c147485124cbf07e1a19ef7db7c32faa907d2cc14f8718e3b2492274d8189aeebaa86d4b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014e00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ce17449ebc7d3ed89635861fbadf771177fc6e9f89fb8568fa5391902a6d3835d493b31b5885c4cdcf1bf0323540e51efcb98b62ef2df2a8e2c98fb4c515fe0c4b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03014f00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020e6dfcc5f0d0a99cdbcfd9159d38d6f22ecd15a4d58fb15eba0a6ddf4461d22531e907738f76c083edb201898dceb5450c00d932d1b4f6df9ccd20d4aa06bdcd94c45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015000ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000209d1276b33097ca1f167d668e58eb02f808ed5ede95ae0646826517df4db91b11a21c9e516a0340e59cd385f817e3c25669751463e060b944098eea334e2c5edc4c45e767ffff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015100ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002064ab992bf9bd6b892dc119c31bf8783a7e9f19762afbae9430e7bb12a89ffd5c61b085a1dd14d757a4a16e4d1913f2874db46586875e90ccb98037ef333d7c424c45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015200ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000207a7763f9cb7a778a222acc4ce585f9b2f5f783c097ebec310702c87ef37ecf121b0d55802404b5ed763b48418273e9a3afb894f0e35f95681278956f9c3aa4af4c45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015300ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020c94c950a983a47b4a710edbf815dc4ccf0b087734a0f26744f9a097a3a0cc96872eee5cb323b070ee052ed2b2dfa84614017e58d48a75f379cdd04487eab0a554c45e767ffff7f200500000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015400ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002089255a3e8e9b7e76b5066a845ee682e5ed4241d51aa8788ec038a52f78760644abbe623aff5dea4eea5c5e6855f0d00d25573fb30d72b4b8767fa7f555f3d0f44c45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015500ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ae3ddb931d73ed22b355c14f0c3349f7ab6c31fa56c0b6050b0c2717bfee7d374433c0b6cb0b30debfa8e3fef9328a43ef6f243d3c92b2a985a054770464abdb4d45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015600ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020884d1649346a457132f5e752b2bd005eb83573eecf08bff4c0927519d9570d406cc13d704d9dea7f5c8ecb624e3f24fc0a2e75e7c6dbaab08d7f3c5ce315fe394d45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015700ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002031b450c77ef3e50b3c356999d8e0732f053c2a470cfd20e016b067eab7ef916a0417caea9b477a95046067fda479ae367e63c3b521ef9243e3cad3e7f5606ac04d45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015800ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020067c78e818d260aa51d34200106db04921a5c8654dfe11828a3e9cdd618da21896424aaee42438322942263255e7121bcda1e783b175af976f84b3d674172b124d45e767ffff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015900ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020d237a92f2a860a1342b7c37c30fd05be47ce6c1344ddc295d8a12dfea21f3447cad7c3cb50788dfb9eecc6f316efb46739ce4ba4703997a3c54c75cc7f0dcff84d45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015a00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002032ceebc14e86191162c4d8ec3fcacd9d6c1466a93aa5092c8f4b1a126db70b260c2932b748d4548575c282f3c4b8d956f7e65fa493a2ec17aa09d943de964f094d45e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015b00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020807fb3f557726ae5a8e246b06e6e3364b9460c2c31dbc37e2ca87763f176fd6463933d80fe1db063acc4a89a154acc892c6f5c13b3c4452f4d5ce51ffac793004e45e767ffff7f200600000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015c00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002035201324cc84078292547aa0a1e0ff7f8727074feff5e319b2dfbb2fe885c01a1d3d9d3494e4652489e3f18a76a52cd492b32f8d18f6cab4fdbd6f24a1b9e3254e45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015d00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002025a80bb6b4aa2c98c18aa828586d51eceb677b8975029ad477a6cf05d23a7527cce8fd4e16c15fe2e80f4af5c576fcaeac1503979e7a51eff66ff2bfe53efa844e45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015e00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ed3b675338a6ba59799594e985173060205e73d7b099e30cb78d1d3690a2c03fdc713a241d2c56875a837de27262273728110ca55174eb09f88203a845b7f8104e45e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03015f00ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ea480607fa29df4d8ea8f8e788bb276159a8a61ba86ba14af483d651548e470156721b4da097ecb6ebc2303127ac2fdfe3eb1c00eaecef6eec40e498b17e00954e45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016000ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020fe360ca0152fecaacd045ca85a2bf08869cffdec799fdf64bea325b6532ad64227ce8d66be0c73025af9cfd24fc7245fad97357f8716eb38e8335f3a4e54c5c34e45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016100ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ec427f7fb74c568fe04fdb414ffbf8b3c8b1e8c206a36024b012b057da73da690e62a987c35cfae251ac11f3d12957681b9a867bcd0bb0702d459a867ecd1d824f45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016200ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020245772e5852eb6bd20111392df098739d32b5b8a50377019787ece224254635563c6200669e2682bed224fd2b0bc68bf644af5098341a6ededf58a601d4fac204f45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016300ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002047bd456d0d95607b24b0e927b639c8b7775803dedd7cdeb4b4a7dc8070179d271989230bf9b5397de410a9a294f7540c716753b74a7def0d00db84fef128af474f45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016400ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020de64fd4e9217b8456df1420b3effe314f40205800426a04070b71eabe9b2b3248290185184f3c2d9a243a1daddb4f17055a2749dbc0ab22cae7bfd791479f6034f45e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016500ffffffff0200f2052a010000001600140544f594aa1610ff55e0797eeb4eb15cd07601cf0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000205ed61888928b352319efb1d85d636291a19ae45c55c4bd46f2a0f3d7a7512e71ad10099b91166668d725b5799ce29611347bf3118153be04971656fd3f0d35394f45e767ffff7f200100000003020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016600ffffffff021af3052a010000001600146a66b1bffe8368689cd695771ada2d78585c45330000000000000000266a24aa21a9edf9d7107703e8bc34556aee02bdc2c3b8eeea905deabf4e46a99528a5a733850d012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101121ed0ffdd5a197230c7b58dd7512177ba827afeca6c2c82ecda3519566ac83d0000000000fdffffff02f36ce72901000000160014982049ddf15138edeec1e5683c033ff8b63f7cc680841e000000000016001401fad90abcd66697e2592164722de4a95ebee16502473044022062107661a713637320a916de5a7eb7537706654f52188d286686741e7c9cf85a022063f0be207245a0a2a180c9ec2e2929dbb306ac3f115cef35d8f0148dbd0abbd60121032d8f3acac06981164dd33a38b5516f91520533c7746ab2f99279d48d1473d80e6500000002000000000101aa8b92c30cc63741018132abc34fc643c95ff6979390075c8670239c8da257410000000000fdffffff02e6e7c82901000000160014fad20f85fde37b24657a810c6730102e72ca699980841e000000000016001427213e2217b4f56bd19b6c8393dc9f61be6912330247304402201765295e2b53c15f50dddc82138e3190a98e368292fdc3d39fa77f2ef249a3820220253efe5c741e4531d9ca0414a65187965e2aca857ee152f331aac8893520c8a2012102529a967625c45f08195978925fa5b101d75db08a009f939e97d52ce9c7b74d2263000000', '00000020f5367ff88080f00f5ca8ba81ceeb5fddb974aeeb0eaf97c65f6fea352782b073a640a845f26e8cfd1e6d9e18a504cd871da0090f38b04066c96d30f194425f334f45e767ffff7f200000000003020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016700ffffffff027e18062a01000000160014a50a98c97a28195e9cab985d29218613ac9253770000000000000000266a24aa21a9eded976f675d4eaaec239ab7107adcd76964881d7c48cec52a265252abd224db9f012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101aa8b92c30cc63741018132abc34fc643c95ff6979390075c8670239c8da257410100000000fdffffff0240420f00000000002200205b8cd3b914cf67cdd8fa6273c930353dd36476734fbd962102c2df53b90880cd012f0f000000000022512063ffee4ea7d51e6cadf9086e286a2527922aaa25b8c53aebf32fa32a0a627f5a0247304402200b13d9931afe81cd1b80495552eb16c74d7be8d0aa2d81241d924f4f2936cc4d0220749cc939eee907b6fedaf1426a0473a12568bad5b531f463132eedeb53f15524012103d745445c9362665f22e0d96e9e766f273f3260dea39c8a76bfa05dd2684ddccf6600000002000000000101594101f10f4e99a8917d2254d2bb769d2c8dcb3dc87a18358daa3d16c2449bcd0100000000fdffffff0240420f00000000002200208698658a01efb001e1cc4df6a8b6f3adb461fa3e679385fdaae400bd73996b32012f0f00000000002251208a16c01895ce90fb7b33bede1ceb6e390d13e453836b33413b5d6c608037616a0247304402204f60d329a4189e60a4f836a8257e37c7c33cb015d7a2208bd9e32a9a301ecc0e02205ad318b476744af50545c4b0674108b35c1a14d5b6f4ac2a2e082bf81698900801210207ec2b35534712d86ae030dd9bfaec08e2ddea1ec1cecffb9725ed7acb12ab6666000000', '00000020b17c929516b801d383bea1afca1fc02d024943a484ec59800a277cc0268bc96b1e7ccfab9bc76f42e5c198f8aaa16a967fe1a612f9cb3350a3f5dffb4fdfe3f85045e767ffff7f200200000003020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016800ffffffff022c02062a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ed04db5647ee6c59935b0df8bb1753eff47e0d9bad2ba51b413ffd7ef5a62b32970120000000000000000000000000000000000000000000000000000000000000000000000000020000000001015124f03a8c61ace7ca9932c89982c34a2ae7153145ec168f96344a783e6e246f0000000000ffffffff012a3a0f0000000000160014ba764c66bc9632a09388cb9558d17f7257a0e9d5040047304402203282dc395fb1f6c5ffb10aabfc55eb0a0f67b8aaa43aa11d0ab28bb8c697ddee02206b6ebd704b7705aaccf93844b176489e45e5994f18983d66f555ea82c9509c0f01473044022057a2551b9122960fb8182044afabfef7e09858b02143d50d3239e333a0ff64a502200d2ac549bf0c2f34da42136fb6354f40efad4e6068acdf097152ace877c61c9b0147522102d595ae92b3544c3250fb772f214ad8d4c51425033740a5bcc357190add6d7e7a2102d6063d022691b2490ab454dee73a57c6ff5d308352b461ece69f3c284f2c241252ae0000000002000000000101090ddae2714f734ead0f10bd4daaf5f02d936e3a51baeb94e1e198ea1178198c0000000000ffffffff012a3a0f0000000000160014071c49cad2f420f3c805f9f6b98a57269cb14150040047304402204b5d89aa633bb2f374e96f223618fdbef3548d8ec76d28ca723568098c9ea79802200688707c3a0f10cb6e7654858644d2fea2c17e43419f912a1fe9efaa22a7f2ee014730440220509cc2e94da95888cf0b4fe7d39aed0caf8c534cb84d7730528bd26b942f5ab902205d224e589686de10ca75e91fb9cd5942a68af67db1b3e7413c47e7f2bd64430c0147522102324266de8403b3ab157a09f1f784d587af61831c998c151bcc21bb74c2b2314b2102e3bd38009866c9da8ec4aa99cc4ea9c6c0dd46df15c61ef0ce1f271291714e5752ae00000000', '0000002017dd07ba111707184f7e00f50b04b47b631f68131f2449efe7b48ff2a1b2045a86e925fd07fbc17df48648500fc93e29ea197f35f016a38e5c185007b5d2a3a55045e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016900ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020474f8bb6f27e2aa3328e239ec337c336ad0df73761af9093ebed74a8fec8020c37d1cb671915d13f14743540bf609b83eeafa289912d02cc64f0b1f20c32c23c5045e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016a00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002040c0ceb4b1de3ce6e1480e26c050646706f7cd210a35a6fc77102f06a2763e291e8a81d503e40a0ef0d8ef3b928e91e07d6c339a5651d324c3aa4ae5d28eb62a5045e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016b00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002024467c8a3801d4d43bb218b14fc55c9f63a898e8950c1d2dc9e48fdf4d090b5961d1cf953f7bb40cd75cc82955ff5a51f3e93bef270b2ac89b4ad53af07bc4da5045e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016c00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002009fdc4cb14d1a5add8d1c2dee11a1fce112ccf31e3bd37b0c0ec515a034a795392c4f42ee282a124f133d3954fefad6d76b674d1c47171ecf456d473c075c70b5045e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016d00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020398c9e3124bdf9e62ea492d0c992cc9b787c587d02f4b8c3f321f55efbf274728f2793a671bbfbcfc43b1adf3ecd29f2c4e53729dcd89ba3f7dc870fa3854dca5145e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016e00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000202ae1c7f5a9ed36967988734a4dba1a1166012c156ab72a430f8a9b8c34489f1532676376f9728fb6c9cf5d6372ec87d1deca0c0d7748d4051767dcd0b8a9b02d5145e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03016f00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020c2d72e960af6f011878d1e1f8ba3ad36d2b0820e9d290108f5fbb536a3a3995c94cf21609e9f30c371f9fbc1c0802ade0a24421e88c12bfd5a4117f49456d8275145e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020bc41fc0dd3fd525af0625a0e89ab4d171980916767a7f5a9b6e6914f19c7b22d128810ac08156d6219ddfc00f12aa66c217d1a3dcfee52ee8d6e968c66e1b7655145e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017100ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002060c9ef09223ce6c7516ee7b54e6a62ce8bceaab5a35b0ec463e30760362db664e34f33604bacf782dbe6a167b9714ed252b0f0ca28b19630137a65e7d46522945145e767ffff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017200ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020be0b41d357fb6c22d5f1f0dfadab28dba6c37b072024d0130cb0e0ec1df51a2b5cfe5c8f96ede90782a17fab4838884ee32dec55a82d43a3c1849350b6b78bf95145e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017300ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020b0a8bb76d1328d15877946845e8545b94702bbaddb838da7aae84968d078a009468c98884e153b97b6aa5e39f46fd59f96f1b277ecd4300da791a981a4d31e9f5245e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017400ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002012f958a63852535164f1c16d95eba199acb359e1c9b1bb80eb49073bf8a3f869a2757578fe6075d4f0c90d406da080bc88870571169ad2c90ffb366fe84346b65245e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017500ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020b39ef61835e31ee4209a20c07d61865b5a6d06c97916a952ff649037a0655b1c7f8ec0b00bb7bf9eb33a363f88e3480ef24c13c1285999a9def57f3484bec9bc5245e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017600ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020dc1685373f0ef48e5099f4c8cd12fa9133f71b7ec23bc165922a793aaaf61645d9d5000644b3a00091235216393430d104d66aba13df66b4b757e4ed01cbcc1c5245e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017700ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020a112806a01c656db35fa021b979cc48c1e929c5e8c98e811aac194737d65755ae3da1393891cd88a50acc5f9cdd1868120b616072976dc47a7dba7191959fa265245e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017800ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020cbe8dbdbd5bfd9443a048e50af5705d958601d63c46d04cd81ec11b220a4406c86930557e168f12def307992b5cec2185a75a190634f3dd402f1bd485b8bd6705245e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017900ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002012a8ca7721f80a55db15cddf7370f43c4a60082f60defe3ddbe978bc4a2d125b726a244fb370c5fc0def3556c4d7d2e23e0b252cbcd88619b6afc3f2774fae1f5345e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017a00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020be154f6c75dbda8161ef8f4f259b1e296dfc3aad8f21c1980064809c7c7f966f0c621bb4fd1f62f2b163fb497a8d25f5e7fd058904d0363d652bd15bfb3df9e15345e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017b00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020506a98d1afd6265b7705a80c338eeea9f7a24c946124ea96d23aa5af79bf1f3e04de253c09d40efd70920e73f1d14a34463dd0969b24e0a092f71177e6f7d0855345e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017c00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020178d05c02adfd564a4a2d1b8934c1cc72e6b61b3698194f58e5a2d171645d32c8043d16a43c399985018abc0c6f8d7c73c2ed226cbd523c0d4aeaecb42a4801a5345e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017d00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002035bcbd7497b0844507eb20ac1031c1b373e20d183184bc95bac48b175e74ed571c671387f330e37f64b7c5c507b183516cd8d12d387f6a44a72d5b1d43cf39d65345e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017e00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ac9c82e0e6b6669634df96486c7c4a2fe343e2cf18d55f5949c669583391b0647ac43806dd5ca67b39293ea22e64b783d682099b3641adc84484d50dd08f2d3e5345e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03017f00ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020b318b5efbc31fa277c36f8d0e3d7b3841e6fe60ac26a0170236176a7fada93082849a03ae5161a917225029da51529e6ef8b92807a58ed6cc4b8c18bdbfa08935445e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402800000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020959448c7dc9e5d511787aa9fe79a86511f22fd855872c5f40608ea7a3234684b5c541667cbc697855759573da2ba21bd77310dbf0671e7a2ea34260ffca5d6475445e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402810000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002063251e8e655ac6552b816b04e63a2b7a2807e71586d1974eb7ffdaa84507ed5e2048dee1eae334b48e2f1a125e47bff73cb5a280091849a739a73fda601364805445e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402820000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000209792edd5fbb152812148da53084fa2e83a0b4cabbebc02d5e364ad15bdcb8038f0adf65f81bed42b6a9eecf6342872096144c3069bee6495195ed6a41249e77e5445e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402830000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000209c8315e2c8c0510b76fe60854466a707d5a52f7df8538dcdc328f1db3b58ff6af12015d5a9bfe4c853f98fe35662f4782f5e0e8781bee16111e8653feb460cc45445e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402840000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020d85967913da39f5ac0e163f89795fb9813e9efac4af9cfde9d151d3e0b977958bbe91e6e214cc1ab3805b095d5a7ce176b86506fec47a673b082bb3525511ad55445e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402850000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ce1166ce40402ac786ccffbb3911c75155afe5b3d702742ac074794874772e1e9cf442623955f5f51409d259e0f112c64a751e01ff7264286a4a3c76310d17125545e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402860000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020112af4801eca6be7c1d5566c35d86e42ed0c7a93776b5760c91c093571f28a24399ae96bed6df9f309b68e970529c117f2f78a812b6505881808709550ed94b05545e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402870000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002017f7440e212cc64b1d343535ccce03d20a13d0bbcafc0730bdf890580d46df0c98c6cce0b18b45275a19543b192802f4988c168c8513ab56f5854f1cdc9457ed5545e767ffff7f200700000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402880000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020818d56a56b89301c1f81c7b73270ff2dcedf894d321b266e38a961c99caba438e70e2d41ac032e66e4971ba77c376073a1340e91d283726e616d0ff4a55f2afc5545e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402890000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000202005fdbbe76ee95acc24295138937f5a92144985e74b876b1fadfe68cbea05362fd13b52114f537b6008c46927374c7d0da1d5ff90efdf9091a6980d0dc02f555545e767ffff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04028a0000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020c205b1d6c0d3e58288e396850a6a4b1404ba6b4eeee25fc13848c61e3629485ab9e4ad251e22f3f2b3ac3ffbe1e3ddaff0f914894c30152d57b38c764a2621685545e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04028b0000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020ae9a8ba221d8d9adfd4d04557cdcb4e8069cb814f70f76a150b5a7a23f2506044468febe96990db1ceb20754b60e2bcda1ae6910ce62bbadcdf80e2a0fd7c74c5645e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04028c0000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000002080b8d3ae945f5a1717b34a545c775d24786d1d1b14beba3021a3c9fd40eae20258659c5e76303059e26da6ab80ece214d536bf7feaa6f33cc13a8aa8d27020f65645e767ffff7f200800000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04028d0000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020f0da206c7f7c3f8e858ad849dfe920ec002d533bff3ed8bb7baa52287d44790bc81675085e4bde33c31e88155a821cec9a7276fd70f86fdd194dbe1c6aceb5dc5645e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04028e0000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000020c2b7db111cd824b485db60706e14b8148b84f52011a6c5769878933c5060eb40aee9d7480c3a9dd888b6cbef15de3d8629ff1785f1f3c3a34d00f79359202cfc5645e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04028f0000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003041243723cfd31809ea5d27f36335806aaf8ba2e94e830b1a6f1f1a75cdb6da1c2d6ff82369cb5e9796ea98a9f0c62027aa1dbf79a290bbbe6cda734eec79accc5645e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402900000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030399cb0cbb9cd70b52b4b3e474a977e746910fcdb2331fc9d9c8a0c7f73aea608648f749194729b1b59a96e14ff44f0dff68c507356497fb5494b826f0a1ed5605645e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402910000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003081bc77107b5f564b5a9932c9426ada1904537f4c6b5b44937c6c482cfe9d6d5bf1fc1d6314bc7f54d7385c08821c3cf018c42c2d9ca7cc550519e901597035dd5745e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402920000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030c971d8e2d12d0c75bc4f32a82c0885062fdc0458be380fd3f062a9531bca7f50cfd4723752ae9a144dbef60dad39c2d96dc9ba90728b5e72d843de1408c706695745e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402930000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003062a4e5825ca5494f99f5a1f18f7ed53aa0fbb95f09d114591e3ba7d56164e61167e814795233e88c9f8e89249d1b03b5ecf383f53caea23a57b782c1a2702a035745e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402940000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030839daf2a34725eadac848f28becb2f780ecae6ee515e208a47252555dd2cbd2505fec8540c7295f26305ef8228fe641b31ff3e211c324b627beef19070ea55065745e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402950000ffffffff0200f2052a01000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030e538757582a20bb1a8057dc94d7f4bcedb61f2bb53352b1dba1eaf1aeaf6f31471acb322f6c6ad997f0f099c49ab5b86bdc1794c84d1cd404a94e3d8755dffc25745e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402960000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030f8f0e1a4e5404270428fb68ddc09d092746aba6b4c39acef662d5ed4cb2a88080906f2966780605159145193bca4e3222566a00bf3b3f5dd4b7316bc877947185745e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402970000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000302d8167258ed66540dfa5ba5880743b94de8ab83d27a7a6b7cb7050ee592acb47bd34d45c6583b4343529e80ef4ea69b987b3067ca230efbb372aa39017f6f4d35845e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402980000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030842c80a35ab51a45c31e10db8a54f6b67795a7f016dead1edeac77ffc5c2590123159a9672cc09f89e9c1d82babde1b5faef2a55bd73b8ba9d3a017dfc0b1a305845e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402990000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000302a39a59278b6d7bbb585c939cae3a0cc05a5ea89f23f4c65afa72510f7e0477fdcd559186a927939f96c4486240318c4e8dc0e0097eeab39fe6bed4153d9d96b5845e767ffff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04029a0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030cf3db2b72f837d5861ffbb32424a9292f8d74db5e8d86d02a5a6c772f756d64437b9bb82b46d3757d1373519e3c87c393948b81b4335f57df9d21e7c4dad02b75845e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04029b0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000309ec59b0696e186d1b93bc8f3fe4ff47ec625528f139ab1e1872136574ef44c7e35c844a2ccebe0d6f31831bfccb377d79c1b87bd04e3a99a51ac5706d0de5e045845e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04029c0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003000595b83332c77133287b13c57587c1d26e761aaec3fb2ae08fd7086005e285494d1ac097c6d6293bbbb4781102d1a853b86982a56f3e2667eeb37f1da79a7ac5845e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04029d0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000304ed33349728239cd8e2450d372b009c2c39629b9b7051832e836449a2fbcb2204c07a4fb38ec0bc0a0870c7e5892ebb0bcb89a1c84c2c4ca39f31c48b0bce5695945e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04029e0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000303a1cd4c9e9dbaaada0692df47668ff0f9afb664b27a397719771dac6cbcbe310fc882af8e2f021f3c3a03f585ee8c4d588d804c207c368528075fef3ec495b235945e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04029f0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030bb938155e78b937df5ce511538515abad3d12daabba0ce20ede9b1c7a332105ec0499736f903d1e7d217a3c7ffb6bbc52ee2440013d259877ea1783825f843d85945e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a00000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030662040ff51f64777f7494d20347fbc1e57de01577c504f2685925ee5d8942c5361935420a0b607d4857eebc67aa8362a4a004c8178ab4b89dece24c619fabe9d5945e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a10000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030024b6383f4faaee8a46abb46153916a4db66791e76b4cf755d2484b25737666f43d8c328003f5a2e31f2644430e44aa3b126ba1b50a9c7908855a6b8c417c5b45945e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a20000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000303d9e4b3cf26d748a7bc0b51d75f47b698caa5d49404df8c7a36960422b469a5477d2c7308de83f064016d046c2cc2dff8b1ba2fdeefee2766b5387a66be3141d5945e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a30000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003016821fbda3a7813aabd2eb519f4625724b36b51f79814477b92fd7c458a2ad63e6ba0bb30c9813a3654c24bd3b1fc547ebe0b1bd6d2ea91f1e35b4822c479e4f5a45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a40000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003006c9cc8172034c14a960a3fe23ce4ab1623396307c4e8c93673946717a484d7fe5755b64f4a14b92ec68e96369a646d201aa9e9bc2d2311b136edd517b7677bc5a45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a50000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030a4a711824183601892260b060d90cdb8689039ec511b4785f5ef30ffa4b2e025f2ccbfbbdfee7808cbdacd7bec4194c9f13b94fe5ddebad1a86450f2cc93a25e5a45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a60000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003028d1ee846d8bccd01719c99e0def4f1f4a86df320a508fbb6b320241a9a7087ace4b6680134bbdf63822a4d3197613c8ab1aa897988960375cd9302249ebfe925a45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a70000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030ae7ea95fa9b43b206738c45c605a6611d5113f7b754a995e3f87c2dd10ff786360eb90fb2524f3be5fe1faef5dac0da7ace6f171ff566d3d54bbaf45b49cb70d5a45e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a80000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000303ebcbfbbda57e0fc821764a8534b1e4c02450ed7262f809577722d46f7eabe27525b7a346619caafc949d4e2787ac4cd1ca35c0df75d5dda7560ab27709bd2ca5a45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402a90000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030ee947752312f9115ea79a5187ba7c380af515b4b2754b229ada6794f7e719e418ee958de6ca1dddecef8fcc9ad765f3d2cfedb34f52be875ffb3fd9e20d0dd725b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402aa0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030ef06d81b99c621541533f4804f7b8645270bf69c0bc594e73453e493cb0b8b29165d399502a3d21cc1abc525a4f9c8be987c97913e6a4cb3a07f2cad962b3fd05b45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402ab0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030c7dfee6970ae91486edc4415eb99cd03b8f0c3f36926915db7f01b13c069d027ec3f893af889af4cf417eb67065fcec3b58c54bc59f133006991f468e0cb94655b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402ac0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030a456e513b80f842e98deced1cd507adf8e464d6c7504f1eb518548fd8dbce40c35ee412935da982bf45a17fa0f1b03dbbd43686323f4c4fdea1d24de09b948c65b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402ad0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003076c26c3d94b09fda94f8d4e4281fe2fdc37618a324c11425befa5c1765919d37dd4640708b275b107d6f73c72f787d12498f0737326f228eca5dea4da47d4bc85b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402ae0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030472c1af61662f83a21a4c64667e9d51ae0c58ce01c901878de3f82a9c953f1154e921ac66b4ef47398156a1221c17fc807ee716b3b145d4f3e52c0e0f4a6f3235b45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402af0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030b67bbc468af152cad458eebd674ebb426df002501dced9d768b1259e564645028c022328bd4acd790258e727526e6752afb0e712ac648abde199e0a3304d57f05c45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b00000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030200621e8e33ec97e867f3ea1cbffffd3f294bf8fe1cf0692d0057ea2b919ec36221e35c58a2ac55440ee09bc9f0e311f4fb663f0fcfc0eada2600677e469401f5c45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b10000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000303b8b015859940691059a2e954c53204e85afb3a4ae1d32ede19971aa5d2fbe52cfa4ba2177ffb2b021c30320f89d0f910bf44efc07067114335780ba3f10bd765c45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b20000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000304cfa59583423a05b452d15b985c2be9d3df88fcbdd031f96e8ad9d51e9b2b3441beffcee4d4c244e1e4ce7094b31646c6cc5567129082046627bb39e5310fe3c5c45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b30000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000301e1e3efd764ddc5011f48108e89a163d50f50e29a377580507d803539fe052071d1a673c9b262898295d243626f5877b909d4598d91d13c75f082224bbb1b9055c45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b40000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000305bac3ad01b70bb74f6c49063e1b818d39ff3366eddac4f7be9b2684883bca74bb6297f1767ccd20ddc3214e45b31ae868d15b520f05e4bd67b88ad28de08fce45c45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b50000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000300d7ca8158d5fe3a982b452b1d60315e32a92ee4d31b8dc3dcbe133333077c97e2758023341f0424bcfd5ab9940df921e88daa2aea0a13cb84aa6c4ec13d532015d45e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b60000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030cce8fd6ca01c2112d6f6a0e2052002e9f71cdefff9b6cdf76547cb1f3a46af615f414285c888f66d5d6240b886287726ecbd5921b13acb2119e67805bb2d56705d45e767ffff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b70000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000301f0040ea70cf5a01e73546e86abdf30ba200c075968052b5e4418230d4acc44be05fec05e27d85f08438b0eab8a752b9b2733d922a377264c138ff059374bb595d45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b80000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000309670a5b4400a07a83489bb2a1d24f4210417eebc39cabe880d2282ec384eee69b2735c7af36ee348970ea85cb9190bc48815a9209eccc537bfc54794cad6a4b35d45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402b90000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000305e6e7740529202f83cd844d3e0189d521a67334e96d356ba52525ff6fda3ab0c5483d2e59b5c73967fb844c59f96072098276b058a63d065830a3ebb4d4123595d45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402ba0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030474f309999d613e1de4e2b3ba181c0d2e8ecfa4d8310bc16094f9cb6d92e514658b16873f8d004be021fe039cf789ac456478117c7a6919c54a49ea6729d3e1c5d45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402bb0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003046089a2183b7b8419ac72919206d16b699975dbd85d3a4e34773af3115e21f30753e4df9a3a59c8be175866e7308443f2013ced01c7d6bdb7edc9bb850b9c5325e45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402bc0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030bd0f28f06326941da6fbe578d7ad7549a37889ea6126471d7314de01eaebab21d6810504e2f55e89ebc0865310c7f1f1d55bf0b806825fa82787d4348141ec875e45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402bd0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030a18d4637819250c31886aaa3ea89f7aff41855a9042340cf60b4b9456b6e7742b00fe5bd78b3a32c9da95eb99b7abfcb592a76cf8b34761c485342eb700b24185e45e767ffff7f200500000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402be0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030e9cb81ab54178bda14a8395e8f294c16d6e907e05b5f6b67f9fa79b4c7d0c86c33ed95c62e55c6235eaeb393d8ee6d426d56bf5b7d0d1190a8de8574524c7a4e5e45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402bf0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000309c90bb876b7f9a8cf5f162c4d9ac5a2ba20d65aecc65d3c5280de23d71acb57148c0ba0114bdf595a5c9dcdebcf922d46aa1057f8075e2168b3abe4d04ceca5d5e45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c00000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003031d701667675f5c9e2a8c143408dc074423d19ad6f410151972ee601de19921b2c4997f71d7d702720598f85730b126d66ddaa65a325ec75cb64bf865bb3f9d75e45e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c10000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030ec1b69c90796b5e7dedf4fac5bfaa38b08986a024cc81e252399d01452aece1ba9ae7a787e60e96a4ec00df22d513c2ec3b6bcf2d819a62b9a69fd4c16ae0ebe5f45e767ffff7f200400000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c20000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030da4f5e26be854bcccfc3efe8bcc969251f9210ba7642fa7f531e70958ce1a53a2cd0938aeee842232c01f13e465fa19e1017b4e25401a3b512e284516675ef755f45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c30000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030598fd05ef23fcbcf7c872b0c748c292874948c6842585d2419a6de5fabd15c1560018fbc99216e9e3cda1a7c1229975ab8228194bb4d4b2a9d5b1fc9792644325f45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c40000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003039fbb34ad9e86a1ef54a9f7221dfd6aab1a9a93911baef457068d1c3880c594234b10d41685b1ef92b9a171e4e004f3d70d8c5b9b26c2542f96bdc5a292698ca5f45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c50000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '000000306a39d1647473dc49f0e78278335dbc0418d91904ce473d824e66573a835b2d0262f7e36c68ad0debdcd31faa63810dc891834c0b8f136029a2c10d9ecee526175f45e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c60000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030253aee6ae859e35bed50d0b4e74463c3c30cea7c484f108379af5e00888bdf222a7169c0bae73b50004b1ad14d968ee0505a5ac40c1400fca17978b5479329465f45e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c70000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '0000003087e40026893cf19f73bcdaacb2adf1c5cc544abc2cb8ab7af4a3943f94d01a35578955ee09ebfbe14d75345d4d61499c39817542f32c18f2dec4006ae9ef5d8e6045e767ffff7f200200000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c80000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030bea2746a48f9ad44a4857546914e4402c62d338e4b1e62699a0175da681f4a0a201e6181e3cddbe84b0b3dca0f97154792ead40dd174f4c5bdaa4574dffec38f6045e767ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402c90000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000', '00000030ad9a4d078baf27e222db419923bff2844fb2bd947951b71d585219c9d49f075304a69461d2decc1155ad72a3e725dd4ec2f321cb13f07a8815349cccd379bc0b6045e767ffff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402ca0000ffffffff0200f9029500000000160014cbe7f8ee3e6133b9f975501d7fe827f6915ec0910000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000']
bitcoind.restore_blocks(blocks)
l1 = node_factory.get_node(dbfile='l1-missed-p2wpkh-CLOSINGD_COMPLETE.sqlite3.xz', options={'database-upgrade': True})
l2 = node_factory.get_node(dbfile='l2-missed-p2wpkh-CLOSED.sqlite3.xz', options={'database-upgrade': True}, broken_log="Potentially missing 2 outputs from previous closes: scanning from block 103|Rescan found [0-9a-f:]*!|Rescan finished! 1 outputs recovered. Let's never do that again")
# They should both see the p2wpkh outputs.
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 2)
wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == 2)
# This can actually take a while for 100 blocks!
l2.daemon.wait_for_log('Rescan finished! 1 outputs recovered')
@pytest.mark.parametrize("restart", [False, True])
@unittest.skipIf(TEST_NETWORK != 'regtest', "elementsd doesn't support psbt features we need")
def test_sendpsbt_confirm(node_factory, bitcoind, restart):
"""We should see our sendpsbt in wallet, and that it gets confirmed"""
l1, l2 = node_factory.get_nodes(2)
l1.fundwallet(100000)
psbt = l1.rpc.fundpsbt(satoshi=10000,
feerate=7500,
startweight=42)['psbt']
psbt = l2.rpc.addpsbtoutput(10000, psbt)['psbt']
psbt = l1.rpc.signpsbt(psbt)['signed_psbt']
sent = l1.rpc.sendpsbt(psbt)
# Unconfirmed
lt = only_one([t for t in l1.rpc.listtransactions()['transactions'] if t['rawtx'] == sent['tx']])
assert lt['blockheight'] == 0
if restart:
l1.restart()
bitcoind.generate_block(1, wait_for_mempool=sent['txid'])
sync_blockheight(bitcoind, [l1])
# Should be confirmed now!
lt = only_one([t for t in l1.rpc.listtransactions()['transactions'] if t['rawtx'] == sent['tx']])
assert lt['blockheight'] == bitcoind.rpc.getblockcount()
def test_old_htlcs_cleanup(node_factory, bitcoind):
"""We lazily delete htlcs from channel_htlcs table"""
l1, l2 = node_factory.line_graph(2)
for _ in range(10):
l1.pay(l2, 1000)
l1.rpc.close(l2.info['id'])
bitcoind.generate_block(100, wait_for_mempool=1)
wait_for(lambda: l1.rpc.listpeerchannels() == {'channels': []})
# We don't see them!
assert l1.rpc.listhtlcs() == {'htlcs': []}
l1.stop()
# They're still there.
assert l1.db_query('SELECT COUNT(*) as c FROM channel_htlcs')[0]['c'] == 10
l1.start()
# Now they're not
assert l1.db_query('SELECT COUNT(*) as c FROM channel_htlcs')[0]['c'] == 0
assert l1.rpc.listhtlcs() == {'htlcs': []}
@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db")
@unittest.skipIf(TEST_NETWORK != 'regtest', "sqlite3 snapshot is regtest")
def test_pending_payments_cleanup(node_factory, bitcoind):
bitcoind.generate_block(1)
l1 = node_factory.get_node(dbfile='l1-pending-sendpays-with-no-htlc.sqlite3.xz', options={'database-upgrade': True})
assert [p['status'] for p in l1.rpc.listsendpays()['payments']] == ['failed', 'pending']
assert [p['status'] for p in l1.rpc.listpays()['pays']] == ['pending']