Browse Source

Allow utxo address validation with script

Prior to this commit, the sendtomany.py script
would sometimes fail based on the output of the
RPC call gettxout returning only a script, and not
an address. After this commit, the validation will
still work correctly in these cases using the
chosen cryptoengine to convert from script to address.
Also notably, the return value of utxo set does
not include an 'address' key any more.
Tests are updated to reflect this.
master
Adam Gibson 4 years ago
parent
commit
fb4644ec5b
No known key found for this signature in database
GPG Key ID: 141001A1AF77F20B
  1. 5
      jmclient/jmclient/blockchaininterface.py
  2. 25
      jmclient/jmclient/commitment_utils.py
  3. 16
      jmclient/test/test_commitment_utils.py
  4. 2
      jmclient/test/test_wallets.py

5
jmclient/jmclient/blockchaininterface.py

@ -387,13 +387,8 @@ class BitcoinCoreInterface(BlockchainInterface):
if ret is None: if ret is None:
result.append(None) result.append(None)
else: else:
if ret['scriptPubKey'].get('addresses'):
address = ret['scriptPubKey']['addresses'][0]
else:
address = None
result_dict = {'value': int(Decimal(str(ret['value'])) * result_dict = {'value': int(Decimal(str(ret['value'])) *
Decimal('1e8')), Decimal('1e8')),
'address': address,
'script': hextobin(ret['scriptPubKey']['hex'])} 'script': hextobin(ret['scriptPubKey']['hex'])}
if includeconf: if includeconf:
result_dict['confirms'] = int(ret['confirmations']) result_dict['confirms'] = int(ret['confirmations'])

25
jmclient/jmclient/commitment_utils.py

@ -33,16 +33,19 @@ def get_utxo_info(upriv, utxo_binary=False):
raise raise
utxo_to_return = utxo_bin if utxo_binary else u utxo_to_return = utxo_bin if utxo_binary else u
return utxo_to_return, priv return utxo_to_return, priv
def print_failed_addr_match(utxostr, addr1, addr2):
jmprint("privkey corresponds to the wrong address for utxo: " + utxostr, "error")
jmprint("blockchain returned address: {}".format(addr2), "error")
jmprint("your privkey gave this address: " + addr1, "error")
return False
def validate_utxo_data(utxo_datas, retrieve=False, utxo_address_type="p2wpkh"): def validate_utxo_data(utxo_datas, retrieve=False, utxo_address_type="p2wpkh"):
"""For each (utxo, privkey), first """For each (utxo, privkey), first
convert the privkey and convert to address, convert the privkey and convert to address,
then use the blockchain instance to look up then use the blockchain instance to look up
the utxo and check that its address field matches. the utxo and check that its address field matches.
If retrieve is True, return the set of utxos and their values. If retrieve is True, return the set of utxos and their values.
If segwit is true, assumes a p2sh wrapped p2wpkh, i.e.
native segwit is NOT currently supported here. If segwit
is false, p2pkh is assumed.
""" """
results = [] results = []
for u, priv in utxo_datas: for u, priv in utxo_datas:
@ -52,10 +55,8 @@ def validate_utxo_data(utxo_datas, retrieve=False, utxo_address_type="p2wpkh"):
sys.exit(EXIT_FAILURE) sys.exit(EXIT_FAILURE)
jmprint('validating this utxo: ' + utxostr, "info") jmprint('validating this utxo: ' + utxostr, "info")
# as noted in `ImportWalletMixin` code comments, there is not # as noted in `ImportWalletMixin` code comments, there is not
# yet a functional auto-detection of key type from WIF, so the # yet a functional auto-detection of key type from WIF, hence
# second argument is ignored; we assume p2sh-p2wpkh if segwit=True, # the need for this additional switch:
# p2pkh if segwit=False, and p2wpkh if segwit="native" (slightly
# ugly, just done for backwards compat.).
if utxo_address_type == "p2wpkh": if utxo_address_type == "p2wpkh":
engine = BTC_P2WPKH engine = BTC_P2WPKH
elif utxo_address_type == "p2sh-p2wpkh": elif utxo_address_type == "p2sh-p2wpkh":
@ -71,11 +72,9 @@ def validate_utxo_data(utxo_datas, retrieve=False, utxo_address_type="p2wpkh"):
if len(res) != 1 or None in res: if len(res) != 1 or None in res:
jmprint("utxo not found on blockchain: " + utxostr, "error") jmprint("utxo not found on blockchain: " + utxostr, "error")
return False return False
if res[0]['address'] != addr: returned_addr = engine.script_to_address(res[0]['script'])
jmprint("privkey corresponds to the wrong address for utxo: " + utxostr, "error") if returned_addr != addr:
jmprint("blockchain returned address: {}".format(res[0]['address']), "error") return print_failed_addr_match(utxostr, addr, returned_addr)
jmprint("your privkey gave this address: " + addr, "error")
return False
if retrieve: if retrieve:
results.append((u, res[0]['value'])) results.append((u, res[0]['value']))
jmprint('all utxos validated OK', "success") jmprint('all utxos validated OK', "success")

