From 55c51a9a252f0de9546aa651a00210a191f993d1 Mon Sep 17 00:00:00 2001 From: AdamISZ Date: Sun, 19 Aug 2018 13:15:07 +0200 Subject: [PATCH] Remove jmtainter script, not used or maintained currently --- scripts/jmtainter.py | 269 ------------------------------------------- 1 file changed, 269 deletions(-) delete mode 100644 scripts/jmtainter.py diff --git a/scripts/jmtainter.py b/scripts/jmtainter.py deleted file mode 100644 index 253121d..0000000 --- a/scripts/jmtainter.py +++ /dev/null @@ -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')