def refund_tx(self, payer_sig, serial_tx, redeem_script): ''' Sends a transaction refunding the funder of the P2SH address. ''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Set script sig txin.scriptSig = CScript([payer_sig + '\x01', OP_FALSE, redeem_script]) # Verify script redeem_script = CScript(redeem_script) VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) serial_tx = tx.serialize() if not self.test: # txid = self.self.proxy.sendrawtransaction(tx) txid = b2lx(Hash(serial_tx)) else: txid = b2lx(Hash(serial_tx)) self.logger.info("refund_tx: TXID is %s", txid) self.logger.info("refund_tx: RAW TX is %s", b2x(serial_tx)) return serial_tx
def spend_escrow(serial_tx, redeem_script, payer_sig, redeemer_sig): ''' Sends a transaction fulfilling the redeem script of escrow tx ''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Set script sig txin.scriptSig = CScript([OP_0, payer_sig + '\x01', redeemer_sig + '\x01', redeem_script]) # Verify script redeem_script = CScript(redeem_script) serial_tx = tx.serialize() VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) serial_tx = tx.serialize() txid = b2lx(Hash(serial_tx)) print("spend_escrow: TXID is %s" % txid) print("spend_escrow: RAW TX is %s" % b2x(serial_tx)) return serial_tx
def spend_escrow(self, payer_sig, redeemer_sig, serial_tx, redeem_script): ''' Sends a transaction fulfilling the redeem script of escrow tx ''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Set script sig txin.scriptSig = CScript([ OP_FALSE, payer_sig + '\x01', redeemer_sig + '\x01', OP_TRUE, redeem_script ]) # Verify script redeem_script = CScript(redeem_script) serial_tx = tx.serialize() VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) serial_tx = tx.serialize() if not self.test: # txid = self.proxy.sendrawtransaction(tx) txid = b2lx(Hash(serial_tx)) else: txid = b2lx(Hash(serial_tx)) self.logger.info("spend_escrow: TXID is %s", txid) self.logger.info("spend_escrow: RAW TX is %s", b2x(serial_tx)) return serial_tx
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 unsigned_settlement(self): """Generate the settlement transaction.""" # Put outputs in the order of the inputs, so that both versions are the same if self.anchor.scriptSig.my_index == 0: transaction = CMutableTransaction([self.anchor], [self.our, self.their]) elif self.anchor.scriptSig.my_index == 1: transaction = CMutableTransaction([self.anchor], [self.their, self.our]) else: raise Exception("Unknown index", self.anchor.scriptSig.my_index) return CMutableTransaction.from_tx(transaction)
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_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 spend_preimage(redeem_script, preimages, redeemer_sig, serial_tx): """ Creates a transaction fulfilling the redeem script of the preimage P2SH. Arguements: redeem_script (bytes): The script that specifies the conditions that a tx has to fulfill to transfer funds from the `funding_tx` preimages (list): The preimages that hash into the hash values specified in the `redeem_script` redeemer_sig (bytes): The signature of the redeemer on the `serial_tx` serial_tx (bytes): The serial transaction Returns: The serial raw transaction that passes the script verification """ # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Setup preimages in reverse order script = [] for p in reversed(preimages): script += [p] # Create script sig txin.scriptSig = CScript([redeemer_sig + '\x01'] + script + [redeem_script]) # Verify script redeem_script = CScript(redeem_script) try: VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) except ValidationError: print("spend_preimage: Script failed to verify") return None serial_tx = tx.serialize() txid = b2lx(Hash(serial_tx)) print("spend_preimage: TXID is %s" % txid) print("spend_preimage: RAW TX is %s" % b2x(serial_tx)) return serial_tx
def get(cls, address): """Get a Channel with the specified address.""" row = g.dat.execute( "SELECT * from CHANNELS WHERE address = ?", (address,)).fetchone() if row is None: raise Exception("Unknown address", address) address, commitment = row commitment = CMutableTransaction.deserialize(commitment) commitment = CMutableTransaction.from_tx(commitment) assert len(commitment.vin) == 1 assert len(commitment.vout) == 2 commitment.vin[0].scriptSig = AnchorScriptSig.from_script( commitment.vin[0].scriptSig) for tx_out in commitment.vout: tx_out.scriptPubKey = CBitcoinAddress.from_scriptPubKey(tx_out.scriptPubKey) return cls(address, commitment.vin[0], commitment.vout[0], commitment.vout[1])
def spend_escrow(redeem_script, payer_sig, redeemer_sig, serial_tx): """ Creates a transaction fulfilling the redeem script of the escrow P2SH. Arguements: redeem_script (bytes): The script that specifies the conditions that a tx has to fulfill to transfer funds from the `funding_tx` payer_sig (bytes): The signature of the payer on the `serial_tx` redeemer_sig (bytes): The signature of the redeemer on the `serial_tx` serial_tx (bytes): The serial transaction Returns: The serial raw transaction that passes the script verification """ # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Set script sig txin.scriptSig = CScript( [OP_0, payer_sig + '\x01', redeemer_sig + '\x01', redeem_script]) # Verify script redeem_script = CScript(redeem_script) serial_tx = tx.serialize() try: VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) except ValidationError: print("spend_escrow: Script failed to verify") return None serial_tx = tx.serialize() txid = b2lx(Hash(serial_tx)) print("spend_escrow: TXID is %s" % txid) print("spend_escrow: RAW TX is %s" % b2x(serial_tx)) return serial_tx
def get_keys_from_tx(self, serial_tx, n_keys=15): '''Extracts n_keys from tx in serial form''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) # Keys are in txin.scriptSig txin = tx.vin[0] script = txin.scriptSig # Extract keys from script keys = [] for i, op in enumerate(script): if i in range(1, n_keys + 1): keys += [op] # Serialize keys in correct order serial_keys = "" for op in reversed(keys): serial_keys += op return serial_keys
def spend_preimage(self, preimages, redeemer_sig, serial_tx, redeem_script): ''' Sends a transaction fulfilling the redeem script of the preimage P2SH ''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Setup preimages in reverse order script = [] for p in reversed(preimages): script += [p] # Create script sig txin.scriptSig = CScript([redeemer_sig + '\x01'] + script + [OP_TRUE, redeem_script]) # Verify script redeem_script = CScript(redeem_script) VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) serial_tx = tx.serialize() if not self.test: # txid = self.proxy.sendrawtransaction(tx) txid = b2lx(Hash(serial_tx)) else: txid = b2lx(Hash(serial_tx)) self.logger.info("spend_preimage: TXID is %s", txid) self.logger.info("spend_preimage: RAW TX is %s", b2x(serial_tx)) return serial_tx
if unspent_tx.vout[0].nValue < args.amount: continue if check_full_rbf_optin(unspent_tx): tx1 = unspent_tx logging.info('Found unconfirmed full-RBF tx1 %s' % b2lx(txid)) break else: txids_seen.add(txid) else: logging.info('No unconfirmed full-RBF tx1 found; creating new tx') tx2 = CMutableTransaction.from_tx(tx1) if tx1 is not None else CMutableTransaction() # There might be a better way to fund the new outputs, so delete all but the # first input. Of course, we can't delete all the inputs - the new transaction # wouldn't conflict with the old one and you'd pay everyone twice! tx2.vin = tx2.vin[0:1] if not args.first_seen_safe and len(tx2.vout) > 0: # Delete the change output. # # Unfortunately there isn't any way to ask Bitcoin Core if a given address # is a change address; if you're sending yourself funds to test the feature # it's not possible to distinguish change from send-to-self outputs. # # So instead we always build transactions such that the first output is # change, and we delete only that output. Not pretty - you don't want to do
# FIXME: this still fails if amount doesn't leave enough for fees if unspent_tx.vout[0].nValue < args.amount: continue if check_full_rbf_optin(unspent_tx): tx1 = unspent_tx logging.info('Found unconfirmed full-RBF tx1 %s' % b2lx(txid)) break else: txids_seen.add(txid) else: logging.info('No unconfirmed full-RBF tx1 found; creating new tx') tx2 = CMutableTransaction.from_tx( tx1) if tx1 is not None else CMutableTransaction() # There might be a better way to fund the new outputs, so delete all but the # first input. Of course, we can't delete all the inputs - the new transaction # wouldn't conflict with the old one and you'd pay everyone twice! tx2.vin = tx2.vin[0:1] if not args.first_seen_safe and len(tx2.vout) > 0: # Delete the change output. # # Unfortunately there isn't any way to ask Bitcoin Core if a given address # is a change address; if you're sending yourself funds to test the feature # it's not possible to distinguish change from send-to-self outputs. # # So instead we always build transactions such that the first output is # change, and we delete only that output. Not pretty - you don't want to do
try: args.txid = lx(args.txid) except ValueError as err: parser.error('Invalid txid: %s' % str(err)) if len(args.txid) != 32: parser.error('Invalid txid: Wrong length.') try: rpc.gettransaction(args.txid) except IndexError as err: parser.exit('Invalid txid: Not in wallet.') txinfo = rpc.getrawtransaction(args.txid, True) tx = CMutableTransaction.from_tx(txinfo['tx']) if 'confirmations' in txinfo and txinfo['confirmations'] > 0: parser.exit("Transaction already mined; %d confirmations." % txinfo['confirmations']) # Find a txout that was being used for change change_txout = None for vout in tx.vout: try: addr = CBitcoinAddress.from_scriptPubKey(vout.scriptPubKey) except ValueError: continue if rpc.validateaddress(addr)['ismine']: change_txout = vout break
try: args.txid = lx(args.txid) except ValueError as err: parser.error('Invalid txid: %s' % str(err)) if len(args.txid) != 32: parser.error('Invalid txid: Wrong length.') try: rpc.gettransaction(args.txid) except IndexError as err: parser.exit('Invalid txid: Not in wallet.') txinfo = rpc.getrawtransaction(args.txid, True) tx = CMutableTransaction.from_tx(txinfo['tx']) if 'confirmations' in txinfo and txinfo['confirmations'] > 0: parser.exit("Transaction already mined; %d confirmations." % txinfo['confirmations']) # Find a txout that was being used for change change_txout = None for vout in tx.vout: try: addr = CBitcoinAddress.from_scriptPubKey(vout.scriptPubKey) except ValueError: continue if rpc.validateaddress(addr)['ismine']: change_txout = vout
new_txin = CMutableTxIn(new_outpoint) tx.vin.append(new_txin) value_in += new_amount change_txout.nValue += new_amount value_out += new_amount # Resign the tx so we can figure out how large the new input's scriptSig will be. r = rpc.signrawtransaction(tx) assert(r['complete']) tx.vin[-1].scriptSig = r['tx'].vin[-1].scriptSig r = rpc.signrawtransaction(tx) assert(r['complete']) tx = CMutableTransaction.from_tx(r['tx']) logging.debug('Payment tx %s' % b2x(tx.serialize())) logging.info('Payment tx size: %.3f KB, fees: %s, %s BTC/KB' % \ (len(tx.serialize()) / 1000, str_money_value(value_in-value_out), str_money_value((value_in-value_out) / len(tx.serialize()) * 1000))) if not args.dryrun: txid = rpc.sendrawtransaction(tx) logging.info('Sent payment tx: %s' % b2lx(txid)) if not args.dryrun: logging.info('Sleeping for %d seconds' % args.delay) time.sleep(args.delay)
new_txin = CMutableTxIn(new_outpoint, nSequence=tx1_nSequence) tx.vin.append(new_txin) value_in += new_amount change_txout.nValue += new_amount value_out += new_amount r = rpc.signrawtransaction(tx) assert(r['complete']) tx.vin[-1].scriptSig = r['tx'].vin[-1].scriptSig r = rpc.signrawtransaction(tx) assert(r['complete']) tx = CMutableTransaction.from_tx(r['tx']) print('Payment raw transaction %s' % b2x(tx.serialize())) print('Payment raw transaction size: %.3f KB, fees: %s, %s BTC/KB' % \ (len(tx.serialize()) / 1000, str_money_value(value_in-value_out), str_money_value((value_in-value_out) / len(tx.serialize()) * 1000))) txid = rpc.sendrawtransaction(tx) print('Sent payment with txid: %s' % b2lx(txid)) print('Waiting for %d seconds before double spending' % 2) time.sleep(10)
def test_increase_revault_tx_feerate(bitcoind): """This tests that any of the stakeholders can increase the feerate of any of the revaulting transactions in a timely manner. Will justice rule?""" # The stakeholders, the first two are the traders. stk_privkeys = [os.urandom(32) for i in range(4)] stk_pubkeys = [CKey(k).pub for k in stk_privkeys] # Same, but for the EDV emer_privkeys = [os.urandom(32) for i in range(4)] emer_pubkeys = [CKey(k).pub for k in emer_privkeys] # The co-signing server, required by the spend tx serv_privkey = os.urandom(32) serv_pubkey = CKey(serv_privkey).pub # Test the vault emergency amount_vault = 50 * COIN - 500 txid = send_vault_tx(bitcoind, stk_pubkeys, amount_vault) amount_emer = amount_vault - 500 CTx = create_emergency_vault_tx(lx(txid), 0, amount_emer, emer_pubkeys) sigs = [ sign_emergency_vault_tx(CTx, p, stk_pubkeys, amount_vault) for p in stk_privkeys ] # Sanity checks don't hurt assert all(sig[-1] == SIGHASH_ALL | SIGHASH_ANYONECANPAY for sig in sigs) CMTx = CMutableTransaction.from_tx( form_emergency_vault_tx(CTx, stk_pubkeys, sigs)) fees_before = tx_fees(bitcoind, CMTx) first_txid = creates_add_input(bitcoind, CMTx) fees_after = tx_fees(bitcoind, CMTx) assert fees_after > fees_before bitcoind.send_tx(CMTx.serialize().hex(), wait_for_mempool=[first_txid]) # Test the emer unvault amount_vault = 50 * COIN - 500 amount_unvault = amount_vault - 500 txid = send_unvault_tx(bitcoind, stk_privkeys, stk_pubkeys, serv_pubkey, amount_vault, amount_unvault) amount_emer = amount_unvault - 500 CTx = create_emer_unvault_tx(txid, 0, emer_pubkeys, amount_emer) sigs = [ sign_emer_unvault_tx(CTx, p, stk_pubkeys, serv_pubkey, amount_unvault) for p in stk_privkeys ] # Sanity checks don't hurt assert all(sig[-1] == SIGHASH_ALL | SIGHASH_ANYONECANPAY for sig in sigs) CMTx = CMutableTransaction.from_tx( form_emer_unvault_tx(CTx, sigs, stk_pubkeys, serv_pubkey)) fees_before = tx_fees(bitcoind, CMTx) first_txid = creates_add_input(bitcoind, CMTx) fees_after = tx_fees(bitcoind, CMTx) assert fees_after > fees_before bitcoind.send_tx(CMTx.serialize().hex(), wait_for_mempool=[first_txid]) # Test the cancel unvault amount_vault = 50 * COIN - 500 amount_unvault = amount_vault - 500 txid = send_unvault_tx(bitcoind, stk_privkeys, stk_pubkeys, serv_pubkey, amount_vault, amount_unvault) amount_cancel = amount_unvault - 500 CTx = create_cancel_tx(txid, 0, emer_pubkeys, amount_cancel) sigs = [ sign_cancel_tx(CTx, p, stk_pubkeys, serv_pubkey, amount_unvault) for p in stk_privkeys ] # Sanity checks don't hurt assert all(sig[-1] == SIGHASH_ALL | SIGHASH_ANYONECANPAY for sig in sigs) CMTx = CMutableTransaction.from_tx( form_cancel_tx(CTx, sigs, stk_pubkeys, serv_pubkey)) fees_before = tx_fees(bitcoind, CMTx) first_txid = creates_add_input(bitcoind, CMTx) fees_after = tx_fees(bitcoind, CMTx) assert fees_after > fees_before bitcoind.send_tx(CMTx.serialize().hex(), wait_for_mempool=[first_txid])