def test_issue_225(self): script = tools.compile("OP_RETURN 'foobar'") tx_out = TxOut(1, script) address = tx_out.bitcoin_address(netcode="XTN") self.assertEqual(address, "(nulldata 666f6f626172)") address = tx_out.bitcoin_address(netcode="BTC") self.assertEqual(address, "(nulldata 666f6f626172)")
def make_bare_tx(candidate, change_address, rs_asm, version=1): # <Tx> components spendables = [] ins = [] outs = [] # estimate the final (signed) bytesize per input based on the redeemscript redeem_script = tools.compile(rs_asm) in_size = estimate_input_size(redeem_script) # initialize size and amount counters in_amount = Decimal(0) est_size = TX_COMPONENTS.version + TX_COMPONENTS.out_count + TX_COMPONENTS.in_count # add output size est_size += OUTSIZE * 2 # iterate over unspents for utxo in candidate.utxos: value = Decimal(utxo.amount) * COIN in_amount += value script = h2b(utxo.script) # for now: test if the in_script we figured we would need, actually matches the in script :D # reverse that tx hash prevtx = h2b_rev(utxo.hash) # output index outnum = utxo.outpoint # create "spendable" spdbl = Spendable(value, script, prevtx, outnum) spendables.append(spdbl) # also create this as input as_input = spdbl.tx_in() as_input.sigs = [] ins.append(as_input) # add the estimated size per input est_size += in_size # calc fee and out amount fee = (Decimal(math.ceil(est_size / 1000)) * COIN * NETWORK_FEE) + FEE_MARGIN change_amount = Decimal( math.floor(in_amount - (candidate.amount * COIN) - fee)) # create outputs outs.append( TxOut(int(candidate.amount * COIN), make_payto(candidate.address))) outs.append(TxOut(int(change_amount), make_payto_script(change_address))) # create bare tx without sigs tx = Tx(version, ins, outs, 0, spendables) return tx
def post(self): logging.info("transaction coming in") hextx = self.get_argument('hextx', None) subkeys = self.get_argument('subkeys', None) payoutaddress = self.get_argument('payoutaddress', None) fees = self.get_argument('fees', None) print subkeys if not hextx or not subkeys or not payoutaddress or not fees: logging.error("Did not receive trans or tree argument") return fees = tornado.escape.json_decode(fees) subkeys = tornado.escape.json_decode(subkeys) seed = mnemonic.Mnemonic.to_seed(PHRASE) wallet = BIP32Node.from_master_secret(seed) wifs = [] keys = [] for subkey in subkeys: key = wallet.subkey_for_path(subkey) keys.append(key) wifs.append(key.wif()) underlying_script = ScriptMultisig( n=N, sec_keys=[key.sec() for key in keys[:M]]).script() address = address_for_pay_to_script(underlying_script) tx2 = Tx.tx_from_hex(hextx) # first tx out, need another for the 1% to our wallet script = standard_tx_out_script(payoutaddress) tx_out = TxOut(fees['seller'], script) # TODO: figure out final wallet. This is sending to my phone script = standard_tx_out_script("1LhkvTTxFXam672vjwbABtkp9td7dxCwyB") tx2_out = TxOut(fees['bestcrow'], script) txs_out = [tx_out, tx2_out] tx2.txs_out = txs_out hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys) p2sh_lookup = build_p2sh_lookup([underlying_script]) tx2.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) print tx2.as_hex() self.write(tx2.as_hex())
def sign_tx(certificate_metadata, last_input, allowable_wif_prefixes=None): """sign the transaction with private key""" with open(certificate_metadata.unsigned_tx_file_name, 'rb') as in_file: hextx = str(in_file.read(), 'utf-8') logging.info('Signing tx with private key for recipient id: %s ...', certificate_metadata.uid) tx = Tx.from_hex(hextx) if allowable_wif_prefixes: wif = wif_to_secret_exponent(helpers.import_key(), allowable_wif_prefixes) else: wif = wif_to_secret_exponent(helpers.import_key()) lookup = build_hash160_lookup([wif]) tx.set_unspents([ TxOut(coin_value=last_input.amount, script=last_input.script_pub_key) ]) signed_tx = tx.sign(lookup) signed_hextx = signed_tx.as_hex() with open(certificate_metadata.unsent_tx_file_name, 'wb') as out_file: out_file.write(bytes(signed_hextx, 'utf-8')) logging.info('Finished signing transaction for certificate uid=%s', certificate_metadata.uid)
def create_raw_transaction(self, escrow, fees): logging.info('starting raw transaction to payout address %s' % escrow['sellerpayoutaddress']) # convenience method provided by pycoin to get spendables from insight server insight = InsightService(INSIGHT) spendables = insight.spendables_for_address(escrow['multisigaddress']) # create the tx_in txs_in = [] for s in spendables: txs_in.append(s.tx_in()) script = standard_tx_out_script(escrow['multisigaddress']) tx_out = TxOut(fees['seller'], script) txs_out = [tx_out] tx1 = Tx(version=1, txs_in=txs_in, txs_out=txs_out) tx1.set_unspents(txs_out) # this will be the hex of the tx we're going to send in the POST request hex_tx = tx1.as_hex(include_unspents=True) return hex_tx
def sign_tx(hextx, tx_input, allowable_wif_prefixes=None): """ Sign the transaction with private key :param hextx: :param tx_input: :param allowable_wif_prefixes: :return: """ logging.info('Signing tx with private key') tx = Tx.from_hex(hextx) if allowable_wif_prefixes: wif = wif_to_secret_exponent( helpers.import_key(), allowable_wif_prefixes) else: wif = wif_to_secret_exponent(helpers.import_key()) lookup = build_hash160_lookup([wif]) tx.set_unspents( [TxOut(coin_value=tx_input.amount, script=tx_input.script_pub_key)]) signed_tx = tx.sign(lookup) signed_hextx = signed_tx.as_hex() logging.info('Finished signing transaction') return signed_hextx
def tx_from_json_dict(r): version = r.get("version") lock_time = r.get("locktime") txs_in = [] for vin in r.get("vin"): if "coinbase" in vin: previous_hash = b'\0' * 32 script = h2b(vin.get("coinbase")) previous_index = 4294967295 else: previous_hash = h2b_rev(vin.get("txid")) scriptSig = vin.get("scriptSig") if "hex" in scriptSig: script = h2b(scriptSig.get("hex")) else: script = tools.compile(scriptSig.get("asm")) previous_index = vin.get("vout") sequence = vin.get("sequence") txs_in.append(TxIn(previous_hash, previous_index, script, sequence)) txs_out = [] for vout in r.get("vout"): coin_value = btc_to_satoshi(decimal.Decimal(vout.get("value"))) script = tools.compile(vout.get("scriptPubKey").get("asm")) txs_out.append(TxOut(coin_value, script)) tx = Tx(version, txs_in, txs_out, lock_time) bh = r.get("blockhash") if bh: bh = h2b_rev(bh) tx.confirmation_block_hash = bh return tx
def test_multisig_one_at_a_time(self): N = 3 M = 3 keys = [Key(secret_exponent=i) for i in range(1, M + 2)] tx_in = TxIn.coinbase_tx_in(script=b'') script = ScriptMultisig(n=N, sec_keys=[key.sec() for key in keys[:M]]).script() tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) ids = [ "403e5bfc59e097bb197bf77a692d158dd3a4f7affb4a1fa41072dafe7bec7058", "5931d9995e83721243dca24772d7012afcd4378996a8b953c458175f15a544db", "9bb4421088190bbbb5b42a9eaa9baed7ec7574a407c25f71992ba56ca43d9c44", "03a1dc2a63f93a5cf5a7cb668658eb3fc2eda88c06dc287b85ba3e6aff751771" ] for i in range(1, M + 1): self.assertEqual(tx2.bad_signature_count(), 1) self.assertEqual(tx2.id(), ids[i - 1]) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys[i - 1:i]) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.id(), ids[i]) self.assertEqual(tx2.bad_signature_count(), 0)
def check_bip143_tx(self, tx_u_hex, tx_s_hex, txs_out_value_scripthex_pair, tx_in_count, tx_out_count, version, lock_time): tx_u = Tx.from_hex(tx_u_hex) tx_s = Tx.from_hex(tx_s_hex) txs_out = [ TxOut(int(coin_value * 1e8), h2b(script_hex)) for coin_value, script_hex in txs_out_value_scripthex_pair ] for tx in (tx_u, tx_s): self.assertEqual(len(tx.txs_in), tx_in_count) self.assertEqual(len(tx.txs_out), tx_out_count) self.assertEqual(tx.version, version) self.assertEqual(tx.lock_time, lock_time) tx.set_unspents(txs_out) self.check_unsigned(tx_u) self.check_signed(tx_s) tx_hex = tx_u.as_hex() self.assertEqual(tx_hex, tx_u_hex) tx_hex = tx_s.as_hex() self.assertEqual(tx_hex, tx_s_hex) tx_u_prime = self.unsigned_copy(tx_s) tx_hex = tx_u_prime.as_hex() self.assertEqual(tx_hex, tx_u_hex) self.assertEqual(b2h_rev(double_sha256(h2b(tx_s_hex))), tx_s.w_id()) self.assertEqual(b2h_rev(double_sha256(h2b(tx_u_hex))), tx_u.w_id()) self.assertEqual(b2h_rev(double_sha256(h2b(tx_u_hex))), tx_u.id()) return tx_u, tx_s
def build_spending_tx(script_in_bin, credit_tx): txs_in = [TxIn(credit_tx.hash(), 0, script_in_bin, sequence=4294967295)] txs_out = [TxOut(credit_tx.txs_out[0].coin_value, b'')] spend_tx = Tx(1, txs_in, txs_out, unspents=credit_tx.tx_outs_as_spendable()) return spend_tx
def prepare_tx_for_signing(hex_tx, tx_inputs): logging.info('Preparing tx for signing') transaction = Tx.from_hex(hex_tx) unspents = [ TxOut(coin_value=tx_input.coin_value, script=tx_input.script) for tx_input in tx_inputs ] transaction.set_unspents(unspents) return transaction
def test_nulldata(self): # note that because chr() is used samples with length > 255 will not work for sample in [b'test', b'me', b'a', b'39qEwuwyb2cAX38MFtrNzvq3KV9hSNov3q']: sample_script = b'\x6a' + chr(len(sample)).encode() + sample nd = ScriptNulldata(sample) self.assertEqual(nd.nulldata, sample) self.assertEqual(nd.script(), sample_script) nd2 = ScriptNulldata.from_script(sample_script) self.assertEqual(nd.nulldata, nd2.nulldata) out = TxOut(1, nd.script()) tx = Tx(0, [], [out]) # ensure we can create a tx self.assertEqual(nd.script(), tools.compile(tools.disassemble(nd.script()))) # convert between asm and back to ensure no bugs with compilation
def multisig_M_of_N(self, M, N, unsigned_id, signed_id): keys = [Key(secret_exponent=i) for i in range(1, N+2)] tx_in = TxIn.coinbase_tx_in(script=b'') script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) self.assertEqual(tx2.id(), unsigned_id) self.assertEqual(tx2.bad_signature_count(), 1) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.id(), signed_id) self.assertEqual(tx2.bad_signature_count(), 0)
def multisig_M_of_N_individually(self, M, N): keys = [Key(secret_exponent=i) for i in range(1, N + 2)] tx_in = TxIn.coinbase_tx_in(script=b'') script = ScriptMultisig(n=M, sec_keys=[key.sec() for key in keys[:N]]).script() tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) for partial_key_list in itertools.permutations(keys[:N], M): tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) for key in partial_key_list: self.assertEqual(tx2.bad_signature_count(), 1) hash160_lookup = build_hash160_lookup([key.secret_exponent()]) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.bad_signature_count(), 0)
def main(): if len(sys.argv) != 2: print("usage: %s address" % sys.argv[0]) sys.exit(-1) # validate the address address = sys.argv[1] assert is_address_valid(address) print("creating coinbase transaction to %s" % address) tx_in = TxIn.coinbase_tx_in(script=b'') tx_out = TxOut(50 * 1e8, standard_tx_out_script(address)) tx = Tx(1, [tx_in], [tx_out]) print("Here is the tx as hex:\n%s" % tx.as_hex())
def sign_tx(hex_tx, tx_input): """ Sign the transaction with private key :param hex_tx: :param tx_input: :return: """ logging.info('Signing tx with private key') transaction = Tx.from_hex(hex_tx) wif = wif_to_secret_exponent(helpers.import_key(), ALLOWABLE_WIF_PREFIXES) lookup = build_hash160_lookup([wif]) transaction.set_unspents([TxOut(coin_value=tx_input.coin_value, script=tx_input.script)]) signed_tx = transaction.sign(lookup) logging.info('Finished signing transaction') return signed_tx
def test_sign_pay_to_script_multisig(self): M, N = 3, 3 keys = [Key(secret_exponent=i) for i in range(1, N+2)] tx_in = TxIn.coinbase_tx_in(script=b'') underlying_script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() address = address_for_pay_to_script(underlying_script) self.assertEqual(address, "39qEwuwyb2cAX38MFtrNzvq3KV9hSNov3q") script = standard_tx_out_script(address) tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [address]) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys[:N]) p2sh_lookup = build_p2sh_lookup([underlying_script]) tx2.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) self.assertEqual(tx2.bad_signature_count(), 0)
def test_p2sh_multisig_sequential_signing(self): raw_scripts = [h2b('52210234abcffd2e80ad01c2ec0276ad02682808169c6fafdd25ebfb60703df272b4612102e5baaafff8094e4d77ce8b009d5ebc3de9110085ebd3d96e50cc7ce70faf1752210316ee25e80eb6e6fc734d9c86fa580cbb9c4bfd94a19f0373a22353ececd4db6853ae')] txs_in = [TxIn(previous_hash=h2b('43c95d14724437bccc102ccf86aba1ac02415524fd1aefa787db886bba52a10c'), previous_index=0)] txs_out = [TxOut(10000, standard_tx_out_script('3KeGeLFmsbmbVdeMLrWp7WYKcA3tdsB4AR'))] spendable = {'script_hex': 'a914c4ed4de526461e3efbb79c8b688a6f9282c0464687', 'does_seem_spent': 0, 'block_index_spent': 0, 'tx_hash_hex': '0ca152ba6b88db87a7ef1afd24554102aca1ab86cf2c10ccbc374472145dc943', 'coin_value': 10000, 'block_index_available': 0, 'tx_out_index': 0} tx__prototype = Tx(version=DEFAULT_VERSION, txs_in=txs_in, txs_out=txs_out, unspents=[Spendable.from_dict(spendable)]) key_1, key_2 = 'Kz6pytJCigYHeMsGLmfHQPJhN5og2wpeSVrU43xWwgHLCAvpsprh', 'Kz7NHgX7MBySA3RSKj9GexUSN6NepEDoPNugSPr5absRDoKgn2dT' for ordered_keys in [(key_1, key_2), (key_2, key_1)]: tx = copy.deepcopy(tx__prototype) for key in ordered_keys: self.assertEqual(tx.bad_signature_count(), 1) tx.sign(LazySecretExponentDB([key], {}), p2sh_lookup=build_p2sh_lookup(raw_scripts)) self.assertEqual(tx.bad_signature_count(), 0)
def tx(self, payables, change_address=None): """ Construct a transaction with available spendables :param list[(str, int)] payables: tuple of address and amount :return Tx or None: the transaction or None if not enough balance """ all_spendables = self.spendables() send_amount = 0 for address, coin_value in payables: send_amount += coin_value txs_out = [] for address, coin_value in payables: script = standard_tx_out_script(address) txs_out.append(TxOut(coin_value, script)) total = 0 txs_in = [] spendables = [] while total < send_amount and all_spendables: spend = all_spendables.pop(0) self.add_spend(spend, spendables, txs_in) total += spend.coin_value tx_for_fee = Tx(txs_in=txs_in, txs_out=txs_out, version=DEFAULT_VERSION, unspents=spendables) fee = recommended_fee_for_tx(tx_for_fee) while total < send_amount + fee and all_spendables: spend = all_spendables.pop(0) self.add_spend(spend, spendables, txs_in) total += spend.coin_value if total > send_amount + fee + DUST: addr = change_address or self.current_change_address() script = standard_tx_out_script(addr) txs_out.append(AccountTxOut(total - send_amount - fee, script, self.path_for_check(addr))) elif total < send_amount + fee: raise InsufficientBalanceException(total) # check total >= amount + fee tx = AccountTx(version=DEFAULT_VERSION, txs_in=txs_in, txs_out=txs_out, unspents=spendables) return tx
def test_sign_bitcoind_partially_signed_2_of_2(self): # Finish signing a 2 of 2 transaction, that already has one signature signed by bitcoind # This tx can be found on testnet3 blockchain, txid: 9618820d7037d2f32db798c92665231cd4599326f5bd99cb59d0b723be2a13a2 raw_script = "522103e33b41f5ed67a77d4c4c54b3e946bd30d15b8f66e42cb29fde059c16885116552102b92cb20a9fb1eb9656a74eeb7387636cf64cdf502ff50511830328c1b479986452ae" p2sh_lookup = build_p2sh_lookup([h2b(raw_script)]) partially_signed_raw_tx = "010000000196238f11a5fd3ceef4efd5a186a7e6b9217d900418e72aca917cd6a6e634e74100000000910047304402201b41b471d9dd93cf97eed7cfc39a5767a546f6bfbf3e0c91ff9ad23ab9770f1f02205ce565666271d055be1f25a7e52e34cbf659f6c70770ff59bd783a6fcd1be3dd0147522103e33b41f5ed67a77d4c4c54b3e946bd30d15b8f66e42cb29fde059c16885116552102b92cb20a9fb1eb9656a74eeb7387636cf64cdf502ff50511830328c1b479986452aeffffffff01a0bb0d00000000001976a9143b3beefd6f7802fa8706983a76a51467bfa36f8b88ac00000000" tx = Tx.from_hex(partially_signed_raw_tx) tx_out = TxOut(1000000, h2b("a914a10dfa21ee8c33b028b92562f6fe04e60563d3c087")) tx.set_unspents([tx_out]) key = Key.from_text( "cThRBRu2jAeshWL3sH3qbqdq9f4jDiDbd1SVz4qjTZD2xL1pdbsx") hash160_lookup = build_hash160_lookup([key.secret_exponent()]) self.assertEqual(tx.bad_signature_count(), 1) tx.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) self.assertEqual(tx.bad_signature_count(), 0) self.assertEqual( tx.id(), "9618820d7037d2f32db798c92665231cd4599326f5bd99cb59d0b723be2a13a2")
def json_to_tx(json_text): # transactions with non-standard lock time are not decoded properly # for example, d1ef46055a84fd02ee82580d691064780def18614d98646371c3448ca20019ac # there is no way to fix this until biteasy add a lock_time field to their output d = json.loads(json_text).get("data") txs_in = [] for d1 in d.get("inputs"): previous_hash = h2b_rev(d1.get("outpoint_hash")) previous_index = d1.get("outpoint_index") script = h2b(d1.get("script_sig")) sequence = 4294967295 # BRAIN DAMAGE txs_in.append(TxIn(previous_hash, previous_index, script, sequence)) txs_out = [] for d1 in d.get("outputs"): coin_value = d1.get("value") script = h2b(d1.get("script_pub_key")) txs_out.append(TxOut(coin_value, script)) version = d.get("version") lock_time = 0 # BRAIN DAMAGE return Tx(version, txs_in, txs_out, lock_time)
def parse_with_paths(class_, f, input_chain_paths, output_chain_paths): """Parse a Bitcoin transaction AccountTx from the file-like object f, attaching relevant chain paths.""" version, count = parse_struct("LI", f) txs_in = [] for i in range(count): txin = AccountTxIn.parse(f) txin.path = input_chain_paths[i] txs_in.append(txin) count, = parse_struct("I", f) txs_out = [] for i in range(count): path = output_chain_paths[i] if path: txout = AccountTxOut.parse(f) txout.path = path else: txout = TxOut.parse(f) txs_out.append(txout) lock_time, = parse_struct("L", f) return class_(version, txs_in, txs_out, lock_time)
def get_unsigned_tx(parser): args = parser.parse_args() # if there is only one item passed, it's assumed to be hex if len(args.txinfo) == 1: try: s = io.BytesIO(binascii.unhexlify(args.txinfo[0].decode("utf8"))) return UnsignedTx.parse(s) except Exception: parser.error("can't parse %s as hex\n" % args.txinfo[0]) coins_from = [] coins_to = [] for txinfo in args.txinfo: if '/' in txinfo: parts = txinfo.split("/") if len(parts) == 2: # we assume it's an output address, amount = parts amount = btc_to_satoshi(amount) coins_to.append((amount, address)) else: try: # we assume it's an input of the form # tx_hash_hex/tx_output_index_decimal/tx_out_val/tx_out_script_hex tx_hash_hex, tx_output_index_decimal, tx_out_val, tx_out_script_hex = parts tx_hash = binascii.unhexlify(tx_hash_hex) tx_output_index = int(tx_output_index_decimal) tx_out_val = btc_to_satoshi(decimal.Decimal(tx_out_val)) tx_out_script = binascii.unhexlify(tx_out_script_hex) tx_out = TxOut(tx_out_val, tx_out_script) coins_source = (tx_hash, tx_output_index, tx_out) coins_from.append(coins_source) except Exception: parser.error("can't parse %s\n" % txinfo) else: print("looking up funds for %s from blockchain.info" % txinfo) coins_sources = blockchain_info.unspent_tx_outs_info_for_address(txinfo) coins_from.extend(coins_sources) unsigned_tx = UnsignedTx.standard_tx(coins_from, coins_to) return unsigned_tx
def construct_data_tx(data, _from, unused_utxo): coins_from = spendables_for_address(_from, unused_utxo) #Ahora if len(coins_from) < 1: ErrorNotification.new('No bitcoins available to spend') return 'No bitcoins available to spend' txs_in = [ TxIn(coins_from[0].tx_hash, coins_from[0].tx_out_index, coins_from[0].script) ] script_text = 'OP_RETURN %s' % data.encode('hex') script_bin = tools.compile(script_text) new_txs_out = [TxOut(0, script_bin)] version = 1 lock_time = 0 unsigned_tx = Tx(version, txs_in, new_txs_out, lock_time, coins_from) return unsigned_tx
def sign_tx(self, hex_tx, tx_input, netcode): """ Sign the transaction with private key :param hex_tx: :param tx_input: :param netcode: :return: """ logging.info('Signing tx with private key') self.secret_manager.start() wif = self.secret_manager.get_wif() transaction = Tx.from_hex(hex_tx) allowable_wif_prefixes = wif_prefix_for_netcode(netcode) se = wif_to_secret_exponent(wif, allowable_wif_prefixes) lookup = build_hash160_lookup([se]) transaction.set_unspents([TxOut(coin_value=tx_input.coin_value, script=tx_input.script)]) signed_tx = transaction.sign(lookup) self.secret_manager.stop() logging.info('Finished signing transaction') return signed_tx
def construct_data_tx(data, _from): # inputs coins_from = blockchain_info.coin_sources_for_address(_from) if len(coins_from) < 1: return "No free outputs to spend" max_coin_value, _, max_idx, max_h, max_script = max( (tx_out.coin_value, random(), idx, h, tx_out.script) for h, idx, tx_out in coins_from) unsigned_txs_out = [ UnsignedTxOut(max_h, max_idx, max_coin_value, max_script) ] # outputs if max_coin_value > TX_FEES * 2: return 'max output greater than twice the threshold, too big.' if max_coin_value < TX_FEES: return 'max output smaller than threshold, too small.' script_text = 'OP_RETURN %s' % data.encode('hex') script_bin = tools.compile(script_text) new_txs_out = [TxOut(0, script_bin)] version = 1 lock_time = 0 unsigned_tx = UnsignedTx(version, unsigned_txs_out, new_txs_out, lock_time) return unsigned_tx
def make_bare_tx(network, from_address, to_address, redeem_script, version=1): # <Tx> components spendables = [] ins = [] outs = [] # estimate the final (signed) bytesize per input based on the redeemscript in_size = estimate_input_size(redeem_script) # initialize size and amount counters in_amount = Decimal(0) est_size = TX_COMPONENTS.version + TX_COMPONENTS.out_count + TX_COMPONENTS.in_count # add output size (we"ll only have 1) est_size += TX_COMPONENTS.out_scriptlen + TX_COMPONENTS.out_scriptsize + TX_COMPONENTS.out_scriptlen unspent_response = sochain_get_unspents(network, from_address) unspents = unspent_response.get("txs", []) # iterate over unspents for tx in unspents: value = Decimal(tx.get("value")) * Decimal(1e8) in_amount += value script = h2b(tx.get("script_hex")) # for now: test if the in_script we figured we would need, actually matches the in script :D # reverse that tx hash txhex = tx.get("txid") prevtx = h2b_rev(txhex) # output index outnum = tx.get("output_no") # create "spendable" spdbl = Spendable(value, script, prevtx, outnum) spendables.append(spdbl) # also create this as input as_input = spdbl.tx_in() as_input.sigs = [] ins.append(as_input) # add the estimated size per input est_size += in_size # calc fee and out amount fee = Decimal(math.ceil( est_size / 1000.0)) * Decimal(1e8) * NETWORK_FEES.get(network) out_amount = in_amount - fee if (is_p2sh(to_address)): outscript = make_payto_script(to_address) else: outscript = make_payto_address(to_address) # create output outs.append(TxOut(out_amount, outscript)) # create bare tx without sigs tx = Tx(version, ins, outs, 0, spendables) return tx
underlying_script = ScriptMultisig(n=N, sec_keys=[key.sec() for key in keys[:M]]).script() # I hash the script and transform it into a p2sh address address = address_for_pay_to_script(underlying_script) print(address) # Filling up the new created address with the fake coinbase transaction. No signature rquired. # Very important part. When you move funds to a p2sh address you write a special scriptPubKey: # Instead of: OP_DUP OP_HASH160 <PubkeyHash> OP_EQUALVERIFY OP_CHECKSIG # your p2sh scriptPubKey will be: # OP_HASH160 <hash(redeemScript)> OP_EQUAL # standard_tx_out_script(address) gives the scriptPubKey for a given multisig address script = standard_tx_out_script(address) # Fake coinbase transaction to fill our p2sh address # It it is a coinbase transaction we put in a newly constructed block. tx_in = TxIn.coinbase_tx_in(script=b'') print("TxIn: %s" % tx_in.__str__()) tx_out = TxOut(1000000, script) print("TxOut: %s" % tx_out.__str__()) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx1.as_hex() # we now have an UTXO redeemable by supplying the script and the required sigs. # tx_utils.create_tx() allows to spend all the UTXO from the preavious tx to an arbitrary address. tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) # to split the input in each of the generated addresses # tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[i].address() for i in range(len(keys))]) print("unsigned transaction:") print("bad signatures: %s" % tx2.bad_signature_count()) print(tx2.as_hex()) for i in range(1, N+1): print("signining with key number: %s" % i) hash160_lookup = build_hash160_lookup([keys[i].secret_exponent()]) p2sh_lookup = build_p2sh_lookup([underlying_script])
def build_credit_tx(script_out_bin, coin_value=0): txs_in = [TxIn(b'\0' * 32, 4294967295, b'\0\0', sequence=4294967295)] txs_out = [TxOut(coin_value, script_out_bin)] return Tx(1, txs_in, txs_out)
def main(): parser = argparse.ArgumentParser( description="Manipulate bitcoin (or alt coin) transactions.", epilog=EPILOG) parser.add_argument('-t', "--transaction-version", type=int, help='Transaction version, either 1 (default) or 3 (not yet supported).') parser.add_argument('-l', "--lock-time", type=parse_locktime, help='Lock time; either a block' 'index, or a date/time (example: "2014-01-01T15:00:00"') parser.add_argument('-n', "--network", default="BTC", help='Define network code (M=Bitcoin mainnet, T=Bitcoin testnet).') parser.add_argument('-a', "--augment", action='store_true', help='augment tx by adding any missing spendable metadata by fetching' ' inputs from cache and/or web services') parser.add_argument("-i", "--fetch-spendables", metavar="address", action="append", help='Add all unspent spendables for the given bitcoin address. This information' ' is fetched from web services.') parser.add_argument('-f', "--private-key-file", metavar="path-to-private-keys", action="append", help='file containing WIF or BIP0032 private keys. If file name ends with .gpg, ' '"gpg -d" will be invoked automatically. File is read one line at a time, and if ' 'the file contains only one WIF per line, it will also be scanned for a bitcoin ' 'address, and any addresses found will be assumed to be public keys for the given' ' private key.', type=argparse.FileType('r')) parser.add_argument('-g', "--gpg-argument", help='argument to pass to gpg (besides -d).', default='') parser.add_argument("--remove-tx-in", metavar="tx_in_index_to_delete", action="append", type=int, help='remove a tx_in') parser.add_argument("--remove-tx-out", metavar="tx_out_index_to_delete", action="append", type=int, help='remove a tx_out') parser.add_argument('-F', "--fee", help='fee, in satoshis, to pay on transaction, or ' '"standard" to auto-calculate. This is only useful if the "split pool" ' 'is used; otherwise, the fee is automatically set to the unclaimed funds.', default="standard", metavar="transaction-fee", type=parse_fee) parser.add_argument('-C', "--cache", help='force the resultant transaction into the transaction cache.' ' Mostly for testing.', action='store_true'), parser.add_argument('-u', "--show-unspents", action='store_true', help='show TxOut items for this transaction in Spendable form.') parser.add_argument('-b', "--bitcoind-url", help='URL to bitcoind instance to validate against (http://user:pass@host:port).') parser.add_argument('-o', "--output-file", metavar="path-to-output-file", type=argparse.FileType('wb'), help='file to write transaction to. This supresses most other output.') parser.add_argument("argument", nargs="+", help='generic argument: can be a hex transaction id ' '(exactly 64 characters) to be fetched from cache or a web service;' ' a transaction as a hex string; a path name to a transaction to be loaded;' ' a spendable 4-tuple of the form tx_id/tx_out_idx/script_hex/satoshi_count ' 'to be added to TxIn list; an address/satoshi_count to be added to the TxOut ' 'list; an address to be added to the TxOut list and placed in the "split' ' pool".') args = parser.parse_args() # defaults txs = [] spendables = [] payables = [] key_iters = [] TX_ID_RE = re.compile(r"^[0-9a-fA-F]{64}$") # there are a few warnings we might optionally print out, but only if # they are relevant. We don't want to print them out multiple times, so we # collect them here and print them at the end if they ever kick in. warning_tx_cache = None warning_get_tx = None warning_spendables = None if args.private_key_file: wif_re = re.compile(r"[1-9a-km-zA-LMNP-Z]{51,111}") # address_re = re.compile(r"[1-9a-kmnp-zA-KMNP-Z]{27-31}") for f in args.private_key_file: if f.name.endswith(".gpg"): gpg_args = ["gpg", "-d"] if args.gpg_argument: gpg_args.extend(args.gpg_argument.split()) gpg_args.append(f.name) popen = subprocess.Popen(gpg_args, stdout=subprocess.PIPE) f = popen.stdout for line in f.readlines(): # decode if isinstance(line, bytes): line = line.decode("utf8") # look for WIFs possible_keys = wif_re.findall(line) def make_key(x): try: return Key.from_text(x) except Exception: return None keys = [make_key(x) for x in possible_keys] for key in keys: if key: key_iters.append((k.wif() for k in key.subkeys(""))) # if len(keys) == 1 and key.hierarchical_wallet() is None: # # we have exactly 1 WIF. Let's look for an address # potential_addresses = address_re.findall(line) # we create the tx_db lazily tx_db = None for arg in args.argument: # hex transaction id if TX_ID_RE.match(arg): if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() tx = tx_db.get(h2b_rev(arg)) if not tx: for m in [warning_tx_cache, warning_get_tx, warning_spendables]: if m: print("warning: %s" % m, file=sys.stderr) parser.error("can't find Tx with id %s" % arg) txs.append(tx) continue # hex transaction data try: tx = Tx.tx_from_hex(arg) txs.append(tx) continue except Exception: pass try: key = Key.from_text(arg) # TODO: check network if key.wif() is None: payables.append((key.address(), 0)) continue # TODO: support paths to subkeys key_iters.append((k.wif() for k in key.subkeys(""))) continue except Exception: pass if os.path.exists(arg): try: with open(arg, "rb") as f: if f.name.endswith("hex"): f = io.BytesIO(codecs.getreader("hex_codec")(f).read()) tx = Tx.parse(f) txs.append(tx) try: tx.parse_unspents(f) except Exception as ex: pass continue except Exception: pass parts = arg.split("/") if len(parts) == 4: # spendable try: spendables.append(Spendable.from_text(arg)) continue except Exception: pass # TODO: fix allowable_prefixes allowable_prefixes = b'\0' if len(parts) == 2 and encoding.is_valid_bitcoin_address( parts[0], allowable_prefixes=allowable_prefixes): try: payables.append(parts) continue except ValueError: pass parser.error("can't parse %s" % arg) if args.fetch_spendables: warning_spendables = message_about_spendables_for_address_env() for address in args.fetch_spendables: spendables.extend(spendables_for_address(address)) for tx in txs: if tx.missing_unspents() and args.augment: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() tx.unspents_from_db(tx_db, ignore_missing=True) txs_in = [] txs_out = [] unspents = [] # we use a clever trick here to keep each tx_in corresponding with its tx_out for tx in txs: smaller = min(len(tx.txs_in), len(tx.txs_out)) txs_in.extend(tx.txs_in[:smaller]) txs_out.extend(tx.txs_out[:smaller]) unspents.extend(tx.unspents[:smaller]) for tx in txs: smaller = min(len(tx.txs_in), len(tx.txs_out)) txs_in.extend(tx.txs_in[smaller:]) txs_out.extend(tx.txs_out[smaller:]) unspents.extend(tx.unspents[smaller:]) for spendable in spendables: txs_in.append(spendable.tx_in()) unspents.append(spendable) for address, coin_value in payables: script = standard_tx_out_script(address) txs_out.append(TxOut(coin_value, script)) lock_time = args.lock_time version = args.transaction_version # if no lock_time is explicitly set, inherit from the first tx or use default if lock_time is None: if txs: lock_time = txs[0].lock_time else: lock_time = DEFAULT_LOCK_TIME # if no version is explicitly set, inherit from the first tx or use default if version is None: if txs: version = txs[0].version else: version = DEFAULT_VERSION if args.remove_tx_in: s = set(args.remove_tx_in) txs_in = [tx_in for idx, tx_in in enumerate(txs_in) if idx not in s] if args.remove_tx_out: s = set(args.remove_tx_out) txs_out = [tx_out for idx, tx_out in enumerate(txs_out) if idx not in s] tx = Tx(txs_in=txs_in, txs_out=txs_out, lock_time=lock_time, version=version, unspents=unspents) fee = args.fee try: distribute_from_split_pool(tx, fee) except ValueError as ex: print("warning: %s" % ex.args[0], file=sys.stderr) unsigned_before = tx.bad_signature_count() if unsigned_before > 0 and key_iters: def wif_iter(iters): while len(iters) > 0: for idx, iter in enumerate(iters): try: wif = next(iter) yield wif except StopIteration: iters = iters[:idx] + iters[idx+1:] break print("signing...", file=sys.stderr) sign_tx(tx, wif_iter(key_iters)) unsigned_after = tx.bad_signature_count() if unsigned_after > 0 and key_iters: print("warning: %d TxIn items still unsigned" % unsigned_after, file=sys.stderr) if len(tx.txs_in) == 0: print("warning: transaction has no inputs", file=sys.stderr) if len(tx.txs_out) == 0: print("warning: transaction has no outputs", file=sys.stderr) include_unspents = (unsigned_after > 0) tx_as_hex = tx.as_hex(include_unspents=include_unspents) if args.output_file: f = args.output_file if f.name.endswith(".hex"): f.write(tx_as_hex.encode("utf8")) else: tx.stream(f) if include_unspents: tx.stream_unspents(f) f.close() elif args.show_unspents: for spendable in tx.tx_outs_as_spendable(): print(spendable.as_text()) else: if not tx.missing_unspents(): check_fees(tx) dump_tx(tx, args.network) if include_unspents: print("including unspents in hex dump since transaction not fully signed") print(tx_as_hex) if args.cache: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() tx_db.put(tx) if args.bitcoind_url: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() validate_bitcoind(tx, tx_db, args.bitcoind_url) if tx.missing_unspents(): print("\n** can't validate transaction as source transactions missing", file=sys.stderr) else: try: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_get_tx = message_about_get_tx_env() tx_db = get_tx_db() tx.validate_unspents(tx_db) print('all incoming transaction values validated') except BadSpendableError as ex: print("\n**** ERROR: FEES INCORRECTLY STATED: %s" % ex.args[0], file=sys.stderr) except Exception as ex: print("\n*** can't validate source transactions as untampered: %s" % ex.args[0], file=sys.stderr) # print warnings for m in [warning_tx_cache, warning_get_tx, warning_spendables]: if m: print("warning: %s" % m, file=sys.stderr)
def _test_sighash_single(self, netcode): k0 = Key(secret_exponent=PRIV_KEYS[0], is_compressed=True, netcode=netcode) k1 = Key(secret_exponent=PRIV_KEYS[1], is_compressed=True, netcode=netcode) k2 = Key(secret_exponent=PRIV_KEYS[2], is_compressed=True, netcode=netcode) k3 = Key(secret_exponent=PRIV_KEYS[3], is_compressed=True, netcode=netcode) k4 = Key(secret_exponent=PRIV_KEYS[4], is_compressed=True, netcode=netcode) k5 = Key(secret_exponent=PRIV_KEYS[5], is_compressed=True, netcode=netcode) # Fake a coinbase transaction coinbase_tx = Tx.coinbase_tx(k0.sec(), 500000000) coinbase_tx.txs_out.append( TxOut(1000000000, pycoin_compile('%s OP_CHECKSIG' % b2h(k1.sec())))) coinbase_tx.txs_out.append( TxOut(1000000000, pycoin_compile('%s OP_CHECKSIG' % b2h(k2.sec())))) self.assertEqual( '2acbe1006f7168bad538b477f7844e53de3a31ffddfcfc4c6625276dd714155a', b2h_rev(coinbase_tx.hash())) # Make the test transaction txs_in = [ TxIn(coinbase_tx.hash(), 0), TxIn(coinbase_tx.hash(), 1), TxIn(coinbase_tx.hash(), 2), ] txs_out = [ TxOut(900000000, standard_tx_out_script(k3.address())), TxOut(800000000, standard_tx_out_script(k4.address())), TxOut(800000000, standard_tx_out_script(k5.address())), ] tx = Tx(1, txs_in, txs_out) tx.set_unspents(coinbase_tx.txs_out) self.assertEqual( '791b98ef0a3ac87584fe273bc65abd89821569fd7c83538ac0625a8ca85ba587', b2h_rev(tx.hash())) sig_type = SIGHASH_SINGLE sig_hash = tx.signature_hash(coinbase_tx.txs_out[0].script, 0, sig_type) self.assertEqual( 'cc52d785a3b4133504d1af9e60cd71ca422609cb41df3a08bbb466b2a98a885e', b2h(to_bytes_32(sig_hash))) sig = sigmake(k0, sig_hash, sig_type) self.assertTrue(sigcheck(k0, sig_hash, sig[:-1])) tx.txs_in[0].script = pycoin_compile(b2h(sig)) self.assertTrue(tx.is_signature_ok(0)) sig_hash = tx.signature_hash(coinbase_tx.txs_out[1].script, 1, sig_type) self.assertEqual( '93bb883d70fccfba9b8aa2028567aca8357937c65af7f6f5ccc6993fd7735fb7', b2h(to_bytes_32(sig_hash))) sig = sigmake(k1, sig_hash, sig_type) self.assertTrue(sigcheck(k1, sig_hash, sig[:-1])) tx.txs_in[1].script = pycoin_compile(b2h(sig)) self.assertTrue(tx.is_signature_ok(1)) sig_hash = tx.signature_hash(coinbase_tx.txs_out[2].script, 2, sig_type) self.assertEqual( '53ef7f67c3541bffcf4e0d06c003c6014e2aa1fb38ff33240b3e1c1f3f8e2a35', b2h(to_bytes_32(sig_hash))) sig = sigmake(k2, sig_hash, sig_type) self.assertTrue(sigcheck(k2, sig_hash, sig[:-1])) tx.txs_in[2].script = pycoin_compile(b2h(sig)) self.assertTrue(tx.is_signature_ok(2)) sig_type = SIGHASH_SINGLE | SIGHASH_ANYONECANPAY sig_hash = tx.signature_hash(coinbase_tx.txs_out[0].script, 0, sig_type) self.assertEqual( '2003393d246a7f136692ce7ab819c6eadc54ffea38eb4377ac75d7d461144e75', b2h(to_bytes_32(sig_hash))) sig = sigmake(k0, sig_hash, sig_type) self.assertTrue(sigcheck(k0, sig_hash, sig[:-1])) tx.txs_in[0].script = pycoin_compile(b2h(sig)) self.assertTrue(tx.is_signature_ok(0)) sig_hash = tx.signature_hash(coinbase_tx.txs_out[1].script, 1, sig_type) self.assertEqual( 'e3f469ac88e9f35e8eff0bd8ad4ad3bf899c80eb7645947d60860de4a08a35df', b2h(to_bytes_32(sig_hash))) sig = sigmake(k1, sig_hash, sig_type) self.assertTrue(sigcheck(k1, sig_hash, sig[:-1])) tx.txs_in[1].script = pycoin_compile(b2h(sig)) self.assertTrue(tx.is_signature_ok(1)) sig_hash = tx.signature_hash(coinbase_tx.txs_out[2].script, 2, sig_type) self.assertEqual( 'bacd7c3ab79cad71807312677c1788ad9565bf3c00ab9a153d206494fb8b7e6a', b2h(to_bytes_32(sig_hash))) sig = sigmake(k2, sig_hash, sig_type) self.assertTrue(sigcheck(k2, sig_hash, sig[:-1])) tx.txs_in[2].script = pycoin_compile(b2h(sig)) self.assertTrue(tx.is_signature_ok(2))