def submit_opreturn(rpc_connection, address, data): from bitcoin.core import CTxIn, CMutableTxOut, CScript, CMutableTransaction, COIN, CENT, b2x, b2lx from bitcoin.core.script import OP_CHECKSIG, OP_RETURN txouts = [] unspent = sorted([y for y in rpc_connection.listunspent(0) if str(y['address']) == address], key=lambda x: hash(x['amount'])) txins = [CTxIn(unspent[-1]['outpoint'])] value_in = unspent[-1]['amount'] change_pubkey = rpc_connection.validateaddress(address)['pubkey'] change_out = CMutableTxOut(int(value_in - 2*CENT), CScript([change_pubkey, OP_CHECKSIG])) digest_outs = [CMutableTxOut(CENT, CScript([OP_RETURN, data]))] txouts = [change_out] + digest_outs tx = CMutableTransaction(txins, txouts) print tx.serialize().encode('hex') r = rpc_connection.signrawtransaction(tx) assert r['complete'] tx = r['tx'] #print b2x(tx.serialize()) #print len(tx.serialize()), 'bytes' print(b2lx(rpc_connection.sendrawtransaction(tx)))
def test_emergency_txout(bitcoind): """Test mostly the emergency tx locktime""" amount = Decimal("50") - Decimal("500") / Decimal(COIN) privkeys = [CKey(os.urandom(32)) for _ in range(4)] pubkeys = [k.pub for k in privkeys] txo = emergency_txout(pubkeys, COIN * amount) addr = str(CBitcoinAddress.from_scriptPubKey(txo.scriptPubKey)) # This makes a transaction with only one vout txid = bitcoind.pay_to(addr, amount) new_amount = amount - Decimal("500") / Decimal(COIN) addr = bitcoind.getnewaddress() txin = CTxIn(COutPoint(lx(txid), 0), nSequence=4464) txout = CTxOut(new_amount * COIN, CBitcoinAddress(addr).to_scriptPubKey()) tx = CMutableTransaction([txin], [txout], nVersion=2) tx_hash = SignatureHash(emergency_script(pubkeys), tx, 0, SIGHASH_ALL, int(amount * COIN), SIGVERSION_WITNESS_V0) sigs = [k.sign(tx_hash) + bytes([SIGHASH_ALL]) for k in privkeys] witness_script = [bytes(0), *sigs, emergency_script(pubkeys)] tx.wit = CTxWitness([CTxInWitness(CScriptWitness(witness_script))]) # 1 month of locktime bitcoind.generate_block(4464 - 2) with pytest.raises(VerifyRejectedError, match="non-BIP68-final"): bitcoind.send_tx(tx.serialize().hex()) bitcoind.generate_block(1) bitcoind.send_tx(tx.serialize().hex()) assert bitcoind.has_utxo(addr)
def main(): proxy = bitcoin.rpc.Proxy() assert len(sys.argv) > 1 digests = [] for f in sys.argv[1:]: try: with open(f, 'rb') as fd: digests.append(Hash(fd.read())) except FileNotFoundError as exp: if len(f)/2 in (20, 32): digests.append(x(f)) else: raise exp except IOError as exp: print(exp, file=sys.stderr) continue for digest in digests: unspent = sorted(proxy.listunspent(0), key=lambda _x: hash(_x['amount'])) txins = [CTxIn(unspent[-1]['outpoint'])] value_in = unspent[-1]['amount'] change_addr = proxy.getnewaddress() change_pubkey = proxy.validateaddress(change_addr)['pubkey'] change_out = CMutableTxOut(params.MAX_MONEY, CScript([change_pubkey, OP_CHECKSIG])) digest_outs = [CMutableTxOut(0, CScript([OP_RETURN, digest]))] txouts = [change_out] + digest_outs tx = CMutableTransaction(txins, txouts) FEE_PER_BYTE = 0.00025*COIN/1000 while True: tx.vout[0].nValue = int(value_in - max(len(tx.serialize()) * FEE_PER_BYTE, 0.00011*COIN)) r = proxy.signrawtransaction(tx) assert r['complete'] tx = r['tx'] if value_in - tx.vout[0].nValue >= len(tx.serialize()) * FEE_PER_BYTE: print(b2x(tx.serialize())) print(len(tx.serialize()), 'bytes', file=sys.stderr) print(b2lx(proxy.sendrawtransaction(tx))) break
def as_tx(self): sum_in = sum(prevtx.nValue for _, prevtx, _ in self.prevouts) sig_size = sum(redeemer.spendbytes for _, _, redeemer in self.prevouts) tx_size = ( 4 + # version field 2 + # # of txins len(self.prevouts) * 41 + # txins, excluding sigs sig_size + # txins, sigs only 1 + # # of txouts 34 + # txout 4 # nLockTime field ) feerate = int(self.proxy._call('estimatefee', 1) * COIN) # satoshi's per KB if feerate <= 0: feerate = 10000 fees = int(tx_size * feerate / 1000) tx = CMutableTransaction( [CTxIn(outpoint, nSequence=0) for outpoint, _, _ in self.prevouts], [CTxOut(sum_in - fees, self.payto.to_scriptPubKey())], 0) for n, (_, _, redeemer) in enumerate(self.prevouts): redeemer.mutate_spend(tx, n) unsigned_tx = CTransaction.from_tx(tx) for n, (_, _, redeemer) in enumerate(self.prevouts): txin = CMutableTxIn.from_txin(tx.vin[n]) txin.scriptSig = redeemer.sign_spend(unsigned_tx, n) tx.vin[n] = CTxIn.from_txin(txin) print(b2x(tx.serialize()))
def refund(proxy, refundGuy, contract): redeemScript = CScript(x(contract['redeemScript'])) txin = CMutableTxIn(fundtx['outpoint']) txout = CMutableTxOut(fundtx['amount'] - FEE, refundGuy['addr'].to_scriptPubKey()) tx = CMutableTransaction([txin], [txout]) txin.nSequence = 0 tx.nLockTime = contract['redeemblocknum'] sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) sig = refundGuy['key'].sign(sighash) + bytes([SIGHASH_ALL]) txin.scriptSig = CScript([sig, refundGuy['key'].pub, OP_FALSE, redeemScript]) txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize()))) res = VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) print("Script verified, sending raw transaction... (NOT)", res) txid = proxy.sendrawtransaction(tx) refund_tx = b2x(lx(b2x(txid))) fund_tx = str(fundtx['outpoint']) return {"refund_tx": refund_tx, "fund_tx": fund_tx}
def redeem(proxy, redeemerGuy, contract, fundtx, secret): print('redeemPubKey', redeemerGuy['addr']) # TODO: Compare with script on blockchain? redeemScript = CScript(x(contract['redeemScript'])) txin = CMutableTxIn(fundtx['outpoint']) txout = CMutableTxOut(fundtx['amount'] - FEE, redeemerGuy['addr'].to_scriptPubKey()) # Create the unsigned raw transaction. tx = CMutableTransaction([txin], [txout]) sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) # TODO: protect privkey better, separate signing from rawtx creation #privkey = self.bitcoind.dumpprivkey(self.redeemPubKey) sig = redeemerGuy['key'].sign(sighash) + bytes([SIGHASH_ALL]) preimage = secret.encode('utf-8') # preimage = x(secret) txin.scriptSig = CScript([sig, redeemerGuy['key'].pub, preimage, OP_TRUE, redeemScript]) # print("txin.scriptSig", b2x(txin.scriptSig)) txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() print('Raw redeem transaction hex: ', b2x(tx.serialize())) VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) print("Script verified, sending raw transaction...") txid = proxy.sendrawtransaction(tx) fund_tx = str(fundtx['outpoint']) redeem_tx = b2x(lx(b2x(txid))) return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
def close_tx(self, fee: int, privkey_dest: str) -> str: """Create a (mutual) close tx""" txin = CTxIn(COutPoint(bytes.fromhex(self.txid), self.output_index)) out_privkey = privkey_expand(privkey_dest) txout = CTxOut(self.amount - fee, CScript([script.OP_0, Hash160(coincurve.PublicKey.from_secret(out_privkey.secret).format())])) tx = CMutableTransaction(vin=[txin], vout=[txout]) sighash = script.SignatureHash(self.redeemscript(), tx, inIdx=0, hashtype=script.SIGHASH_ALL, amount=self.amount, sigversion=script.SIGVERSION_WITNESS_V0) sigs = [key.sign(sighash, hasher=None) for key in self.funding_privkeys_for_tx()] # BOLT #3: # ## Closing Transaction # ... # * `txin[0]` witness: `0 <signature_for_pubkey1> <signature_for_pubkey2>` witness = CScriptWitness([bytes(), sigs[0] + bytes([script.SIGHASH_ALL]), sigs[1] + bytes([script.SIGHASH_ALL]), self.redeemscript()]) tx.wit = CTxWitness([CTxInWitness(witness)]) return tx.serialize().hex()
def main(): ''' Our main function. ''' SelectParams('mainnet') decoded_transaction = eval(open('transaction_to_sign.txt').read()) # pylint: disable=eval-used txin_txid = lx(decoded_transaction['vin'][0]['txid']) txin_vout = 0 tx_in = CMutableTxIn(COutPoint(txin_txid, txin_vout)) tx_out = [] for idx in range(len(decoded_transaction['vout'])): satoshis = int(COIN * decoded_transaction['vout'][idx]['value']) script_pub_key = CScript(bytes.fromhex(decoded_transaction['vout'][idx]['scriptPubKey']['hex'])) tx_out.append(CMutableTxOut(satoshis, script_pub_key)) tx_to_spend = CMutableTransaction([tx_in], tx_out) priv_1 = CBitcoinSecret.from_secret_bytes(bytes.fromhex(PRIV_HEX_1)) priv_2 = CBitcoinSecret.from_secret_bytes(bytes.fromhex(PRIV_HEX_2)) txin_redeem_script = CScript(bytes.fromhex(decoded_transaction['vin'][0]['scriptSig']['hex'])) # Input 0 is fixed. sighash = SignatureHash(txin_redeem_script, tx_to_spend, 0, SIGHASH_ALL) signatures = [] for priv in [priv_1, priv_2]: signatures.append(priv.sign(sighash) + bytes([SIGHASH_ALL])) tx_in.scriptSig = CScript([CScriptOp(0x00), signatures[0], signatures[1], txin_redeem_script]) # script_pub_key Defined in cycle. VerifyScript(tx_in.scriptSig, txin_redeem_script, tx_to_spend, 0, (SCRIPT_VERIFY_P2SH,)) print(b2x(tx_to_spend.serialize()))
def refund(self, contract): fundtx = self.find_transaction_to_address(contract.p2sh) print("Fund tx found in refund: ", fundtx) refundPubKey = self.find_refundAddr(contract) print('refundPubKey: {0}'.format(refundPubKey)) redeemScript = CScript(x(contract.redeemScript)) txin = CMutableTxIn(fundtx['outpoint']) txout = CMutableTxOut(fundtx['amount'] - FEE, refundPubKey.to_scriptPubKey()) # Create the unsigned raw transaction. tx = CMutableTransaction([txin], [txout]) # Set nSequence and nLockTime txin.nSequence = 0 tx.nLockTime = contract.redeemblocknum sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) privkey = self.bitcoind.dumpprivkey(refundPubKey) sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) # Sign without secret txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript]) # txin.nSequence = 2185 txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize()))) res = VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) print("Script verified, sending raw transaction... (NOT)", res) txid = self.bitcoind.sendrawtransaction(tx) refund_tx = b2x(lx(b2x(txid))) fund_tx = str(fundtx['outpoint']) return {"refund_tx": refund_tx, "fund_tx": fund_tx}
def spend_p2sh_mediator(redeemScript, txins_str, amounts, daddrs, sig, rein): txin_redeemScript = CScript(x(redeemScript)) txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txins_obj = [] for txin_str in txins_str.split(): txin_list = txin_str.split("-") txins_obj.append( CMutableTxIn(COutPoint(lx(txin_list[0]), int(txin_list[1])))) txouts = [] len_amounts = len(amounts) for i in range(0, len_amounts): txouts.append( CMutableTxOut( round(amounts[i], 8) * COIN, CBitcoinAddress(daddrs[i]).to_scriptPubKey())) tx = CMutableTransaction(txins_obj, txouts) seckey = CBitcoinSecret(rein.user.dkey) ntxins = len(txins_obj) sig_list = [] for s in sig.split(): sig_list.append(x(s)) sig2_str = "" for i in range(0, ntxins): sighash = SignatureHash(txin_redeemScript, tx, i, SIGHASH_ALL) sig2 = seckey.sign(sighash) + x("01") sig2_str += " " + b2x(sig2) txins_obj[i].scriptSig = CScript( [OP_0, sig2, sig_list[i], txin_redeemScript]) VerifyScript(txins_obj[i].scriptSig, txin_scriptPubKey, tx, i, (SCRIPT_VERIFY_P2SH, )) tx_bytes = tx.serialize() hash = sha256(sha256(tx_bytes).digest()).digest() txid = b2x(hash[::-1]) txid_causeway = broadcast_tx(b2x(tx_bytes), rein) return (txid, sig2_str[1:])
def as_tx(self): sum_in = sum(prevtx.nValue for _,prevtx,_ in self.prevouts) sig_size = sum(redeemer.spendbytes for _,_,redeemer in self.prevouts) tx_size = (4 + # version field 2 + # # of txins len(self.prevouts) * 41 + # txins, excluding sigs sig_size + # txins, sigs only 1 + # # of txouts 34 + # txout 4 # nLockTime field ) feerate = int(self.proxy._call('estimatefee', 1) * COIN) # satoshi's per KB if feerate <= 0: feerate = 10000 fees = int(tx_size * feerate / 1000) tx = CMutableTransaction( [CTxIn(outpoint, nSequence=0) for outpoint,_,_ in self.prevouts], [CTxOut(sum_in - fees, self.payto.to_scriptPubKey())], 0) for n,(_,_,redeemer) in enumerate(self.prevouts): redeemer.mutate_spend(tx, n) unsigned_tx = CTransaction.from_tx(tx) for n,(_,_,redeemer) in enumerate(self.prevouts): txin = CMutableTxIn.from_txin(tx.vin[n]) txin.scriptSig = redeemer.sign_spend(unsigned_tx, n) tx.vin[n] = CTxIn.from_txin(txin) print(b2x(tx.serialize()))
def submit_opreturn(rpc_raw, rpc_connection, address, data): from bitcoin.core import CTxIn, CMutableTxOut, CScript, CMutableTransaction, COIN, CENT, b2x, b2lx from bitcoin.core.script import OP_CHECKSIG, OP_RETURN txouts = [] unspent = sorted( [y for y in rpc_connection.listunspent(0) if str(y["address"]) == address], key=lambda x: hash(x["amount"]) ) txins = [CTxIn(unspent[-1]["outpoint"])] value_in = unspent[-1]["amount"] change_pubkey = rpc_connection.validateaddress(address)["pubkey"] change_out = CMutableTxOut(int(value_in - 1 * CENT), CScript([change_pubkey, OP_CHECKSIG])) digest_outs = [CMutableTxOut(0, CScript([OP_RETURN, data]))] txouts = [change_out] + digest_outs tx = CMutableTransaction(txins, txouts) tx_hex = tx.serialize().encode("hex") print "decoderawtransaction", tx_hex transaction_decoded = rpc_raw.decoderawtransaction(tx_hex) print json.dumps(transaction_decoded, indent=4, default=json_custom_parser) r = rpc_connection.signrawtransaction(tx) assert r["complete"] tx = r["tx"] # print b2x(tx.serialize()) # print len(tx.serialize()), 'bytes' sent_tx_id = rpc_connection.sendrawtransaction(tx) return b2lx(sent_tx_id)
def test_sendrawtransaction(self): num_outputs = 5 given_transaction = CMutableTransaction([], [make_txout() for x in range(0, num_outputs)]) expected_txid = b2lx(given_transaction.GetHash()) given_transaction_hex = b2x(given_transaction.serialize()) proxy = FakeBitcoinProxy() resulting_txid = proxy.sendrawtransaction(given_transaction_hex) self.assertEqual(resulting_txid, expected_txid)
def put(self): """Persist self.""" commitment = CMutableTransaction([self.anchor], [self.our, self.their]) commitment = CMutableTransaction.from_tx(commitment) commitment.vin[0].scriptSig = commitment.vin[0].scriptSig.to_script() for tx_out in commitment.vout: tx_out.scriptPubKey = tx_out.scriptPubKey.to_scriptPubKey() with g.dat: g.dat.execute("INSERT OR REPLACE INTO CHANNELS VALUES (?, ?)", (self.address, commitment.serialize()))
def test_fundrawtransaction_adds_output(self): num_outputs = 5 unfunded_transaction = CMutableTransaction([], [make_txout() for x in range(0, num_outputs)]) proxy = FakeBitcoinProxy() funded_transaction_hex = proxy.fundrawtransaction(b2x(unfunded_transaction.serialize()))["hex"] funded_transaction = CMutableTransaction.deserialize(x(funded_transaction_hex)) self.assertTrue(len(funded_transaction.vout) > num_outputs) self.assertEqual(len(funded_transaction.vout), num_outputs + 1)
def test_create_transaction_object_1(inputs_1, outputs_1): r = PaymentService.create_transaction_object(inputs=inputs_1, outputs=outputs_1) tx_in: List[CMutableTxIn] = [ CMutableTxIn(COutPoint(lx('4521418569de2f78d5d2d3c535567ac9c48c95314c53a67788d4dcdc9a8d915b'), 0))] pk = CBitcoinAddress('16moGJwkzWhNC1pfnfJptKj9G1ogQz16xd').to_scriptPubKey() tx_out: List[CMutableTxOut] = [CMutableTxOut(100000, pk)] expected = CMutableTransaction(tx_in, tx_out) assert b2x(r.serialize()) == b2x(expected.serialize())
def redeem_contract(self, contract, secret): print("Parsing script for redeem_contract...") scriptarray = self.parse_script(contract.redeemScript) redeemblocknum = scriptarray[8] redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6])) refundPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[13])) p2sh = contract.p2sh #checking there are funds in the address amount = self.check_funds(p2sh) if(amount == 0): print("address ", p2sh, " not funded") quit() fundtx = self.find_transaction_to_address(p2sh) amount = fundtx['amount'] / COIN # print("Found fund_tx: ", fundtx) p2sh = P2SHBitcoinAddress(p2sh) if fundtx['address'] == p2sh: print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh)) blockcount = self.bitcoind.getblockcount() print("\nCurrent blocknum at time of redeem on Zcash:", blockcount) if blockcount < int(redeemblocknum): print('redeemPubKey', redeemPubKey) zec_redeemScript = CScript(x(contract.redeemScript)) txin = CMutableTxIn(fundtx['outpoint']) txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey()) # Create the unsigned raw transaction. tx = CMutableTransaction([txin], [txout]) sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL) # TODO: protect privkey better, separate signing from rawtx creation privkey = self.bitcoind.dumpprivkey(redeemPubKey) sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) preimage = secret.encode('utf-8') txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript]) # print("txin.scriptSig", b2x(txin.scriptSig)) txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey() print('Raw redeem transaction hex: ', b2x(tx.serialize())) VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) print("Script verified, sending raw transaction...") txid = self.bitcoind.sendrawtransaction(tx) fund_tx = str(fundtx['outpoint']) redeem_tx = b2x(lx(b2x(txid))) return {"redeem_tx": redeem_tx, "fund_tx": fund_tx} else: print("nLocktime exceeded, refunding") print('refundPubKey', refundPubKey) txid = self.bitcoind.sendtoaddress(refundPubKey, fundtx['amount'] - FEE) fund_tx = str(fundtx['outpoint']) refund_tx = b2x(lx(b2x(txid))) return {"refund_tx": refund_tx, "fund_tx": fund_tx} else: print("No contract for this p2sh found in database", p2sh)
def test_fundrawtransaction_hex_hash(self): unfunded_transaction = CMutableTransaction([], [make_txout() for x in range(0, 5)]) proxy = FakeBitcoinProxy() funded_transaction_hex = proxy.fundrawtransaction(b2x(unfunded_transaction.serialize()))["hex"] funded_transaction = CMutableTransaction.deserialize(x(funded_transaction_hex)) self.assertTrue(unfunded_transaction is not funded_transaction) self.assertEqual(type(funded_transaction), type(unfunded_transaction)) self.assertNotEqual(len(funded_transaction.vin), 0) self.assertTrue(len(funded_transaction.vin) > len(unfunded_transaction.vin)) self.assertEqual(type(funded_transaction.vin[0]), CTxIn)
def from_utxo(txid_in: str, tx_index_in: int, sats: int, privkey: str, fee: int, local_node_privkey: str, local_funding_privkey: str, remote_node_privkey: str, remote_funding_privkey: str, chain_hash: str = regtest_hash) -> Tuple['Funding', str]: """Make a funding transaction by spending this utxo using privkey: return Funding, tx.""" # Create dummy one to start: we will fill in txid at the end. funding = Funding('', 0, sats - fee, local_node_privkey, local_funding_privkey, remote_node_privkey, remote_funding_privkey, chain_hash) # input private key. inkey = privkey_expand(privkey) inkey_pub = coincurve.PublicKey.from_secret(inkey.secret) # use RBF'able input (requirement for dual-funded things) txin = CTxIn(COutPoint(bytes.fromhex(txid_in), tx_index_in), nSequence=0xFFFFFFFD) txout = CTxOut( sats - fee, CScript([script.OP_0, sha256(funding.redeemscript()).digest()])) tx = CMutableTransaction([txin], [txout], nVersion=2, nLockTime=funding.locktime) # now fill in funding txid. funding.txid = tx.GetTxid().hex() funding.tx = tx # while we're here, sign the transaction. address = P2WPKHBitcoinAddress.from_scriptPubKey( CScript([script.OP_0, Hash160(inkey_pub.format())])) sighash = script.SignatureHash(address.to_redeemScript(), tx, 0, script.SIGHASH_ALL, amount=sats, sigversion=script.SIGVERSION_WITNESS_V0) sig = inkey.sign(sighash, hasher=None) + bytes([script.SIGHASH_ALL]) tx.wit = CTxWitness( [CTxInWitness(CScriptWitness([sig, inkey_pub.format()]))]) return funding, tx.serialize().hex()
def get_unsigned_tx(funding_tx, redeem_script, address, amount, lock_time=0, n_sequence=0, vout=0): """ Returns a raw transaction and it's signature hash that pays to address from funding_tx Arguments: funding_tx (str): the 'input' tx redeem_script (str): The redeem script address (str): The output address that would receive `amount` lock_time (int, optional): The time the tx should be locked to n_sequence (int, optional): The sequence number to use in tx vout (int, optional): The index of the output point of `funding_tx` Returns: A tuple containing: 1/ The serial tx 2/ The tx hash """ # Set P2SH funding tx in little-endian fx = lx(funding_tx) # nSequence must be any number less than 0xffffffff to enable nLockTime txin = CMutableTxIn(COutPoint(fx, vout), nSequence=n_sequence) # if(nlock_time != 0): # txin = CMutableTxIn(COutPoint(fx, vout), nSequence=n_sequence) # else: # txin = CMutableTxIn(COutPoint(fx, vout)) # Convert amount to Satoshi's amount *= COIN # Create the txout to address script_pubkey = CBitcoinAddress(address).to_scriptPubKey() txout = CMutableTxOut(amount, script_pubkey) # Create the unsigned transaction. tx = CMutableTransaction([txin], [txout], nLockTime=lock_time) # Calculte TX sig hash sighash = SignatureHash(CScript(redeem_script), tx, 0, SIGHASH_ALL) return (tx.serialize(), sighash)
def _build_certificate_transactions(wallet, issuing_address, revocation_address, certificate_metadata, fees, tail): """Make transactions for the certificates. """ logging.info('Creating tx of certificate for recipient uid: %s ...', certificate_metadata.uid) with open(certificate_metadata.certificate_hash_file_name, 'rb') as in_file: # this is the recipient-specific hash that will be recorded on the # blockchain hashed_certificate = in_file.read() cert_out = CMutableTxOut(0, CScript([OP_RETURN, hashed_certificate])) # send a transaction to the recipient's public key, and to a revocation # address txouts = create_recipient_outputs(certificate_metadata.pubkey, revocation_address, fees.min_per_output) # define transaction inputs unspent_outputs = wallet.get_unspent_outputs(issuing_address) last_input = unspent_outputs[tail] txins = [CTxIn(last_input.outpoint)] value_in = last_input.amount # very important! If we don't send the excess change back to ourselves, # some lucky miner gets it! amount = value_in - fees.cost_per_transaction if amount > 0: change_out = create_transaction_output(issuing_address, amount) txouts = txouts + [change_out] txouts = txouts + [cert_out] tx = CMutableTransaction(txins, txouts) # this is the transaction for a recipient, unsigned hextx = hexlify(tx.serialize()) with open(certificate_metadata.unsigned_tx_file_name, 'wb') as out_file: out_file.write(bytes(hextx, 'utf-8')) logging.info('Created unsigned tx for recipient; last_input=%s', last_input) return last_input
def get_tx(self, redeem_script, address, amount, funding_tx, lock_time=0, vout=0): ''' Returns a raw transaction and it's signature hash that pays to address from funding_tx Options: vout -> which vout of funding_tx nLockTime -> set's transaction locktime ''' # Set P2SH funding tx in little-endian fx = lx(funding_tx) if lock_time > 0: nlock_time = lock_time else: nlock_time = 0 # nSequence must be any number less than 0xffffffff to enable nLockTime if (nlock_time != 0): txin = CMutableTxIn(COutPoint(fx, vout), nSequence=0) else: txin = CMutableTxIn(COutPoint(fx, vout)) # Convert amount to Satoshi's amount *= COIN # Create the txout to address script_pubkey = CBitcoinAddress(address).to_scriptPubKey() txout = CMutableTxOut(amount, script_pubkey) # Create the unsigned transaction. tx = CMutableTransaction([txin], [txout], nLockTime=nlock_time) # Calculte TX sig hash sighash = SignatureHash(CScript(redeem_script), tx, 0, SIGHASH_ALL) self.logger.info("get_tx: TX SIGHASH is %s", b2x(sighash)) return (tx.serialize(), sighash)
def maketx(tx): #txid from blockr. bitcoind does not support tx index! txid = lx(tx) vout = 0 outp = COutPoint(txid, vout) print("output: %s" % outp) # Create the txin structure, which includes the outpoint txin = CMutableTxIn(outp) print(txin) txin_scriptPubKey = CScript( [OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG]) print(txin_scriptPubKey) amount = 0.001 * COIN txout = CMutableTxOut(amount, CBitcoinAddress(a).to_scriptPubKey()) print(txout) # Create the unsigned transaction. newtx = CMutableTransaction([txin], [txout]) print(newtx) sighash = SignatureHash(txin_scriptPubKey, newtx, 0, SIGHASH_ALL) print(sighash) # Now sign it. We have to append the type of signature we want to the end, in # this case the usual SIGHASH_ALL. sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) print(sig) # Set the scriptSig of our transaction input appropriately. txin.scriptSig = CScript([sig, seckey.pub]) try: VerifyScript(txin.scriptSig, txin_scriptPubKey, newtx, 0, (SCRIPT_VERIFY_P2SH, )) except: pass print('*' * 20) print(b2x(newtx.serialize())) """
def partial_spend_p2sh_mediator_2 (redeemScript,txins_str,amount,daddr,rein): txin_redeemScript = CScript(x(redeemScript)) txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txins_obj = [] for txin_str in txins_str.split(): txin_list = txin_str.split("-") txins_obj.append(CMutableTxIn(COutPoint(lx(txin_list[0]),int(txin_list[1])))) txout = CMutableTxOut(amount*COIN,CBitcoinAddress(daddr).to_scriptPubKey()) tx = CMutableTransaction(txins_obj,[txout]) seckey = CBitcoinSecret(rein.user.dkey) ntxins = len(txins_obj) for i in range(0,ntxins): sighash = SignatureHash(txin_redeemScript,tx,i,SIGHASH_ALL) sig = seckey.sign(sighash)+x("01") txins_obj[i].scriptSig = CScript([OP_0, sig, txin_redeemScript]) #VerifyScript(txins_obj[i].scriptSig, txin_scriptPubKey, tx, i, (SCRIPT_VERIFY_P2SH,)) tx_bytes = tx.serialize() return b2x(tx_bytes)
def maketx(tx): #txid from blockr. bitcoind does not support tx index! txid = lx(tx) vout = 0 outp = COutPoint(txid, vout) print ("output: %s"%outp) # Create the txin structure, which includes the outpoint txin = CMutableTxIn(outp) print (txin) txin_scriptPubKey = CScript([OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG]) print (txin_scriptPubKey) amount = 0.001*COIN txout = CMutableTxOut(amount, CBitcoinAddress(a).to_scriptPubKey()) print (txout) # Create the unsigned transaction. newtx = CMutableTransaction([txin], [txout]) print (newtx) sighash = SignatureHash(txin_scriptPubKey, newtx, 0, SIGHASH_ALL) print (sighash) # Now sign it. We have to append the type of signature we want to the end, in # this case the usual SIGHASH_ALL. sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) print (sig) # Set the scriptSig of our transaction input appropriately. txin.scriptSig = CScript([sig, seckey.pub]) try: VerifyScript(txin.scriptSig, txin_scriptPubKey, newtx, 0, (SCRIPT_VERIFY_P2SH,)) except: pass print ('*'*20) print(b2x(newtx.serialize())) """
def test_create_transaction_object_2(inputs_2, outputs_2): r = PaymentService.create_transaction_object(inputs=inputs_2, outputs=outputs_2) tx_in: List[CMutableTxIn] = [ CMutableTxIn(COutPoint(lx('4521418569de2f78d5d2d3c535567ac9c48c95314c53a67788d4dcdc9a8d915b'), 0)), CMutableTxIn(COutPoint(lx('0bb4abea99101197cf2ddf43a2af1e73f868887d5f4a3619241cbb67413a34e7'), 0)), ] pk1 = CBitcoinAddress('34s7MXyL5sEbgsto6SRdvV3YYUu8XVs4xg').to_scriptPubKey() pk2 = CBitcoinAddress('16moGJwkzWhNC1pfnfJptKj9G1ogQz16xd').to_scriptPubKey() pk3 = CBitcoinAddress('17A16QmavnUfCW11DAApiJxp7ARnxN5pGX').to_scriptPubKey() tx_out: List[CMutableTxOut] = [ CMutableTxOut(100000, pk1), CMutableTxOut(2000, pk2), CMutableTxOut(20000000, pk3), ] expected = CMutableTransaction(tx_in, tx_out) assert b2x(r.serialize()) == b2x(expected.serialize())
def _build_certificate_transactions(wallet, issuing_address, revocation_address, certificate_metadata, fees, tail): """Make transactions for the certificates. """ logging.info('Creating tx of certificate for recipient uid: %s ...', certificate_metadata.uid) with open(certificate_metadata.certificate_hash_file_name, 'rb') as in_file: # this is the recipient-specific hash that will be recorded on the # blockchain hashed_certificate = in_file.read() cert_out = CMutableTxOut(0, CScript([OP_RETURN, hashed_certificate])) # send a transaction to the recipient's public key, and to a revocation # address txouts = create_recipient_outputs( certificate_metadata.pubkey, revocation_address, fees.min_per_output) # define transaction inputs unspent_outputs = wallet.get_unspent_outputs(issuing_address) last_input = unspent_outputs[tail] txins = [CTxIn(last_input.outpoint)] value_in = last_input.amount # very important! If we don't send the excess change back to ourselves, # some lucky miner gets it! amount = value_in - fees.cost_per_transaction if amount > 0: change_out = create_transaction_output(issuing_address, amount) txouts = txouts + [change_out] txouts = txouts + [cert_out] tx = CMutableTransaction(txins, txouts) # this is the transaction for a recipient, unsigned hextx = hexlify(tx.serialize()) with open(certificate_metadata.unsigned_tx_file_name, 'wb') as out_file: out_file.write(bytes(hextx, 'utf-8')) logging.info( 'Created unsigned tx for recipient; last_input=%s', last_input) return last_input
def generate_tx(self): tx_chain = self.get_next_tx_chain() txid = lx(tx_chain.current_unspent_tx) txins = [ CMutableTxIn(COutPoint(txid, 0)), CMutableTxIn(COutPoint(txid, 1)) ] txin_seckeys = [tx_chain.seckey, self._spent_to.seckey] amount_in = tx_chain.amount tx_chain.amount -= int(config.transaction_fee / 2) txout1 = CMutableTxOut( tx_chain.amount, CBitcoinAddress(tx_chain.address).to_scriptPubKey()) txout2 = CMutableTxOut( tx_chain.amount, CBitcoinAddress(self._spent_to.address).to_scriptPubKey()) tx = CMutableTransaction(txins, [txout1, txout2], nVersion=2) for i, txin in enumerate(txins): txin_scriptPubKey = CScript([ OP_DUP, OP_HASH160, Hash160(txin_seckeys[i].pub), OP_EQUALVERIFY, OP_CHECKSIG ]) sighash = SignatureHash(txin_scriptPubKey, tx, i, SIGHASH_ALL) sig = txin_seckeys[i].sign(sighash) + bytes([SIGHASH_ALL]) txin.scriptSig = CScript([sig, txin_seckeys[i].pub]) tx_serialized = tx.serialize() logging.debug('{} trying to sendrawtransaction' ' (in=2x{} out=2x{} fee={} bytes={})' ' using tx_chain number={}'.format( self._name, amount_in, txout1.nValue, (amount_in * 2) - (txout1.nValue * 2), len(tx_serialized), self._current_tx_chain_index)) tx_hash = self.execute_rpc('sendrawtransaction', b2x(tx_serialized)) tx_chain.current_unspent_tx = tx_hash logging.info( '{} sendrawtransaction was successful; tx got hash={}'.format( self._name, tx_hash))
def make_signed_tx(ins, outs, priv): print('####### in make_signed_tx #######') print('ins: {}'.format(ins)) print('outs: {}'.format(outs)) print('priv: {}'.format(priv)) txins = [] txouts = [] txin_scriptPubKeys = [] # txin_scriptPubKeys = [] for i, inp in enumerate(ins): print('inp[tx_id]: {}'.format(inp['tx_id'])) txin = CMutableTxIn(COutPoint(lx(inp['tx_id']), inp['index'])) seckey = CBitcoinSecret.from_secret_bytes( pygcoinlib.script_to_address(inp['script']).encode('utf-8')) txin_scriptPubKeys.append( CScript([ OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG ])) # txin_scriptPubKeys.append(CScript([OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG])) txins.append(txin) for o, out in enumerate(outs): # print('out[\'address\']: {}'.format(out['address'])) if 'script' in out: # txouts.append(CMutableTxOut(0, CScript([bytes(out['script'], encoding='UTF-8')]), 2)) print('song') else: txouts.append( CMutableTxOut( out['value'], CBitcoinAddress(out['address']).to_scriptPubKey(), out['color'])) # print('address: {}'.format(pygcoinlib.script_to_address(spk['script']))) tx = CMutableTransaction(txins, txouts) for i, inp in enumerate(ins): sighash = SignatureHash(txin_scriptPubKeys[i], tx, i, SIGHASH_ALL) sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) txins[i].scriptSig = CScript([sig, seckey.pub]) VerifyScript(txins[i].scriptSig, txin_scriptPubKeys[i], tx, i, (SCRIPT_VERIFY_P2SH, )) return b2x(tx.serialize())
# Create the txout. This time we create the scriptPubKey from a Bitcoin # address. txout = CMutableTxOut(0.0005*COIN, CBitcoinAddress('323uf9MgLaSn9T7vDaK1cGAZ2qpvYUuqSp').to_scriptPubKey()) # Create the unsigned transaction. tx = CMutableTransaction([txin], [txout]) # Calculate the signature hash for that transaction. Note how the script we use # is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG # operation happens EvalScript() will be evaluating the redeemScript, so the # corresponding SignatureHash() function will use that same script when it # replaces the scriptSig in the transaction being hashed with the script being # executed. sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL) # Now sign it. We have to append the type of signature we want to the end, in # this case the usual SIGHASH_ALL. sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) # Set the scriptSig of our transaction input appropriately. txin.scriptSig = CScript([sig, txin_redeemScript]) # Verify the signature worked. This calls EvalScript() and actually executes # the opcodes in the scripts to see if everything worked out. If it doesn't an # exception will be raised. VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) # Done! Print the transaction to standard output with the bytes-to-hex # function. print(b2x(tx.serialize()))
def transfer(self, src_addrs_key_map: OrderedDict, dst_addrs_amount_map: dict, txfee: Decimal, auto_calc_pay_back: bool, pay_back_index: int = 0xfffffff, ensure_one_txout: bool = False) -> str: """ :param src_addrs_key_map: {'addr1': 'privkey1', 'addr2': 'privkey2' } 私钥为 hex字符串 :param dst_addrs_amount_map: {'addr1':Decimal(0.1234), 'addr2':Deciaml(0.234) } :param txfee: 矿工费 :param auto_calc_pay_back: 是否自动计算并找零 :param pay_back_index: 找零地址索引 即在 src_addrs_key_map 中的索引 :param ensure_one_txout: 确认只有一个交易输出(比如: 提币不需要找零的情况, 归集) :return: txid """ #仅支持 P2PKH 类型的from地址 assert isinstance(src_addrs_key_map, OrderedDict), 'src_addrs_key_map is not OrderedDict' assert isinstance(dst_addrs_amount_map, dict), 'dst_addrs_amount_map is not dict' assert Decimal('0.00001') <= txfee <= Decimal( '0.001'), 'invalid txfee, please check txfee' assert len( src_addrs_key_map) >= 1, 'src_addrs_key_map length must >= 1' assert not (True == auto_calc_pay_back == ensure_one_txout) , \ 'True == auto_calc_pay_back == ensure_one_txout , must be mutex ' if ensure_one_txout: assert (len(dst_addrs_amount_map) == 1 ), 'dst_addrs_amount_map length must equal 1' elif not auto_calc_pay_back: assert (len(dst_addrs_amount_map) >= 1), 'dst_addrs_amount_map length must >= 2' if auto_calc_pay_back and pay_back_index >= len(src_addrs_key_map): raise Exception('pay_back_index is to large') self.logger.info( f'dst_addrs_amount_map is { json.dumps(dst_addrs_amount_map, indent=4, default=decimal_default) }' ) assert self.ping() == True, 'bitcoind rpc is gone' total_amount = sum(dst_addrs_amount_map.values()) + txfee self.logger.info(f'total_amount is {total_amount}') source_addrs = list(src_addrs_key_map.keys()) #WARNING: 禁止打印私钥!!! is_enough, founded_utxos = self.search_utxo(addrs=source_addrs, total_amount=total_amount) if not is_enough: msg = 'balance is not enough' self.logger.error(msg) raise Exception(msg) self.logger.info( f'founded_utxos is { json.dumps(founded_utxos, indent=4, default=decimal_default) }' ) #设置全局变量 SelectParams(self.net_type) #构造inputs txins = [] utxo_owner_map = dict() for addr, utxos in founded_utxos.items(): assert addr in src_addrs_key_map, 'addr is not in src_addrs_key_map' for utxo in utxos: txin = CMutableTxIn( prevout=COutPoint(hash=lx(utxo['txid']), n=utxo['vout'])) txins.append(txin) #因为顺序不会被打乱, 所以可以直接使用 索引进行对应 utxo_owner_map[len(txins) - 1] = addr #构造outputs txouts = [] for to_addr, amount in dst_addrs_amount_map.items(): out = CMutableTxOut( nValue=amount * COIN, scriptPubKey=CBitcoinAddress(to_addr).to_scriptPubKey()) txouts.append(out) #自动结算 if auto_calc_pay_back: sum_in_satoshi = 0 for addr, utxos in founded_utxos.items(): for utxo in utxos: sum_in_satoshi += utxo['value'] pay_back_in_satoshi = int(sum_in_satoshi - int(total_amount * COIN)) #判断找零的金额, 如果太小就不要了, 作为矿工费 if pay_back_in_satoshi >= MIN_AVAILABLE_UTXO_VALUE_IN_SATOSHI: pay_back_addr = list(src_addrs_key_map.keys())[pay_back_index] pay_back_out = CMutableTxOut( nValue=pay_back_in_satoshi, scriptPubKey=CBitcoinAddress( pay_back_addr).to_scriptPubKey()) txouts.append(pay_back_out) muttx = CMutableTransaction(vin=txins, vout=txouts) #对每个 input 进行签名 for n in range(len(txins)): #查找这个utxo属于哪个地址 owner_addr = utxo_owner_map[n] privkey = src_addrs_key_map[owner_addr] if len(privkey) == 64: # hex wif_key = PrivKeyToWIFCompress(privkey, self.net_type != 'mainnet') seckey = CBitcoinSecret(s=wif_key) elif len(privkey) == 52: #base58格式 seckey = CBitcoinSecret(s=privkey) else: raise Exception("invalid privkey") txin_script_pubkey = CScript([ OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG ]) sig_hash = SignatureHash(txin_script_pubkey, muttx, n, SIGHASH_ALL) sig = seckey.sign(sig_hash) + bytes([SIGHASH_ALL]) muttx.vin[n].scriptSig = CScript([sig, seckey.pub]) #TODO: 处理验签失败抛异常 VerifyScript(muttx.vin[n].scriptSig, txin_script_pubkey, muttx, n, (SCRIPT_VERIFY_P2SH, )) pass raw_tx_hex = b2x(muttx.serialize()) self.logger.info(f'raw_tx_hex is: {raw_tx_hex}') # local_tx_hash = muttx.GetTxid() # self.logger.info(f'local_tx_hash is: {b2x(local_tx_hash)}') #广播交易 assert self.ping( ) == True, 'bitcoind rpc is gone' #测试 bitcoind的 rpc服务是否还在 txid = self.send_raw_tx(raw_tx_hex=raw_tx_hex) self.logger.info(f'send_raw_tx txid is: {txid}') return txid
class BitcoinTransaction(object): '''Bitcoin transaction object.''' @auto_switch_params(1) def __init__(self, network, recipient_address: str, value: float, solvable_utxo: list, tx_locktime: int = 0): self.recipient_address = recipient_address self.value = value self.network = network self.symbol = network.default_symbol self.validate_address() self.solvable_utxo = solvable_utxo self.utxo_value = sum(utxo.value for utxo in self.solvable_utxo) self.tx_in_list = [utxo.tx_in for utxo in self.solvable_utxo] self.tx_out_list = [] self.tx = None self.tx_locktime = tx_locktime self.fee = 0.0 self.fee_per_kb = 0.0 self.signed = False def validate_address(self): if not self.network.is_valid_address(self.recipient_address): raise ValueError('Given recipient address is invalid.') def build_outputs(self): self.tx_out_list = [ CMutableTxOut( to_base_units(self.value), CBitcoinAddress(self.recipient_address).to_scriptPubKey()) ] def add_fee_and_sign(self, default_wallet=None): """Signing transaction and adding fee under the hood.""" # signing the transaction for the first time to get the right transaction size self.sign(default_wallet) # adding fee based on transaction size (this will modify the transaction) self.add_fee() # signing the modified transaction self.sign(default_wallet) def sign(self, default_wallet: BitcoinWallet = None): """Signing transaction using the wallet object.""" for tx_index, tx_in in enumerate(self.tx.vin): utxo = self.solvable_utxo[tx_index] wallet = utxo.wallet or default_wallet if wallet is None: raise RuntimeError('Cannot sign transaction without a wallet.') tx_script = utxo.parsed_script if utxo.contract: sig_hash = script.SignatureHash( script.CScript.fromhex(utxo.contract), self.tx, tx_index, script.SIGHASH_ALL) else: sig_hash = script.SignatureHash(tx_script, self.tx, tx_index, script.SIGHASH_ALL) sig = wallet.private_key.sign(sig_hash) + struct.pack( '<B', script.SIGHASH_ALL) script_sig = [sig, wallet.private_key.pub ] + utxo.unsigned_script_sig tx_in.scriptSig = script.CScript(script_sig) VerifyScript(tx_in.scriptSig, tx_script, self.tx, tx_index, (SCRIPT_VERIFY_P2SH, )) self.signed = True def create_unsigned_transaction(self): assert self.utxo_value >= self.value, 'You want to spend more than you\'ve got. Add more UTXO\'s.' self.build_outputs() self.tx = CMutableTransaction(self.tx_in_list, self.tx_out_list, nLockTime=self.tx_locktime) def publish(self): return self.network.publish(self.raw_transaction) @property def size(self) -> int: """Returns the size of a transaction represented in bytes.""" return len(self.tx.serialize()) def calculate_fee(self, add_sig_size=False): """Calculating fee for given transaction based on transaction size and estimated fee per kb.""" if not self.fee_per_kb: self.fee_per_kb = self.network.get_current_fee_per_kb() size = self.size if add_sig_size: size += len(self.tx_in_list) * SIGNATURE_SIZE self.fee = round((self.fee_per_kb / 1000) * size, 8) def add_fee(self): """Adding fee to the transaction by decreasing 'change' transaction.""" if not self.fee: self.calculate_fee() fee_in_satoshi = to_base_units(self.fee) if self.tx.vout[0].nValue < fee_in_satoshi: raise RuntimeError( 'Cannot subtract fee from transaction. You need to add more input transactions.' ) self.tx.vout[0].nValue -= fee_in_satoshi @property def raw_transaction(self): return b2x(self.tx.serialize()) @property def address(self): return b2lx(self.tx.GetHash()) def show_details(self): details = { 'transaction': self.raw_transaction, 'transaction_address': self.address, 'fee': self.fee, 'fee_per_kb': self.fee_per_kb, 'fee_per_kb_text': f'{self.fee_per_kb:.8f} {self.symbol} / 1 kB', 'fee_text': f'{self.fee:.8f} {self.symbol}', 'recipient_address': self.recipient_address, 'size': self.size, 'size_text': f'{self.size} bytes', 'value': self.value, 'value_text': f'{self.value:.8f} {self.symbol}', } if self.signed: details['transaction_link'] = self.network.get_transaction_url( self.address) return details def get_transaction_url(self) -> Optional[str]: '''Wrapper around the `get_transaction_url` method from base network.''' if not self.tx: return return self.network.get_transaction_url(self.address)
CScript([1, x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71'), b'\x00'*33, 2, OP_CHECKMULTISIG])) tx.vout.append(multisig_txout) for bad_addr in args.bad_addr: bad_addr = CBitcoinAddress(bad_addr) txout = CMutableTxOut(args.dust, bad_addr.to_scriptPubKey()) tx.vout.append(txout) # Add inputs until we meet the fee1 threshold unspent = sorted(rpc.listunspent(1), key=lambda x: x['amount']) value_in = 0 value_out = sum([vout.nValue for vout in tx.vout]) while (value_in - value_out) / len(tx.serialize()) < feeperbyte1: # What's the delta fee that we need to get to our desired fees per byte at # the current tx size? delta_fee = math.ceil((feeperbyte1 * len(tx.serialize())) - (value_in - value_out)) logging.debug('Delta fee: %s' % str_money_value(delta_fee)) # If we simply subtract that from the change outpoint are we still above # the dust threshold? if change_txout.nValue - delta_fee > args.dust: change_txout.nValue -= delta_fee value_out -= delta_fee # Do we need to add another input? if value_in - value_out < 0: new_outpoint = unspent[-1]['outpoint']
def test_unvault_txout(bitcoind): """Test that unvault_txout() produces a valid and conform txo. Note that we use python-bitcoinlib for this one, as signrawtransactionwithkey is (apparently?) not happy dealing with exotic scripts. Note also that bitcoinlib's API uses sats, while bitcoind's one uses BTC.. """ amount = 50 * COIN - 500 # The stakeholders stk_privkeys = [CKey(os.urandom(32)) for i in range(4)] stk_pubkeys = [k.pub for k in stk_privkeys] # The cosigning server serv_privkey = CKey(os.urandom(32)) # First, pay to the unvault tx script txo = unvault_txout(stk_pubkeys, serv_privkey.pub, amount) txo_addr = str(CBitcoinAddress.from_scriptPubKey(txo.scriptPubKey)) amount_for_bitcoind = float(Decimal(amount) / Decimal(COIN)) txid = bitcoind.pay_to(txo_addr, amount_for_bitcoind) # We can spend it immediately if all stakeholders sign (emergency or cancel # tx) txin = CTxIn(COutPoint(lx(txid), 0)) amount_min_fees = amount - 500 addr = bitcoind.getnewaddress() new_txo = CTxOut(amount_min_fees, CBitcoinAddress(addr).to_scriptPubKey()) tx = CMutableTransaction([txin], [new_txo], nVersion=2) # We can't test the signing against bitcoind, but we can at least test the # transaction format bitcoind_tx = bitcoind.rpc.createrawtransaction([ {"txid": txid, "vout": 0} ], [ {addr: float(Decimal(amount_min_fees) / Decimal(COIN))} ]) assert b2x(tx.serialize()) == bitcoind_tx tx_hash = SignatureHash(unvault_script(*stk_pubkeys, serv_privkey.pub), tx, 0, SIGHASH_ALL, amount, SIGVERSION_WITNESS_V0) sigs = [key.sign(tx_hash) + bytes([SIGHASH_ALL]) for key in stk_privkeys[::-1]] # Note the reverse here witness_script = [*sigs, unvault_script(*stk_pubkeys, serv_privkey.pub)] witness = CTxInWitness(CScriptWitness(witness_script)) tx.wit = CTxWitness([witness]) bitcoind.send_tx(b2x(tx.serialize())) assert bitcoind.has_utxo(addr) # If two out of three stakeholders sign, we need the signature from the # cosicosigning server and we can't spend it before 6 blocks (csv). # Pay back to the unvault tx script txo = unvault_txout(stk_pubkeys, serv_privkey.pub, amount) txo_addr = str(CBitcoinAddress.from_scriptPubKey(txo.scriptPubKey)) txid = bitcoind.pay_to(txo_addr, amount_for_bitcoind) # Reconstruct the transaction but with only two stakeholders signatures txin = CTxIn(COutPoint(lx(txid), 0), nSequence=6) amount_min_fees = amount - 500 addr = bitcoind.getnewaddress() new_txo = CTxOut(amount_min_fees, CBitcoinAddress(addr).to_scriptPubKey()) tx = CMutableTransaction([txin], [new_txo], nVersion=2) # We can't test the signing against bitcoind, but we can at least test the # transaction format bitcoind_tx = bitcoind.rpc.createrawtransaction([ {"txid": txid, "vout": 0, "sequence": 6} ], [ {addr: float(Decimal(amount_min_fees) / Decimal(COIN))} ]) assert b2x(tx.serialize()) == bitcoind_tx tx_hash = SignatureHash(unvault_script(*stk_pubkeys, serv_privkey.pub), tx, 0, SIGHASH_ALL, amount, SIGVERSION_WITNESS_V0) # The cosigning server sigs = [serv_privkey.sign(tx_hash) + bytes([SIGHASH_ALL])] # We fail the third CHECKSIG !! sigs += [empty_signature()] sigs += [key.sign(tx_hash) + bytes([SIGHASH_ALL]) for key in stk_privkeys[::-1][2:]] # Just the first two witness_script = [*sigs, unvault_script(*stk_pubkeys, serv_privkey.pub)] witness = CTxInWitness(CScriptWitness(witness_script)) tx.wit = CTxWitness([witness]) # Relative locktime ! for i in range(5): with pytest.raises(VerifyRejectedError, match="non-BIP68-final"): bitcoind.send_tx(b2x(tx.serialize())) bitcoind.generate_block(1) # It's been 6 blocks now bitcoind.send_tx(b2x(tx.serialize())) assert bitcoind.has_utxo(addr)
def test_send_script_spend(self): """ Run more in-depth execution checks on the script generated for the send transaction """ sender_private_key = CBitcoinSecret.from_secret_bytes(x('4f65da9b656de4036076911707d2b2dbf065689245b8674faa8790e36d7e5850')) sender_public_key = sender_private_key.pub recipient_private_key = CBitcoinSecret.from_secret_bytes(x('cfe8e33672f7045f020210f3c7afbca660e053e4c9415c542ff185e97b175cf0')) recipient_public_key = recipient_private_key.pub secret = x('88d6e51f777b0b8dc0f429da9f372fbc') secret_hash = Hash(secret) send_to_key = CBitcoinSecret.from_secret_bytes(x('15a249b4c09286b877d4708191f1ee8de09903bae034dd9dc8e3286451fa1c80')) send_to_address = P2PKHBitcoinAddress.from_pubkey(send_to_key.pub) random_tx_id = x('8390b4c8198198c6447da1a6fad498209436a785459936b95a1e3b63618c1d8a') value = 10 * COIN send_tx_script_pub_key = build_send_out_script(sender_public_key, recipient_public_key, secret_hash) send_txins = [CMutableTxIn(COutPoint(random_tx_id, 0))] send_txouts = [CMutableTxOut(value, send_tx_script_pub_key)] send_tx = CMutableTransaction(send_txins, send_txouts) # Test the standard spend transaction txins = [CMutableTxIn(COutPoint(Hash(send_tx.serialize()), 0))] txouts = [CMutableTxOut(value, send_to_address.to_scriptPubKey())] recv_tx = CMutableTransaction(txins, txouts) sighash = SignatureHash(send_tx_script_pub_key, recv_tx, 0, SIGHASH_ALL) recipient_sig = recipient_private_key.sign(sighash) + (b'\x01') # bytes([SIGHASH_ALL]) recv_tx.vin[0].scriptSig = CScript([secret, 0, recipient_sig, recipient_public_key]) VerifyScript(recv_tx.vin[0].scriptSig, send_tx.vout[0].scriptPubKey, recv_tx, 0, (SCRIPT_VERIFY_P2SH,)) # Test a refund transaction refund_tx = CMutableTransaction(txins, txouts) sighash = SignatureHash(send_tx_script_pub_key, refund_tx, 0, SIGHASH_ALL) sender_sig = sender_private_key.sign(sighash) + (b'\x01') # bytes([SIGHASH_ALL]) recipient_sig = recipient_private_key.sign(sighash) + (b'\x01') # bytes([SIGHASH_ALL]) refund_tx.vin[0].scriptSig = CScript([sender_sig, sender_public_key, 1, recipient_sig, recipient_public_key]) VerifyScript(refund_tx.vin[0].scriptSig, send_tx_script_pub_key, refund_tx, 0, (SCRIPT_VERIFY_P2SH,)) # Test invalid transactions are rejected invalid_tx = CMutableTransaction(txins, txouts) sighash = SignatureHash(send_tx_script_pub_key, invalid_tx, 0, SIGHASH_ALL) sender_sig = sender_private_key.sign(sighash) + (b'\x01') # bytes([SIGHASH_ALL]) recipient_sig = recipient_private_key.sign(sighash) + (b'\x01') # bytes([SIGHASH_ALL]) invalid_tx.vin[0].scriptSig = CScript([]) with self.assertRaises(MissingOpArgumentsError): VerifyScript(invalid_tx.vin[0].scriptSig, send_tx_script_pub_key, invalid_tx, 0, (SCRIPT_VERIFY_P2SH,)) invalid_tx.vin[0].scriptSig = CScript([recipient_sig, recipient_public_key, 0]) with self.assertRaises(VerifyOpFailedError): VerifyScript(invalid_tx.vin[0].scriptSig, send_tx_script_pub_key, invalid_tx, 0, (SCRIPT_VERIFY_P2SH,)) invalid_tx.vin[0].scriptSig = CScript([recipient_sig, recipient_public_key, 1]) with self.assertRaises(VerifyOpFailedError): VerifyScript(invalid_tx.vin[0].scriptSig, send_tx_script_pub_key, invalid_tx, 0, (SCRIPT_VERIFY_P2SH,)) invalid_tx.vin[0].scriptSig = CScript([recipient_sig, recipient_public_key, 1, recipient_sig, recipient_public_key]) with self.assertRaises(VerifyOpFailedError): VerifyScript(invalid_tx.vin[0].scriptSig, send_tx_script_pub_key, invalid_tx, 0, (SCRIPT_VERIFY_P2SH,)) invalid_tx.vin[0].scriptSig = CScript([sender_sig, sender_public_key, 1, sender_sig, sender_public_key]) with self.assertRaises(VerifyOpFailedError): VerifyScript(invalid_tx.vin[0].scriptSig, send_tx_script_pub_key, invalid_tx, 0, (SCRIPT_VERIFY_P2SH,))
# is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG # operation happens EvalScript() will be evaluating the redeemScript, so the # corresponding SignatureHash() function will use that same script when it # replaces the scriptSig in the transaction being hashed with the script being # executed. ## override module method SignatureHash = MySignatureHash sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL) # Now sign it. We have to append the type of signature we want to the end, in # this case the usual SIGHASH_ALL. sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) # Set the scriptSig of our transaction input appropriately. txin.scriptSig = CScript([sig, txin_redeemScript]) # Verify the signature worked. This calls EvalScript() and actually executes # the opcodes in the scripts to see if everything worked out. If it doesn't an # exception will be raised. VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) # Done! Print the transaction to standard output with the bytes-to-hex # function. print("Transaction:\n", b2x(tx.serialize())) pycoin_tx = Tx.from_bin(tx.serialize()) print("Pycoin tx:\n", pycoin_tx.__repr__())
def construct_signed_transaction(redeem_script: str, sig_files: List[str], inputs_file: str, outputs_file: str, testnet: bool = False) -> str: """ Construct a hex-encoded serialized bitcoin transaction fully-signed and ready for broadcast for a 2-of-3 P2SH-multisig address Args: redeem_script: hex-encoded redeem script (see generate_multisig_redeem_script.py) sig_files: List of 2 JSON signature files containing a public key and signatures for each UTXO (see sign_multisig_spend.py) inputs_file: JSON file of UTXOs to be spent (see get_utxo_set.py) outputs_file: JSON file of destination addresses (see generate_outputs.py) testnet: Is this a testnet or mainnet transaction? Returns: fully-signed hex-encoded serialized Bitcoin transaction Raises: ValueError: If sig_files doesn't have exactly 2 elements. Example: TODO """ if testnet: SelectParams('testnet') else: SelectParams('mainnet') # Input validation if len(sig_files) != 2: raise ValueError("Two Signature Files are Required") # load inputs, outputs, and signatures with open(inputs_file, 'r') as f: inputs = json.load(f) with open(outputs_file, 'r') as f: outputs = json.load(f) signatures = [] for sig_file in sig_files: with open(sig_file, 'r') as f: signatures.append(json.load(f)) # Order Signatures parsed_redeem_script = btc_utils.parse_redeem_script(redeem_script) sig1_index = parsed_redeem_script['pubkeys'].index(signatures[0]['pubkey']) sig2_index = parsed_redeem_script['pubkeys'].index(signatures[1]['pubkey']) if sig2_index < sig1_index: signatures = [signatures[1], signatures[0]] # Construct ScriptSigs zipped_sigs = zip(signatures[0]['signatures'], signatures[1]['signatures']) scriptsigs = [ script.CScript([ OP_0, x(sig1) + bytes([SIGHASH_ALL]), x(sig2) + bytes([SIGHASH_ALL]), x(redeem_script) ]) for (sig1, sig2) in zipped_sigs ] # Construct Inputs List TxIns = [] for input in inputs: txid = lx(input['txid']) vout = input['n'] TxIns.append(CMutableTxIn(COutPoint(txid, vout))) # Insert ScriptSigs for i in range(len(TxIns)): TxIns[i].scriptSig = scriptsigs[i] # Construct Outputs List TxOuts = [] for output in outputs: output_script = CBitcoinAddress(output['address']).to_scriptPubKey() TxOuts.append(CMutableTxOut(output['amount'], output_script)) # Construct TX tx = CMutableTransaction(TxIns, TxOuts) return b2x(tx.serialize())