Browse Source

Merge #1295: Default 3rd argument of gettxout should be True

a3e1ba3 Default 3rd argument of gettxout should be True (Adam Gibson)
master
Adam Gibson 4 years ago
parent
commit
babad19639
No known key found for this signature in database
GPG Key ID: 141001A1AF77F20B
  1. 20
      jmclient/jmclient/blockchaininterface.py
  2. 2
      jmclient/jmclient/maker.py
  3. 9
      jmclient/jmclient/taker.py
  4. 2
      jmclient/jmclient/wallet.py
  5. 8
      jmclient/test/commontest.py
  6. 4
      jmclient/test/test_wallets.py

20
jmclient/jmclient/blockchaininterface.py

@ -39,7 +39,7 @@ class BlockchainInterface(object):
"""pushes tx to the network, returns False if failed""" """pushes tx to the network, returns False if failed"""
@abc.abstractmethod @abc.abstractmethod
def query_utxo_set(self, txouts, includeconf=False): def query_utxo_set(self, txouts, includeconfs=False):
""" """
takes a utxo or a list of utxos takes a utxo or a list of utxos
returns None if they are spend or unconfirmed returns None if they are spend or unconfirmed
@ -109,7 +109,7 @@ class ElectrumWalletInterface(BlockchainInterface): #pragma: no cover
log.debug("Pushed via Electrum successfully, hash: " + tx_hash) log.debug("Pushed via Electrum successfully, hash: " + tx_hash)
return True return True
def query_utxo_set(self, txout, includeconf=False): def query_utxo_set(self, txout, includeconfs=False):
"""Behaves as for Core; TODO make it faster if possible. """Behaves as for Core; TODO make it faster if possible.
Note in particular a failed connection should result in Note in particular a failed connection should result in
a result list containing at least one "None" which the a result list containing at least one "None" which the
@ -141,7 +141,7 @@ class ElectrumWalletInterface(BlockchainInterface): #pragma: no cover
'address': address, 'address': address,
'script': btc.address_to_script(address) 'script': btc.address_to_script(address)
} }
if includeconf: if includeconfs:
if int(u['height']) in [0, -1]: if int(u['height']) in [0, -1]:
#-1 means unconfirmed inputs #-1 means unconfirmed inputs
r['confirms'] = 0 r['confirms'] = 0
@ -389,15 +389,17 @@ class BitcoinCoreInterface(BlockchainInterface):
return False return False
return True return True
def query_utxo_set(self, txout, includeconf=False, includeunconf=False): def query_utxo_set(self, txout, includeconfs=False, include_mempool=True):
"""If txout is either (a) a single utxo in (txidbin, n) form, """If txout is either (a) a single utxo in (txidbin, n) form,
or a list of the same, returns, as a list for each txout item, or a list of the same, returns, as a list for each txout item,
the result of gettxout from the bitcoind rpc for those utxos; the result of gettxout from the bitcoind rpc for those utxos;
if any utxo is invalid, None is returned. if any utxo is invalid, None is returned.
includeconf: if this is True, the current number of confirmations includeconfs: if this is True, the current number of confirmations
of the prescribed utxo is included in the returned result dict. of the prescribed utxo is included in the returned result dict.
includeunconf: if True, utxos which currently have zero confirmations include_mempool: if True, the contents of the mempool are included;
are included in the result set. this *both* means that utxos that are spent in in-mempool transactions
are *not* returned, *and* means that utxos that are created in the
mempool but have zero confirmations *are* returned.
If the utxo is of a non-standard type such that there is no address, If the utxo is of a non-standard type such that there is no address,
the address field in the dict is None. the address field in the dict is None.
""" """
@ -416,14 +418,14 @@ class BitcoinCoreInterface(BlockchainInterface):
log.warn("Invalid utxo format, ignoring: {}".format(txo)) log.warn("Invalid utxo format, ignoring: {}".format(txo))
result.append(None) result.append(None)
continue continue
ret = self._rpc('gettxout', [txo_hex, txo_idx, includeunconf]) ret = self._rpc('gettxout', [txo_hex, txo_idx, include_mempool])
if ret is None: if ret is None:
result.append(None) result.append(None)
else: else:
result_dict = {'value': int(Decimal(str(ret['value'])) * result_dict = {'value': int(Decimal(str(ret['value'])) *
Decimal('1e8')), Decimal('1e8')),
'script': hextobin(ret['scriptPubKey']['hex'])} 'script': hextobin(ret['scriptPubKey']['hex'])}
if includeconf: if includeconfs:
result_dict['confirms'] = int(ret['confirmations']) result_dict['confirms'] = int(ret['confirmations'])
result.append(result_dict) result.append(result_dict)
return result return result

2
jmclient/jmclient/maker.py

@ -82,7 +82,7 @@ class Maker(object):
#finally, check that the proffered utxo is real, old enough, large enough, #finally, check that the proffered utxo is real, old enough, large enough,
#and corresponds to the pubkey #and corresponds to the pubkey
res = jm_single().bc_interface.query_utxo_set([cr_dict['utxo']], res = jm_single().bc_interface.query_utxo_set([cr_dict['utxo']],
includeconf=True) includeconfs=True)
if len(res) != 1 or not res[0]: if len(res) != 1 or not res[0]:
reason = "authorizing utxo is not valid" reason = "authorizing utxo is not valid"
return reject(reason) return reject(reason)

