Browse Source

Remove jmtainter script, not used or maintained currently

master
AdamISZ 7 years ago
parent
commit
55c51a9a25
No known key found for this signature in database
GPG Key ID: B3AE09F1E9A3197A
  1. 269
      scripts/jmtainter.py

269
scripts/jmtainter.py

@ -1,269 +0,0 @@
#!/usr/bin/env python2
from __future__ import print_function
"""
Tool to create or receive a single input, output pair
signed single|acp to allow the receiver to add further
inputs and outputs and broadcast a full transaction.
Thus the original input can be tainted and the taint
can spread to other outputs included.
This is a tool for Joinmarket wallets specifically.
"""
import binascii
from optparse import OptionParser
from pprint import pformat
import jmbitcoin as btc
from jmclient import (
load_program_config, validate_address, jm_single, WalletError, sync_wallet,
RegtestBitcoinCoreInterface, estimate_tx_fee, get_p2pk_vbyte,
get_p2sh_vbyte, open_test_wallet_maybe, get_wallet_path)
def get_parser():
parser = OptionParser(
usage=
'usage: %prog [options] walletfile make utxo\n' + \
'or: %prog [options] walletfile take amount-in-satoshis destination-addr txhex',
description='Makes a single|acp signed in-out pair for giving to others '
+
'or receives such a pair and creates a full transaction with your utxos. '
+
'Primarily useful for spreading utxo taint.')
parser.add_option(
'-b',
'--bump',
type='int',
dest='bump',
default=10000,
help=
'How much bigger the output is than the input, when making a single|acp in-out pair.')
parser.add_option(
'-m',
'--mixdepth',
type='int',
dest='mixdepth',
help=
'Mixing depth to source utxo from. default=0.',
default=0)
parser.add_option('-a',
'--amtmixdepths',
action='store',
type='int',
dest='amtmixdepths',
help='number of mixdepths in wallet, default 5',
default=5)
parser.add_option('-g',
'--gap-limit',
type="int",
action='store',
dest='gaplimit',
help='gap limit for wallet, default=6',
default=6)
parser.add_option('--fast',
action='store_true',
dest='fastsync',
default=False,
help=('choose to do fast wallet sync, only for Core and '
'only for previously synced wallet'))
return parser
def is_utxo(utxo):
try:
txid, N = utxo.split(":")
assert len(txid) == 64
N = int(N)
except:
return False
return True
def cli_get_wallet(wallet_name, sync=True):
wallet_path = get_wallet_path(wallet_name, None)
wallet = open_test_wallet_maybe(
wallet_path, wallet_name, options.amtmixdepths, gap_limit=options.gaplimit)
if jm_single().config.get("BLOCKCHAIN",
"blockchain_source") == "electrum-server":
jm_single().bc_interface.synctype = "with-script"
if sync:
while not jm_single().bc_interface.wallet_synced:
sync_wallet(wallet, fast=options.fastsync)
return wallet
#======Electrum specific utils=========================
def rev_hex(s):
return s.decode('hex')[::-1].encode('hex')
def int_to_hex(i, length=1):
s = hex(i)[2:].rstrip('L')
s = "0"*(2*length - len(s)) + s
return rev_hex(s)
def serialize_derivation(roc, i):
x = ''.join(map(lambda x: int_to_hex(x, 2), (roc, i)))
print("returning: ", x)
raw_input()
return x
#=======================================================
def get_script_amount_from_utxo(wallet, utxo):
"""Given a JM wallet and a utxo string, find
the corresponding private key and amount controlled
in satoshis.
"""
for md, utxos in wallet.get_utxos_by_mixdepth_().items():
for (txid, index), utxo in utxos.items():
txhex = binascii.hexlify(txid) + ':' + str(index)
if txhex != utxo:
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(wallet, utxo_in, script, addr_out, amount, bump, segwit=False):
"""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
with single|acp sighash flags so it can be grafted into a bigger
transaction.
Also provide a destination address and a 'bump' value (so the creator
can claim more output in the final transaction.
Note that, for safety, bump *should* be positive if the recipient
is untrusted, since otherwise they can waste your money by simply
broadcasting this transaction without adding any inputs of their own.
Returns the serialized 1 in, 1 out, and signed transaction.
"""
assert bump >= 0, "Output of single|acp pair must be bigger than input for safety."
out = {"address": addr_out, "value": amount + bump}
tx = btc.mktx([utxo_in], [out])
return wallet.sign_tx(tx, {0: (script, amount)},
hashcode=btc.SIGHASH_SINGLE|btc.SIGHASH_ANYONECANPAY)
def graft_onto_single_acp(wallet, txhex, amount, destaddr):
"""Given a serialized txhex which is checked to be of
form single|acp (one in, one out), a destination address
and an amount to spend, grafts in this in-out pair (at index zero)
to our own transaction spending amount amount to destination destaddr,
and uses a user-specified transaction fee (normal joinmarket
configuration), and sanity checks that the bump value is not
greater than user specified bump option.
Returned: serialized txhex of fully signed transaction.
"""
d = btc.deserialize(txhex)
if len(d['ins']) != 1 or len(d['outs']) != 1:
return (False, "Proposed tx should have 1 in 1 out, has: " + ','.join(
[str(len(d[x])) for x in ['ins', 'outs']]))
#most important part: check provider hasn't bumped more than options.bump:
other_utxo_in = d['ins'][0]['outpoint']['hash'] + ":" + str(d['ins'][0]['outpoint']['index'])
res = jm_single().bc_interface.query_utxo_set(other_utxo_in)
assert len(res) == 1
if not res[0]:
return (False, "Utxo provided by counterparty not found.")
excess = d['outs'][0]['value'] - res[0]["value"]
if not excess <= options.bump:
return (False, "Counterparty claims too much excess value: " + str(excess))
#Last sanity check - ensure that it's single|acp, else we're wasting our time
try:
if 'txinwitness' in d['ins'][0]:
sig, pub = d['ins'][0]['txinwitness']
else:
sig, pub = btc.deserialize_script(d['ins'][0]['script'])
assert sig[-2:] == "83"
except Exception as e:
return (False, "The transaction's signature does not parse as signed with "
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, for p2pkh or p2sh-p2wpkh, or "
"is otherwise invalid, and so is not valid for this function.\n" + repr(e))
#source inputs for our own chosen spending amount:
try:
input_utxos = wallet.select_utxos(options.mixdepth, amount)
except Exception as e:
return (False, "Unable to select sufficient coins from mixdepth: " + str(options.mixdepth))
total_selected = sum([x['value'] for x in input_utxos.values()])
fee = estimate_tx_fee(len(input_utxos)+1, 3, txtype='p2sh-p2wpkh')
change_amount = total_selected - amount - excess - fee
changeaddr = wallet.get_new_addr(options.mixdepth, 1)
#Build new transaction and, graft in signature
ins = [other_utxo_in] + input_utxos.keys()
outs = [d['outs'][0], {'address': destaddr, 'value': amount},
{'address': changeaddr, 'value': change_amount}]
fulltx = btc.mktx(ins, outs)
df = btc.deserialize(fulltx)
#put back in original signature
df['ins'][0]['script'] = d['ins'][0]['script']
if 'txinwitness' in d['ins'][0]:
df['ins'][0]['txinwitness'] = d['ins'][0]['txinwitness']
for i, iu in enumerate(input_utxos):
script, inamt = get_script_amount_from_utxo(wallet, iu)
print("Signing index: ", i+1, " with script: ", script, " and amount: ", inamt, " for utxo: ", iu)
fulltx = wallet.sign_tx(df, {i: (script, inamt)})
return True, btc.serialize(fulltx)
if __name__ == "__main__":
parser = get_parser()
(options, args) = parser.parse_args()
load_program_config()
if get_wallet_cls() != SegwitWallet:
print("Only segwit wallets are supported; remove any setting of `segwit`"
" in `POLICY` in joinmarket.cfg. Quitting.")
exit(0)
#default args causes wallet sync here:
wallet = cli_get_wallet(args[0])
if args[1] not in ['make', 'take']:
print("Second argument must be 'make' or 'take', see '--help'")
exit(0)
if args[1] == "make":
if len(args) < 3 or not is_utxo(args[2]):
print("You must provide a utxo as third argument; 64 character hex "
"txid, followed by ':', followed by the output index. "
"Use wallet-tool.py method 'showutxos' to select one")
exit(0)
utxo_in = args[2]
script, amount = get_script_amount_from_utxo(wallet, utxo_in)
if not script:
print("Failed to find the utxo's private key from the wallet; check "
"if this utxo is actually contained in the wallet using "
"wallet-tool.py showutxos")
exit(0)
#destination sourced from wallet
addr_out = wallet.get_new_addr((options.mixdepth+1)%options.amtmixdepths, 1)
single_acp = create_single_acp_pair(wallet, utxo_in, script, addr_out, amount,
options.bump, segwit=True)
print("Created the following one-in, one-out transaction, which will not "
"be valid to broadcast itself (negative fee). Pass it to your "
"counterparty:")
print(pformat(single_acp))
print("Pass the following raw hex to your counterparty:")
print(btc.serialize(single_acp))
exit(0)
elif args[1] == "take":
try:
amount, destaddr, txhex = args[2:5]
#sanity check input
amount = int(amount)
assert amount > 0
assert validate_address(destaddr)
binascii.unhexlify(txhex)
except Exception as e:
print("Syntax error, should be 5 arguments, see --help. ", repr(e))
exit(0)
success, complete_tx = graft_onto_single_acp(wallet, txhex, amount, destaddr)
if not success:
print("Quitting, reason: " + complete_tx)
exit(0)
#allow user to decide whether to broadcast:
print("The following transaction has been prepared:")
print(pformat(btc.deserialize(complete_tx)))
broadcast = raw_input("Do you want to broadcast now? (y/n): ")
if broadcast == "y":
success = jm_single().bc_interface.pushtx(complete_tx)
if not success:
print("Failed to broadcast.")
exit(0)
else:
print("You chose not to broadcast.")
exit(0)
print('done')
Loading…
Cancel
Save