|
|
|
|
@ -70,103 +70,6 @@ class BlockchainInterface(object):
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ElectrumWalletInterface is currently broken |
|
|
|
|
class ElectrumWalletInterface(BlockchainInterface): #pragma: no cover |
|
|
|
|
"""A pseudo-blockchain interface using the existing |
|
|
|
|
Electrum server connection in an Electrum wallet. |
|
|
|
|
Usage requires calling set_wallet with a valid Electrum |
|
|
|
|
wallet instance. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
def __init__(self, testnet=False): |
|
|
|
|
super().__init__() |
|
|
|
|
self.last_sync_unspent = 0 |
|
|
|
|
|
|
|
|
|
def set_wallet(self, wallet): |
|
|
|
|
self.wallet = wallet |
|
|
|
|
|
|
|
|
|
def sync_addresses(self, wallet): |
|
|
|
|
log.debug("Dummy electrum interface, no sync address") |
|
|
|
|
|
|
|
|
|
def sync_unspent(self, wallet): |
|
|
|
|
log.debug("Dummy electrum interface, no sync unspent") |
|
|
|
|
|
|
|
|
|
def pushtx(self, txhex, timeout=10): |
|
|
|
|
#synchronous send |
|
|
|
|
from electrum.transaction import Transaction |
|
|
|
|
etx = Transaction(txhex) |
|
|
|
|
etx.deserialize() |
|
|
|
|
tx_hash = etx.hash() |
|
|
|
|
try: |
|
|
|
|
retval = self.wallet.network.synchronous_get( |
|
|
|
|
('blockchain.transaction.broadcast', [str(etx)]), timeout) |
|
|
|
|
except: |
|
|
|
|
log.debug("Failed electrum push") |
|
|
|
|
return False |
|
|
|
|
if retval != tx_hash: |
|
|
|
|
log.debug("Pushtx over Electrum returned wrong value: " + str( |
|
|
|
|
retval)) |
|
|
|
|
return False |
|
|
|
|
log.debug("Pushed via Electrum successfully, hash: " + tx_hash) |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def query_utxo_set(self, txout, includeconfs=False): |
|
|
|
|
"""Behaves as for Core; TODO make it faster if possible. |
|
|
|
|
Note in particular a failed connection should result in |
|
|
|
|
a result list containing at least one "None" which the |
|
|
|
|
caller can use as a flag for failure. |
|
|
|
|
""" |
|
|
|
|
self.current_height = self.wallet.network.blockchain.local_height |
|
|
|
|
if not isinstance(txout, list): |
|
|
|
|
txout = [txout] |
|
|
|
|
utxos = [[t[:64], int(t[65:])] for t in txout] |
|
|
|
|
result = [] |
|
|
|
|
for ut in utxos: |
|
|
|
|
address = self.wallet.network.synchronous_get(( |
|
|
|
|
'blockchain.utxo.get_address', ut)) |
|
|
|
|
try: |
|
|
|
|
utxo_info = self.wallet.network.synchronous_get(( |
|
|
|
|
"blockchain.address.listunspent", [address])) |
|
|
|
|
except Exception as e: |
|
|
|
|
log.debug("Got exception calling listunspent: " + repr(e)) |
|
|
|
|
raise |
|
|
|
|
utxo = None |
|
|
|
|
for u in utxo_info: |
|
|
|
|
if u['tx_hash'] == ut[0] and u['tx_pos'] == ut[1]: |
|
|
|
|
utxo = u |
|
|
|
|
if utxo is None: |
|
|
|
|
result.append(None) |
|
|
|
|
continue |
|
|
|
|
r = { |
|
|
|
|
'value': u['value'], |
|
|
|
|
'address': address, |
|
|
|
|
'script': btc.address_to_script(address) |
|
|
|
|
} |
|
|
|
|
if includeconfs: |
|
|
|
|
if int(u['height']) in [0, -1]: |
|
|
|
|
#-1 means unconfirmed inputs |
|
|
|
|
r['confirms'] = 0 |
|
|
|
|
else: |
|
|
|
|
#+1 because if current height = tx height, that's 1 conf |
|
|
|
|
r['confirms'] = int(self.current_height) - int(u['height']) + 1 |
|
|
|
|
result.append(r) |
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
def estimate_fee_per_kb(self, N): |
|
|
|
|
tx_fees_factor = float(jm_single().config.get('POLICY', |
|
|
|
|
'tx_fees_factor')) |
|
|
|
|
if super().fee_per_kb_has_been_manually_set(N): |
|
|
|
|
# use a floor of 1000 to not run into node relay problems |
|
|
|
|
return int(max(1000, |
|
|
|
|
random.uniform(N * float(1 - tx_fees_factor), |
|
|
|
|
N * float(1 + tx_fees_factor)))) |
|
|
|
|
fee = self.wallet.network.synchronous_get(('blockchain.estimatefee', [N] |
|
|
|
|
)) |
|
|
|
|
fee_per_kb_sat = int(float(fee) * 100000000) |
|
|
|
|
log.info("Got fee: " + btc.fee_per_kb_to_str(fee_per_kb_sat)) |
|
|
|
|
return fee_per_kb_sat |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BitcoinCoreInterface(BlockchainInterface): |
|
|
|
|
|
|
|
|
|
def __init__(self, jsonRpc, network, wallet_name): |
|
|
|
|
|