Browse Source

remove usages of wallet.unspent

master
undeath 8 years ago
parent
commit
705d41d299
  1. 28
      jmclient/jmclient/blockchaininterface.py
  2. 41
      jmclient/jmclient/electruminterface.py
  3. 19
      scripts/add-utxo.py
  4. 49
      scripts/jmtainter.py
  5. 2
      test/ygrunner.py

28
jmclient/jmclient/blockchaininterface.py

@ -51,7 +51,7 @@ class BlockchainInterface(object):
@staticmethod @staticmethod
def get_wallet_name(wallet): def get_wallet_name(wallet):
return 'joinmarket-wallet-' + btc.dbl_sha256(wallet.keys[0][0])[:6] return 'joinmarket-wallet-' + wallet.get_wallet_id()
@abc.abstractmethod @abc.abstractmethod
def sync_addresses(self, wallet): def sync_addresses(self, wallet):
@ -60,8 +60,7 @@ class BlockchainInterface(object):
@abc.abstractmethod @abc.abstractmethod
def sync_unspent(self, wallet): def sync_unspent(self, wallet):
"""Finds the unspent transaction outputs belonging to this wallet, """Finds the unspent transaction outputs belonging to this wallet"""
sets wallet.unspent """
def add_tx_notify(self, txd, unconfirmfun, confirmfun, notifyaddr, def add_tx_notify(self, txd, unconfirmfun, confirmfun, notifyaddr,
wallet_name=None, timeoutfun=None, spentfun=None, txid_flag=True, wallet_name=None, timeoutfun=None, spentfun=None, txid_flag=True,
@ -642,7 +641,7 @@ class BitcoinCoreInterface(BlockchainInterface):
def sync_unspent(self, wallet): def sync_unspent(self, wallet):
st = time.time() st = time.time()
wallet_name = self.get_wallet_name(wallet) wallet_name = self.get_wallet_name(wallet)
wallet.unspent = {} wallet.reset_utxos()
listunspent_args = [] listunspent_args = []
if 'listunspent_args' in jm_single().config.options('POLICY'): if 'listunspent_args' in jm_single().config.options('POLICY'):
@ -655,16 +654,29 @@ class BitcoinCoreInterface(BlockchainInterface):
continue continue
if u['account'] != wallet_name: if u['account'] != wallet_name:
continue continue
# TODO
if u['address'] not in wallet.addr_cache: if u['address'] not in wallet.addr_cache:
continue continue
wallet.unspent[u['txid'] + ':' + str(u['vout'])] = { self._add_unspent_utxo(wallet, u)
'address': u['address'],
'value': int(Decimal(str(u['amount'])) * Decimal('1e8'))
}
et = time.time() et = time.time()
log.debug('bitcoind sync_unspent took ' + str((et - st)) + 'sec') log.debug('bitcoind sync_unspent took ' + str((et - st)) + 'sec')
self.wallet_synced = True self.wallet_synced = True
@staticmethod
def _add_unspent_utxo(wallet, utxo):
"""
Add a UTXO as returned by rpc's listunspent call to the wallet.
params:
wallet: wallet
utxo: single utxo dict as returned by listunspent
"""
txid = binascii.unhexlify(utxo['txid'])
script = binascii.unhexlify(utxo['scriptPubKey'])
value = int(Decimal(str(utxo['amount'])) * Decimal('1e8'))
wallet.add_utxo(txid, int(utxo['vout']), script, value)
def get_deser_from_gettransaction(self, rpcretval): def get_deser_from_gettransaction(self, rpcretval):
"""Get full transaction deserialization from a call """Get full transaction deserialization from a call
to `gettransaction` to `gettransaction`

41
jmclient/jmclient/electruminterface.py

@ -6,11 +6,9 @@ import pprint
import random import random
import socket import socket
import threading import threading
import time
import sys
import ssl import ssl
from twisted.python.log import startLogging import binascii
from twisted.internet.protocol import ClientFactory, Protocol from twisted.internet.protocol import ClientFactory
from twisted.internet.ssl import ClientContextFactory from twisted.internet.ssl import ClientContextFactory
from twisted.protocols.basic import LineReceiver from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor, task, defer from twisted.internet import reactor, task, defer
@ -328,10 +326,10 @@ class ElectrumInterface(BlockchainInterface):
def sync_unspent(self, wallet): def sync_unspent(self, wallet):
# finds utxos in the wallet # finds utxos in the wallet
wallet.unspent = {} wallet.reset_utxos()
#Prepare list of all used addresses #Prepare list of all used addresses
addrs = [] addrs = set()
for m in range(wallet.max_mix_depth): for m in range(wallet.max_mixdepth):
for fc in [0, 1]: for fc in [0, 1]:
branch_list = [] branch_list = []
for k, v in self.temp_addr_history[m][fc].iteritems(): for k, v in self.temp_addr_history[m][fc].iteritems():
@ -339,7 +337,7 @@ class ElectrumInterface(BlockchainInterface):
continue continue
if v["used"]: if v["used"]:
branch_list.append(v["addr"]) branch_list.append(v["addr"])
addrs.extend(branch_list) addrs.update(branch_list)
if len(addrs) == 0: if len(addrs) == 0:
log.debug('no tx used') log.debug('no tx used')
self.wallet_synced = True self.wallet_synced = True
@ -348,21 +346,28 @@ class ElectrumInterface(BlockchainInterface):
return return
#make sure to add any addresses during the run (a subset of those #make sure to add any addresses during the run (a subset of those
#added to the address cache) #added to the address cache)
addrs = list(set(self.wallet.addr_cache.keys()).union(set(addrs))) for md in range(wallet.max_mixdepth):
self.listunspent_calls = 0 for internal in (True, False):
for index in range(wallet.get_next_unused_index(md, internal)):
addrs.add(wallet.get_addr(md, internal, index))
for path in wallet.get_imported_paths(md):
addrs.add(wallet.get_addr_path(path))
self.listunspent_calls = len(addrs)
for a in addrs: for a in addrs:
# FIXME: update to protocol version 1.1 and use scripthash instead
script = wallet.address_to_script(a)
d = self.get_from_electrum('blockchain.address.listunspent', a) d = self.get_from_electrum('blockchain.address.listunspent', a)
d.addCallback(self.process_listunspent_data, wallet, a, len(addrs)) d.addCallback(self.process_listunspent_data, wallet, script)
def process_listunspent_data(self, unspent_info, wallet, address, n): def process_listunspent_data(self, unspent_info, wallet, script):
self.listunspent_calls += 1
res = unspent_info['result'] res = unspent_info['result']
for u in res: for u in res:
wallet.unspent[str(u['tx_hash']) + ':' + str( txid = binascii.unhexlify(u['tx_hash'])
u['tx_pos'])] = {'address': address, 'value': int(u['value'])} wallet.add_utxo(txid, int(u['tx_pos']), script, int(u['value']))
if self.listunspent_calls == n:
for u in wallet.spent_utxos: self.listunspent_calls -= 1
wallet.unspent.pop(u, None) if self.listunspent_calls == 0:
self.wallet_synced = True self.wallet_synced = True
if self.synctype == "sync-only": if self.synctype == "sync-only":
reactor.stop() reactor.stop()

19
scripts/add-utxo.py

@ -9,6 +9,7 @@ the anti-snooping feature employed by makers.
import sys import sys
import os import os
import json import json
import binascii
from pprint import pformat from pprint import pformat
from optparse import OptionParser from optparse import OptionParser
@ -173,20 +174,16 @@ def main():
#Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet, #Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet,
#csv file or json file. #csv file or json file.
if options.loadwallet: if options.loadwallet:
# TODO: new wallet has no unspent attribute
raise NotImplementedError("This is not yet implemented.")
wallet_path = get_wallet_path(options.loadwallet, None) wallet_path = get_wallet_path(options.loadwallet, None)
wallet = open_wallet(wallet_path, gap_limit=options.gaplimit) wallet = open_wallet(wallet_path, gap_limit=options.gaplimit)
sync_wallet(wallet, fast=options.fastsync) sync_wallet(wallet, fast=options.fastsync)
unsp = {}
for u, av in wallet.unspent.iteritems(): for md, utxos in wallet.get_utxos_by_mixdepth_().items():
addr = av['address'] for (txid, index), utxo in utxos.items():
key = wallet.get_key_from_addr(addr) txhex = binascii.hexlify(txid) + ':' + str(index)
wifkey = btc.wif_compressed_privkey(key, vbyte=get_p2pk_vbyte()) wif = wallet.get_wif_path(utxo['path'])
unsp[u] = {'address': av['address'], utxo_data.append((txhex, wif))
'value': av['value'], 'privkey': wifkey}
for u, pva in unsp.iteritems():
utxo_data.append((u, pva['privkey']))
elif options.in_file: elif options.in_file:
with open(options.in_file, "rb") as f: with open(options.in_file, "rb") as f:
utxo_info = f.readlines() utxo_info = f.readlines()

49
scripts/jmtainter.py

@ -103,18 +103,24 @@ def serialize_derivation(roc, i):
return x return x
#======================================================= #=======================================================
def get_privkey_amount_from_utxo(wallet, utxo):
def get_script_amount_from_utxo(wallet, utxo):
"""Given a JM wallet and a utxo string, find """Given a JM wallet and a utxo string, find
the corresponding private key and amount controlled the corresponding private key and amount controlled
in satoshis. in satoshis.
""" """
for k, v in wallet.unspent.iteritems(): for md, utxos in wallet.get_utxos_by_mixdepth_().items():
if k == utxo: for (txid, index), utxo in utxos.items():
print("Found utxo, its value is: ", v['value']) txhex = binascii.hexlify(txid) + ':' + str(index)
return wallet.get_key_from_addr(v['address']), v['value'] if txhex != utxo:
return (None, None) continue
script = wallet.get_script_path(utxo['path'])
print("Found utxo, its value is: {}".format(utxo['value']))
return script, utxo['value']
return None, None
def create_single_acp_pair(utxo_in, priv, addr_out, amount, bump, segwit=False): def create_single_acp_pair(wallet, utxo_in, script, addr_out, amount, bump, segwit=False):
"""Given a utxo and a signing key for it, and its amout in satoshis, """Given a utxo and a signing key for it, and its amout in satoshis,
sign a "transaction" consisting of only 1 input and one output, signed sign a "transaction" consisting of only 1 input and one output, signed
with single|acp sighash flags so it can be grafted into a bigger with single|acp sighash flags so it can be grafted into a bigger
@ -129,10 +135,9 @@ def create_single_acp_pair(utxo_in, priv, addr_out, amount, bump, segwit=False):
assert bump >= 0, "Output of single|acp pair must be bigger than input for safety." assert bump >= 0, "Output of single|acp pair must be bigger than input for safety."
out = {"address": addr_out, "value": amount + bump} out = {"address": addr_out, "value": amount + bump}
tx = btc.mktx([utxo_in], [out]) tx = btc.mktx([utxo_in], [out])
amt = amount if segwit else None return wallet.sign_tx(tx, {0: (script, amount)},
return btc.sign(tx, 0, priv, hashcode=btc.SIGHASH_SINGLE|btc.SIGHASH_ANYONECANPAY)
hashcode=btc.SIGHASH_SINGLE|btc.SIGHASH_ANYONECANPAY,
amount=amt)
def graft_onto_single_acp(wallet, txhex, amount, destaddr): def graft_onto_single_acp(wallet, txhex, amount, destaddr):
"""Given a serialized txhex which is checked to be of """Given a serialized txhex which is checked to be of
@ -187,12 +192,14 @@ def graft_onto_single_acp(wallet, txhex, amount, destaddr):
df['ins'][0]['script'] = d['ins'][0]['script'] df['ins'][0]['script'] = d['ins'][0]['script']
if 'txinwitness' in d['ins'][0]: if 'txinwitness' in d['ins'][0]:
df['ins'][0]['txinwitness'] = d['ins'][0]['txinwitness'] df['ins'][0]['txinwitness'] = d['ins'][0]['txinwitness']
fulltx = btc.serialize(df)
for i, iu in enumerate(input_utxos): for i, iu in enumerate(input_utxos):
priv, inamt = get_privkey_amount_from_utxo(wallet, iu) script, inamt = get_script_amount_from_utxo(wallet, iu)
print("Signing index: ", i+1, " with privkey: ", priv, " and amount: ", inamt, " for utxo: ", iu) print("Signing index: ", i+1, " with script: ", script, " and amount: ", inamt, " for utxo: ", iu)
fulltx = btc.sign(fulltx, i+1, priv, amount=inamt) fulltx = wallet.sign_tx(df, {i: (script, inamt)})
return (True, fulltx)
return True, btc.serialize(fulltx)
if __name__ == "__main__": if __name__ == "__main__":
parser = get_parser() parser = get_parser()
@ -214,22 +221,22 @@ if __name__ == "__main__":
"Use wallet-tool.py method 'showutxos' to select one") "Use wallet-tool.py method 'showutxos' to select one")
exit(0) exit(0)
utxo_in = args[2] utxo_in = args[2]
priv, amount = get_privkey_amount_from_utxo(wallet, utxo_in) script, amount = get_script_amount_from_utxo(wallet, utxo_in)
if not priv: if not script:
print("Failed to find the utxo's private key from the wallet; check " print("Failed to find the utxo's private key from the wallet; check "
"if this utxo is actually contained in the wallet using " "if this utxo is actually contained in the wallet using "
"wallet-tool.py showutxos") "wallet-tool.py showutxos")
exit(0) exit(0)
#destination sourced from wallet #destination sourced from wallet
addr_out = wallet.get_new_addr((options.mixdepth+1)%options.amtmixdepths, 1) addr_out = wallet.get_new_addr((options.mixdepth+1)%options.amtmixdepths, 1)
serialized_single_acp = create_single_acp_pair(utxo_in, priv, addr_out, amount, single_acp = create_single_acp_pair(wallet, utxo_in, script, addr_out, amount,
options.bump, segwit=True) options.bump, segwit=True)
print("Created the following one-in, one-out transaction, which will not " print("Created the following one-in, one-out transaction, which will not "
"be valid to broadcast itself (negative fee). Pass it to your " "be valid to broadcast itself (negative fee). Pass it to your "
"counterparty:") "counterparty:")
print(pformat(btc.deserialize(serialized_single_acp))) print(pformat(single_acp))
print("Pass the following raw hex to your counterparty:") print("Pass the following raw hex to your counterparty:")
print(serialized_single_acp) print(btc.serialize(single_acp))
exit(0) exit(0)
elif args[1] == "take": elif args[1] == "take":
try: try:

2
test/ygrunner.py

@ -110,7 +110,7 @@ def test_start_ygs(setup_ygrunner, num_ygs, wallet_structures, mean_amt,
print("Seed : " + wallets[num_ygs]['seed']) print("Seed : " + wallets[num_ygs]['seed'])
#useful to see the utxos on screen sometimes #useful to see the utxos on screen sometimes
sync_wallet(wallet, fast=True) sync_wallet(wallet, fast=True)
print(wallet.unspent) print(wallet.get_utxos_by_mixdepth())
txfee = 1000 txfee = 1000
cjfee_a = 4200 cjfee_a = 4200
cjfee_r = '0.001' cjfee_r = '0.001'

Loading…
Cancel
Save