9
jmclient/jmclient/taker.py

@ -558,7 +558,8 @@ class Taker(object):
return verified_data return verified_data
def _verify_ioauth_inputs(self, nick, utxo_list, auth_pub): def _verify_ioauth_inputs(self, nick, utxo_list, auth_pub):
utxo_data = jm_single().bc_interface.query_utxo_set(utxo_list) utxo_data = jm_single().bc_interface.query_utxo_set(utxo_list,
includeconfs=True)
if None in utxo_data: if None in utxo_data:
raise IoauthInputVerificationError([ raise IoauthInputVerificationError([
"ERROR: outputs unconfirmed or already spent. utxo_data=" "ERROR: outputs unconfirmed or already spent. utxo_data="
@ -570,6 +571,10 @@ class Taker(object):
# Construct the Bitcoin address for the auth_pub field # Construct the Bitcoin address for the auth_pub field
# Ensure that at least one address from utxos corresponds. # Ensure that at least one address from utxos corresponds.
for inp in utxo_data: for inp in utxo_data:
if inp["confirms"] <= 0:
raise IoauthInputVerificationError([
f"maker's ({nick}) proposed utxo is not confirmed, "
"rejecting."])
try: try:
if self.wallet_service.pubkey_has_script( if self.wallet_service.pubkey_has_script(
auth_pub, inp['script']): auth_pub, inp['script']):
@ -756,7 +761,7 @@ class Taker(object):
def filter_by_coin_age_amt(utxos, age, amt): def filter_by_coin_age_amt(utxos, age, amt):
results = jm_single().bc_interface.query_utxo_set(utxos, results = jm_single().bc_interface.query_utxo_set(utxos,
includeconf=True) includeconfs=True)
newresults = [] newresults = []
too_new = [] too_new = []
too_small = [] too_small = []

2
jmclient/jmclient/wallet.py

@ -2564,7 +2564,7 @@ class FidelityBondMixin(object):
def get_validated_timelocked_fidelity_bond_utxo(cls, utxo, utxo_pubkey, locktime, def get_validated_timelocked_fidelity_bond_utxo(cls, utxo, utxo_pubkey, locktime,
cert_expiry, current_block_height): cert_expiry, current_block_height):
utxo_data = jm_single().bc_interface.query_utxo_set(utxo, includeconf=True) utxo_data = jm_single().bc_interface.query_utxo_set(utxo, includeconfs=True)
if utxo_data[0] == None: if utxo_data[0] == None:
return None return None
if utxo_data[0]["confirms"] <= 0: if utxo_data[0]["confirms"] <= 0:

8
jmclient/test/commontest.py

@ -72,7 +72,7 @@ class DummyBlockchainInterface(BlockchainInterface):
def reset_confs(self): def reset_confs(self):
self.confs_for_qus = {} self.confs_for_qus = {}
def query_utxo_set(self, txouts, includeconf=False): def query_utxo_set(self, txouts, includeconfs=False):
if self.qusfail: if self.qusfail:
#simulate failure to find the utxo #simulate failure to find the utxo
return [None] return [None]
@ -97,8 +97,8 @@ class DummyBlockchainInterface(BlockchainInterface):
'fd9711a2ef340750db21efb761f5f7d665d94b312332dc354e252c77e9c48349:0': [50000000, 6]} 'fd9711a2ef340750db21efb761f5f7d665d94b312332dc354e252c77e9c48349:0': [50000000, 6]}
wallet_outs = dictchanger(wallet_outs) wallet_outs = dictchanger(wallet_outs)
if includeconf and set(txouts).issubset(set(wallet_outs)): if includeconfs and set(txouts).issubset(set(wallet_outs)):
#includeconf used as a trigger for a podle check; #includeconfs used as a trigger for a podle check;
#here we simulate a variety of amount/age returns #here we simulate a variety of amount/age returns
results = [] results = []
for to in txouts: for to in txouts:
@ -116,7 +116,7 @@ class DummyBlockchainInterface(BlockchainInterface):
result_dict = {'value': 200000000, result_dict = {'value': 200000000,
'address': "mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ", 'address': "mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ",
'script': hextobin('76a91479b000887626b294a914501a4cd226b58b23598388ac')} 'script': hextobin('76a91479b000887626b294a914501a4cd226b58b23598388ac')}
if includeconf: if includeconfs:
if t in self.confs_for_qus: if t in self.confs_for_qus:
confs = self.confs_for_qus[t] confs = self.confs_for_qus[t]
else: else:

4
jmclient/test/test_wallets.py

@ -44,10 +44,10 @@ def test_query_utxo_set(setup_wallets):
txid2 = do_tx(wallet_service, 20000000) txid2 = do_tx(wallet_service, 20000000)
print("Got txs: ", txid, txid2) print("Got txs: ", txid, txid2)
res1 = jm_single().bc_interface.query_utxo_set( res1 = jm_single().bc_interface.query_utxo_set(
(txid, 0), includeunconf=True) (txid, 0), include_mempool=True)
res2 = jm_single().bc_interface.query_utxo_set( res2 = jm_single().bc_interface.query_utxo_set(
[(txid, 0), (txid2, 1)], [(txid, 0), (txid2, 1)],
includeconf=True, includeunconf=True) includeconfs=True, include_mempool=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', 'value']]) assert all([x in res1[0] for x in ['script', 'value']])

Loading…
Cancel
Save