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 sign_unvault_revault(tx, privkey, pubkeys, pub_server, prev_value, sign_all=False): """Signs a transaction revaulting an unvault transaction. This is the "all stakeholders sign" path of the script, not encumbered by a timelock. This path is used for both the emergency and cancel transactions. These transactions are crucial to preserve our security assumptions (i.e. a revaulting transaction will be confirmed), so stakeholders exchange SINGLE | ANYONECANPAY signatures to allow any of them to increase the feerate by appending an input and an output. :param tx: The unsigned transaction, a CMutableTransaction. :param privkey: (bytes) The private key to sign the transaction with. :param pubkeys: The pubkeys of the stakeholders. :param pub_server: The pubkey of the cosigning server. :param prev_value: The prevout's value in satoshis. :param sign_all: If set to True, sign we SIGHASH_ALL instead. :return: The signatures for the provided privkeys (a list). """ sighash = SIGHASH_ALL if sign_all else SINGLE_ANYONECANPAY tx_hash = SignatureHash(unvault_script(*pubkeys, pub_server), tx, 0, sighash, prev_value, SIGVERSION_WITNESS_V0) return CKey(privkey).sign(tx_hash) + bytes([sighash])
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 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 partial_spend_p2sh_mediator(redeemScript, rein, mediator_address, mediator_sig=False): txin_redeemScript = CScript(x(redeemScript)) txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) (txins, total_value) = unspent_txins(txin_p2sh_address, rein.testnet) if len(txins) == 0: raise ValueError('No unspent txins found') txins_str = "" txins_obj = [] for txid, vout in txins: txins_str += " " + txid + "-" + str(vout) txins_obj.append(CMutableTxIn(COutPoint(lx(txid), vout))) fee = 0.00025 amount = round(total_value - fee, 8) if amount <= 0: raise ValueError('Not enough value in the inputs') if mediator_sig: txout = CMutableTxOut( amount * COIN, CBitcoinAddress(mediator_address).to_scriptPubKey()) tx = CMutableTransaction(txins_obj, [txout]) seckey = CBitcoinSecret(rein.user.dkey) ntxins = len(txins_obj) sig = "" for i in range(0, ntxins): sighash = SignatureHash(txin_redeemScript, tx, i, SIGHASH_ALL) sig += " " + b2x(seckey.sign(sighash) + x("01")) return (txins_str[1:], "{:.8f}".format(amount), str(mediator_address), sig[1:]) return (txins_str[1:], "{:.8f}".format(amount), str(mediator_address))
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 sign_emergency_vault_tx(tx, privkey, pubkeys, prev_value, sign_all=False): """Signs the transaction which moves a vault's coins to the offline 4of4. This transaction is crucial to preserve our security assumptions (i.e. a revaulting transaction will be confirmed), so stakeholders exchange SINGLE | ANYONECANPAY signatures to allow any of them to increase the feerate by appending an input and an output. :param vault_txid: The id of the transaction funding the vault, as bytes. :param privkey: (bytes) The private key to sign the transaction with. :param pubkeys: A list containing the public key of each stakeholder. :param prev_value: The vault output (previout output) value in satoshis. :param sign_all: If set to True, sign we SIGHASH_ALL instead. :return: A list, one signature per given privkey. """ sighash = SIGHASH_ALL if sign_all else SINGLE_ANYONECANPAY tx_hash = SignatureHash(vault_script(pubkeys), tx, 0, sighash, amount=prev_value, sigversion=SIGVERSION_WITNESS_V0) # A signature per pubkey return CKey(privkey).sign(tx_hash) + bytes([sighash])
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 creates_add_input(bitcoind, tx): """Creates and add an input to a CMutableTransaction, SIGHASH_ALL. :returns: The txid of the first stage fee bumping tx (for convenience) """ # First we get some coins privkey = CKey(os.urandom(32)) scriptPubKey = CScript([OP_0, Hash160(privkey.pub)]) address = CBitcoinAddress.from_scriptPubKey(scriptPubKey) # Let's say we want to increase the fees by 5000 sats amount = 5000 # Bitcoind is nice and will create the first stage transaction first_txid = bitcoind.rpc.sendtoaddress(str(address), amount / COIN) vout_index = get_output_index( bitcoind.rpc.getrawtransaction(first_txid, 1), amount) # === We don't generate a block yet ! === tx.vin.append( CTxIn(COutPoint(lx(first_txid), vout_index), nSequence=0xfffffffe)) # Sign the new input with ALL tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL, amount, SIGVERSION_WITNESS_V0) sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL]) tx.wit.vtxinwit.append(CTxInWitness(CScriptWitness([sig, privkey.pub]))) return first_txid
def spend_hodl_redeemScript(privkey, nLockTime, unsigned_tx, n): """Spend a hodl output Returns the complete scriptSig """ redeemScript = hodl_redeemScript(privkey, nLockTime) sighash = SignatureHash(redeemScript, unsigned_tx, n, SIGHASH_ALL) sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) return CScript([sig, redeemScript])
def mkhash(basescript, tx, vin, sigflags, drop_codesep=0): if drop_codesep > 0: sclst = list(basescript) assert basescript == CScript(sclst) # not fancy for _ in range(dropcs): assert OP_CODESEPARATOR in sclst sclst = sclst[sclst.index(OP_CODESEPARATOR) + 1:] basescript = CScript(sclst) return SignatureHash(basescript, tx, vin, sigflags)
def settlement_sig(self): """Generate a signature for the settlement transaction.""" transaction = self.unsigned_settlement() transaction.vin[0].scriptSig = transaction.vin[0].scriptSig.to_script() for tx_out in transaction.vout: tx_out.scriptPubKey = tx_out.scriptPubKey.to_scriptPubKey() sighash = SignatureHash(self.anchor.scriptSig.redeem, transaction, 0, SIGHASH_ALL) sig = g.seckey.sign(sighash) + bytes([SIGHASH_ALL]) return sig
def spend_redeemScript(self, who, privkey, unsigned_tx: CTransaction, n_in): if who not in ('user', 'exch'): raise ValueError("who must be either user or exch") branch = OP_FALSE if who == 'user' else OP_TRUE redeemScript = self.deposit_redeemScript sighash = SignatureHash(redeemScript, unsigned_tx, n_in, SIGHASH_ALL) sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) return CScript([sig, branch, redeemScript])
def sig_hash(self, input, redeem_script=None): # FIXME: This is going to break if no redeem_script is supplied. # The idea in allowing it to be None was that in some cases, # the redeem_script could be determined from the input object. # This is probably a bad idead, so the default value for redeem_script # should be eliminated. # We only allow SIGHASH_ALL at this time # https://en.bitcoin.it/wiki/OP_CHECKSIG#Hashtype_SIGHASH_ALL_.28default.29 return SignatureHash(redeem_script.cscript, self.native(), input.index, SIGHASH_ALL)
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 sig_for_them(self): """Generate a signature for the mirror commitment transaction.""" transaction = CMutableTransaction([self.anchor], [self.their, self.our]) transaction = CMutableTransaction.from_tx(transaction) # copy # convert scripts to CScript transaction.vin[0].scriptSig = transaction.vin[0].scriptSig.to_script() for tx_out in transaction.vout: tx_out.scriptPubKey = tx_out.scriptPubKey.to_scriptPubKey() # sign sighash = SignatureHash(self.anchor.scriptSig.redeem, transaction, 0, SIGHASH_ALL) sig = g.seckey.sign(sighash) + bytes([SIGHASH_ALL]) return sig
def signed_settlement(self, their_sig): """Return the fully signed settlement transaction.""" transaction = self.unsigned_settlement() for tx_out in transaction.vout: tx_out.scriptPubKey = tx_out.scriptPubKey.to_scriptPubKey() sighash = SignatureHash(self.anchor.scriptSig.redeem, transaction, 0, SIGHASH_ALL) sig = g.seckey.sign(sighash) + bytes([SIGHASH_ALL]) transaction.vin[0].scriptSig.sig = their_sig transaction.vin[0].scriptSig = transaction.vin[0].scriptSig.to_script(sig) VerifyScript(transaction.vin[0].scriptSig, self.anchor.scriptSig.redeem.to_p2sh_scriptPubKey(), transaction, 0, (SCRIPT_VERIFY_P2SH,)) return transaction
def create_signature(self, privkey, reedem_script): """ Exports a raw signature suitable for use in a multisig transaction """ seckey = CBitcoinSecret.from_secret_bytes(x(bitcointools.encode_privkey(privkey, "hex"))) signatures = [] for i in range(len(self.tx.vin)): sighash = SignatureHash(CScript(x(reedem_script)), self.tx, i, SIGHASH_ALL) signatures.append({ "index": i, "signature": (seckey.sign(sighash) + struct.pack('<B', SIGHASH_ALL)).encode("hex") }) return signatures
def signed_commitment(self): """Return the fully signed commitment transaction.""" transaction = CMutableTransaction([self.anchor], [self.our, self.their]) transaction = CMutableTransaction.from_tx(transaction) for tx_out in transaction.vout: tx_out.scriptPubKey = tx_out.scriptPubKey.to_scriptPubKey() sighash = SignatureHash(self.anchor.scriptSig.redeem, transaction, 0, SIGHASH_ALL) sig = g.seckey.sign(sighash) + bytes([SIGHASH_ALL]) transaction.vin[0].scriptSig = transaction.vin[0].scriptSig.to_script(sig) # verify signing worked VerifyScript(transaction.vin[0].scriptSig, self.anchor.scriptSig.redeem.to_p2sh_scriptPubKey(), transaction, 0, (SCRIPT_VERIFY_P2SH,)) return transaction
def sign(self, privkey): """ Sign each of the inputs with the private key. Inputs should all be sent to the same scriptPubkey so we should only need one key. """ seckey = CBitcoinSecret.from_secret_bytes(x(bitcointools.encode_privkey(privkey, "hex"))) for i in range(len(self.tx.vin)): txin_scriptPubKey = self.tx.vin[i].scriptSig sighash = SignatureHash(txin_scriptPubKey, self.tx, i, SIGHASH_ALL) sig = seckey.sign(sighash) + struct.pack('<B', SIGHASH_ALL) self.tx.vin[i].scriptSig = CScript([sig, seckey.pub]) VerifyScript(self.tx.vin[i].scriptSig, txin_scriptPubKey, self.tx, i, (SCRIPT_VERIFY_P2SH,))
def split_coins(amount_to_send, txid_to_spend, utxo_index, n, network): txin_scriptPubKey = address.to_scriptPubKey() txin = create_txin(txid_to_spend, utxo_index) txout_scriptPubKey = address.to_scriptPubKey() txout = create_txout(amount_to_send / n, txout_scriptPubKey) tx = CMutableTransaction([txin], [txout] * n) sighash = SignatureHash(txin_scriptPubKey, tx, 0, SIGHASH_ALL) txin.scriptSig = CScript( [private_key.sign(sighash) + bytes([SIGHASH_ALL]), public_key]) VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH, )) response = broadcast_transaction(tx, network) print(response.status_code, response.reason) print(response.text)
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 redeem_after_timelock(contract): p2sh = contract.p2sh fundtx = find_transaction_to_address(p2sh) amount = fundtx['amount'] / COIN if (fundtx['address'].__str__() != p2sh): print("no fund transaction found to the contract p2sh address ", p2sh) quit() print("Found fundtx:", fundtx) # Parsing redeemblocknum from the redeemscript of the p2sh redeemblocknum = find_redeemblocknum(contract) blockcount = bitcoind.getblockcount() print("Current block:", blockcount, "Can redeem from block:", redeemblocknum) if (still_locked(contract)): print("too early for redeeming with timelock try again at block", redeemblocknum, "or later") return 0 print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh)) redeemPubKey = find_refundAddr(contract) print('refundPubKey', redeemPubKey) redeemScript = CScript(x(contract.redeemScript)) txin = CMutableTxIn(fundtx['outpoint']) txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey()) # Create the unsigned raw transaction. txin.nSequence = 0 tx = CMutableTransaction([txin], [txout]) tx.nLockTime = redeemblocknum sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL) # TODO: figure out how to better protect privkey privkey = bitcoind.dumpprivkey(redeemPubKey) sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript]) # exit() # print("txin.scriptSig", b2x(txin.scriptSig)) txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() # print('Redeem txhex', b2x(tx.serialize())) VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH, )) print("script verified, sending raw tx") txid = bitcoind.sendrawtransaction(tx) print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid)))) return b2x(lx(b2x(txid)))
def partial_spend_p2sh(redeemScript, rein, daddr=None, alt_amount=None, alt_daddr=None): if daddr is None: daddr = rein.user.daddr txin_redeemScript = CScript(x(redeemScript)) txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) (txins, total_value) = unspent_txins(txin_p2sh_address, rein.testnet) if len(txins) == 0: raise ValueError( 'Primary escrow is empty. Please inform client to add funds.') txins_str = "" txins_obj = [] for txid, vout in txins: txins_str += " " + txid + "-" + str(vout) txins_obj.append(CMutableTxIn(COutPoint(lx(txid), vout))) fee = float(PersistConfig.get(rein, 'fee', 0.001)) amount = round(total_value - fee, 8) if alt_amount: amount = round(amount - alt_amount, 8) if amount <= 0. or alt_amount > total_value - fee: click.echo("amount: " + str(amount) + " alt_amount: " + str(alt_amount) + " total_value: " + str(total_value)) raise ValueError( 'Primary escrow balance too low. Please inform client to add funds.' ) txouts = [] txout = CMutableTxOut(amount * COIN, CBitcoinAddress(daddr).to_scriptPubKey()) txouts.append(txout) if alt_amount: txout_alt = CMutableTxOut( round(alt_amount, 8) * COIN, CBitcoinAddress(alt_daddr).to_scriptPubKey()) txouts.append(txout_alt) tx = CMutableTransaction(txins_obj, txouts) ntxins = len(txins_obj) seckey = CBitcoinSecret(rein.user.dkey) sig = "" for i in range(0, ntxins): sighash = SignatureHash(txin_redeemScript, tx, i, SIGHASH_ALL) sig += " " + b2x(seckey.sign(sighash)) + "01" if alt_amount: return (txins_str[1:], "{:.8f}".format(amount), daddr, "{:.8f}".format(alt_amount), alt_daddr, sig[1:]) return (txins_str[1:], "{:.8f}".format(amount), daddr, sig[1:])
def sign_spend_tx(tx, privkey, pubkeys, pub_server, prev_value): """Signs the transaction which spends the unvault_tx after the relative locktime with the given private keys. :param tx: The unsigned transaction, a CMutableTransaction. :param privkey: (bytes) The private key to sign the transaction with. :param pubkeys: A list of the 4 stakeholders' pubkeys, to form the script. :param pub_server: The public key of the cosigning server, to form the script. :param prev_value: The prevout's value in satoshis. :return: A list of the signature for each given private key. """ tx_hash = SignatureHash(unvault_script(*pubkeys, pub_server), tx, 0, SIGHASH_ALL, prev_value, SIGVERSION_WITNESS_V0) return CKey(privkey).sign(tx_hash) + bytes([SIGHASH_ALL])
def redeem_with_secret(contract, secret): # How to find redeemScript and redeemblocknum from blockchain? print("Redeeming contract using secret", contract.__dict__) p2sh = contract.p2sh minamount = float(contract.amount) #checking there are funds in the address amount = check_funds(p2sh) if (amount < minamount): print("address ", p2sh, " not sufficiently funded") return false fundtx = find_transaction_to_address(p2sh) amount = fundtx['amount'] / COIN p2sh = P2SHBitcoinAddress(p2sh) if fundtx['address'] == p2sh: print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh)) redeemPubKey = find_redeemAddr(contract) print('redeemPubKey', redeemPubKey) 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(redeemScript, tx, 0, SIGHASH_ALL) # TODO: figure out how to better protect privkey privkey = bitcoind.dumpprivkey(redeemPubKey) sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) print("SECRET", secret) preimage = secret.encode('utf-8') txin.scriptSig = CScript( [sig, privkey.pub, preimage, OP_TRUE, redeemScript]) # exit() # print("txin.scriptSig", b2x(txin.scriptSig)) txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() # print('Redeem txhex', b2x(tx.serialize())) VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH, )) print("script verified, sending raw tx") txid = bitcoind.sendrawtransaction(tx) print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid)))) return b2x(lx(b2x(txid))) else: print("No contract for this p2sh found in database", p2sh)
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 add_input_output(bitcoind, tx): """Add an input and an output to a CMutableTransaction, SIGHASH_ALL.""" # First we get some coins privkey = CKey(os.urandom(32)) scriptPubKey = CScript([OP_0, Hash160(privkey.pub)]) address = CBitcoinAddress.from_scriptPubKey(scriptPubKey) amount = Decimal("50") * Decimal(COIN) - Decimal("500") # This creates a one-output transaction txid = bitcoind.pay_to(str(address), amount / Decimal(COIN)) # We bump the fees by 5000 tx.vout.append(CTxOut(amount - Decimal("5000"), scriptPubKey)) tx.vin.append(CTxIn(COutPoint(lx(txid), 0))) # Sign the new output with ALL tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL, int(amount), SIGVERSION_WITNESS_V0) sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL]) tx.wit.vtxinwit.append(CTxInWitness(CScriptWitness([sig, privkey.pub])))
def sign_unvault_spend(tx, privkeys, pubkeys, pub_server, prev_value): """Signs a transaction spending from an unvault transaction. This is the "all stakeholders sign" path of the script, not encumbered by a timelock. This path is used for both the emergency and cancel transactions. :param tx: The unsigned transaction, a CMutableTransaction. :param privkeys: The private keys to sign the transaction with (a list). :param pubkeys: The pubkeys of the stakeholders. :param pub_server: The pubkey of the cosigning server. :param prev_value: The prevout's value in satoshis. :return: The signatures for the provided privkeys (a list). """ tx_hash = SignatureHash(unvault_script(*pubkeys, pub_server), tx, 0, SIGHASH_ALL, prev_value, SIGVERSION_WITNESS_V0) return [CKey(key).sign(tx_hash) + bytes([SIGHASH_ALL]) for key in privkeys]