16
jmclient/test/test_commitment_utils.py

@ -1,8 +1,8 @@
from commontest import DummyBlockchainInterface from commontest import DummyBlockchainInterface
import pytest import pytest
from jmbase import utxostr_to_utxo from jmbase import utxostr_to_utxo, hextobin
from jmclient import (load_test_config, jm_single) from jmclient import (load_test_config, jm_single, BTC_P2WPKH)
from jmclient.commitment_utils import get_utxo_info, validate_utxo_data from jmclient.commitment_utils import get_utxo_info, validate_utxo_data
from jmbitcoin import select_chain_params from jmbitcoin import select_chain_params
@ -20,7 +20,7 @@ def test_get_utxo_info():
success, fakeutxo_bin = utxostr_to_utxo(fakeutxo) success, fakeutxo_bin = utxostr_to_utxo(fakeutxo)
assert success assert success
fake_query_results = [{'value': 200000000, fake_query_results = [{'value': 200000000,
'address': iaddr, 'script': BTC_P2WPKH.address_to_script(iaddr),
'utxo': fakeutxo_bin, 'utxo': fakeutxo_bin,
'confirms': 20}] 'confirms': 20}]
dbci.insert_fake_query_results(fake_query_results) dbci.insert_fake_query_results(fake_query_results)
@ -29,15 +29,15 @@ def test_get_utxo_info():
assert u == fakeutxo assert u == fakeutxo
assert priv == privkey assert priv == privkey
#invalid format #invalid format
with pytest.raises(Exception) as e_info: with pytest.raises(Exception):
u, priv = get_utxo_info(fakeutxo + privkey) u, priv = get_utxo_info(fakeutxo + privkey)
#invalid index #invalid index
fu2 = "ab"*32 + ":-1" fu2 = "ab"*32 + ":-1"
with pytest.raises(Exception) as e_info: with pytest.raises(Exception):
u, priv = get_utxo_info(fu2 + "," + privkey) u, priv = get_utxo_info(fu2 + "," + privkey)
#invalid privkey #invalid privkey
p2 = privkey[:-1] + 'j' p2 = privkey[:-1] + 'j'
with pytest.raises(Exception) as e_info: with pytest.raises(Exception):
u, priv = get_utxo_info(fakeutxo + "," + p2) u, priv = get_utxo_info(fakeutxo + "," + p2)
utxodatas = [(fakeutxo_bin, privkey)] utxodatas = [(fakeutxo_bin, privkey)]
@ -46,7 +46,7 @@ def test_get_utxo_info():
#try to retrieve #try to retrieve
retval = validate_utxo_data(utxodatas, True) retval = validate_utxo_data(utxodatas, True)
assert retval[0] == (fakeutxo_bin, 200000000) assert retval[0] == (fakeutxo_bin, 200000000)
fake_query_results[0]['address'] = "fakeaddress" fake_query_results[0]['script'] = hextobin("76a91479b000887626b294a914501a4cd226b58b23598388ac")
dbci.insert_fake_query_results(fake_query_results) dbci.insert_fake_query_results(fake_query_results)
#validate should fail for wrong address #validate should fail for wrong address
retval = validate_utxo_data(utxodatas, False) retval = validate_utxo_data(utxodatas, False)
@ -58,4 +58,4 @@ def test_get_utxo_info():
assert not retval assert not retval
dbci.setQUSFail(False) dbci.setQUSFail(False)
select_chain_params("bitcoin/regtest") select_chain_params("bitcoin/regtest")
jm_single().config.set("BLOCKCHAIN", "network", "mainnet") jm_single().config.set("BLOCKCHAIN", "network", "regtest")

2
jmclient/test/test_wallets.py

@ -50,7 +50,7 @@ def test_query_utxo_set(setup_wallets):
includeconf=True, includeunconf=True) includeconf=True, includeunconf=True)
assert len(res1) == 1 assert len(res1) == 1
assert len(res2) == 2 assert len(res2) == 2
assert all([x in res1[0] for x in ['script', 'address', 'value']]) assert all([x in res1[0] for x in ['script', 'value']])
assert not 'confirms' in res1[0] assert not 'confirms' in res1[0]
assert 'confirms' in res2[0] assert 'confirms' in res2[0]
assert 'confirms' in res2[1] assert 'confirms' in res2[1]

Loading…
Cancel
Save