Browse Source

Merge JoinMarket-Org/joinmarket-clientserver#1124: Allow utxo address validation with script

fb4644ec5b Allow utxo address validation with script (Adam Gibson)

Pull request description:

  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.

ACKs for top commit:
  kristapsk:
    ACK fb4644ec5b. Was able to reproduce the issue and that this fixes it. Also test suite passes.

Tree-SHA512: 805e45144403317167243023bb3bda0d14b0197d5df71e510a1ca898b69faa2298f8d2a13d6915e6875fd99fcdeb343297b062fe0f4ce0d3b3ca1a95da7271ba
master
Kristaps Kaupe 4 years ago
parent
commit
71bb2e808b
No known key found for this signature in database
GPG Key ID: 33E472FE870C7E5D
  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:
result.append(None)
else:
if ret['scriptPubKey'].get('addresses'):
address = ret['scriptPubKey']['addresses'][0]
else:
address = None
result_dict = {'value': int(Decimal(str(ret['value'])) *
Decimal('1e8')),
'address': address,
'script': hextobin(ret['scriptPubKey']['hex'])}
if includeconf:
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
utxo_to_return = utxo_bin if utxo_binary else u
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"):
"""For each (utxo, privkey), first
convert the privkey and convert to address,
then use the blockchain instance to look up
the utxo and check that its address field matches.
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 = []
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)
jmprint('validating this utxo: ' + utxostr, "info")
# as noted in `ImportWalletMixin` code comments, there is not
# yet a functional auto-detection of key type from WIF, so the
# second argument is ignored; we assume p2sh-p2wpkh if segwit=True,
# p2pkh if segwit=False, and p2wpkh if segwit="native" (slightly
# ugly, just done for backwards compat.).
# yet a functional auto-detection of key type from WIF, hence
# the need for this additional switch:
if utxo_address_type == "p2wpkh":
engine = BTC_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:
jmprint("utxo not found on blockchain: " + utxostr, "error")
return False
if res[0]['address'] != addr:
jmprint("privkey corresponds to the wrong address for utxo: " + utxostr, "error")
jmprint("blockchain returned address: {}".format(res[0]['address']), "error")
jmprint("your privkey gave this address: " + addr, "error")
return False
returned_addr = engine.script_to_address(res[0]['script'])
if returned_addr != addr:
return print_failed_addr_match(utxostr, addr, returned_addr)
if retrieve:
results.append((u, res[0]['value']))
jmprint('all utxos validated OK', "success")

16
jmclient/test/test_commitment_utils.py

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

Loading…
Cancel
Save