def make_unsigned(cls, outpoints, outputs, tx_fee=TRANSACTION_FEE, testnet=False, out_value=None): """ Build an unsigned transaction. Args: outpoints: A `list` of `dict` objects which contain a txid, vout, value, and scriptPubkey. outputs: If a single address the full value of the inputs (minus the tx fee) will be sent there. Otherwise it should be a `list` of `dict` objects containing address and value. tx_fee: The Bitcoin network fee to be paid on this transaction. testnet: Should this transaction be built for testnet? out_value: used if you want to specify a specific output value otherwise the full value of the inputs (minus the tx fee) will be used. """ # build the inputs from the outpoints object SelectParams("testnet" if testnet else "mainnet") txins = [] in_value = 0 for outpoint in outpoints: in_value += outpoint["value"] txin = CMutableTxIn(COutPoint(lx(outpoint["txid"]), outpoint["vout"])) txin.scriptSig = CScript(x(outpoint["scriptPubKey"])) txins.append(txin) # build the outputs txouts = [] if isinstance(outputs, list): for output in outputs: value = output["value"] address = output["address"] txouts.append(CMutableTxOut(value, CBitcoinAddress(address).to_scriptPubKey())) else: value = out_value if out_value is not None else (in_value - tx_fee) txouts.append(CMutableTxOut(value, CBitcoinAddress(outputs).to_scriptPubKey())) # make the transaction tx = CMutableTransaction(txins, txouts) return BitcoinTransaction(tx)
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 Options: vout -> which vout of funding_tx nLockTime -> set's transaction locktime ''' # 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 generate_multisig_address(redeemscript: str, testnet: bool = False) -> str: """ Generates a P2SH-multisig Bitcoin address from a redeem script Args: redeemscript: hex-encoded redeem script use generate_multisig_redeem_script to create the redeem script from three compressed public keys testnet: Should the address be testnet or mainnet? Example: TODO """ if testnet: bitcoin.SelectParams('testnet') else: bitcoin.SelectParams('mainnet') redeem_script = CScript(x(redeemscript)) addr = P2SHBitcoinAddress.from_redeemScript(redeem_script) return str(addr)
def make_p2sh_encoding_redeemscript(datachunk, n, pubKey=None, multisig_pubkeys=None, multisig_pubkeys_required=None): _logger = logger.getChild('p2sh_encoding') assert len(datachunk) <= bitcoinlib.core.script.MAX_SCRIPT_ELEMENT_SIZE dataDropScript = [datachunk, bitcoinlib.core.script.OP_DROP] # just drop the data chunk cleanupScript = [n, bitcoinlib.core.script.OP_DROP, bitcoinlib.core.script.OP_DEPTH, 0, bitcoinlib.core.script.OP_EQUAL] # unique offset + prevent scriptSig malleability if pubKey is not None: # a p2pkh script looks like this: {pubkey} OP_CHECKSIGVERIFY verifyOwnerScript = [pubKey, bitcoinlib.core.script.OP_CHECKSIGVERIFY] elif multisig_pubkeys_required is not None and multisig_pubkeys: # a 2-of-3 multisig looks like this: # 2 {pubkey1} {pubkey2} {pubkey3} 3 OP_CHECKMULTISIGVERIFY multisig_pubkeys_required = int(multisig_pubkeys_required) if multisig_pubkeys_required < 2 or multisig_pubkeys_required > 15: raise exceptions.TransactionError('invalid multisig pubkeys value') verifyOwnerScript = [multisig_pubkeys_required] for multisig_pubkey in multisig_pubkeys: verifyOwnerScript.append(multisig_pubkey) verifyOwnerScript = verifyOwnerScript + [len(multisig_pubkeys), bitcoinlib.core.script.OP_CHECKMULTISIGVERIFY] else: raise exceptions.TransactionError('Either pubKey or multisig pubKeys must be provided') #redeemScript = CScript(datachunk) + CScript(dataDropScript + verifyOwnerScript + cleanupScript) redeemScript = CScript(dataDropScript + verifyOwnerScript + cleanupScript) _logger.debug('datachunk %s' % (binascii.hexlify(datachunk))) _logger.debug('dataDropScript %s (%s)' % (repr(CScript(dataDropScript)), binascii.hexlify(CScript(dataDropScript)))) _logger.debug('verifyOwnerScript %s (%s)' % (repr(CScript(verifyOwnerScript)), binascii.hexlify(CScript(verifyOwnerScript)))) _logger.debug('entire redeemScript %s (%s)' % (repr(redeemScript), binascii.hexlify(redeemScript))) #scriptSig = CScript([]) + redeemScript # PUSH(datachunk) + redeemScript scriptSig = CScript([redeemScript]) outputScript = redeemScript.to_p2sh_scriptPubKey() _logger.debug('scriptSig %s (%s)' % (repr(scriptSig), binascii.hexlify(scriptSig))) _logger.debug('outputScript %s (%s)' % (repr(outputScript), binascii.hexlify(outputScript))) # outputScript looks like OP_HASH160 {{ hash160([redeemScript]) }} OP_EQUALVERIFY # redeemScript looks like OP_DROP {{ pubkey }} OP_CHECKSIGVERIFY {{ n }} OP_DROP OP_DEPTH 0 OP_EQUAL # scriptSig is {{ datachunk }} OP_DROP {{ pubkey }} OP_CHECKSIGVERIFY {{ n }} OP_DROP OP_DEPTH 0 OP_EQUAL return scriptSig, redeemScript, outputScript
# Make a private key with Trezor if you have one # seckey = get_tz_priv(coin, path) # Otherwise pull a private key from Core # seckey = get_core_priv() # Or just hardcode it seckey = CBitcoinSecret('cQNjiPwYKMBr2oB3bWzf3rgBsu198xb8Nxxe51k6D3zVTA98L25N') print("wif", seckey) print("pubk", b2x(seckey.pub)) expiry = lx(hex(nLockTime).replace('0x', '')) # Create a redeemScript. Similar to a scriptPubKey the redeemScript must be # satisfied for the funds to be spent. txin_redeemScript = CScript( [expiry, OP_CHECKLOCKTIMEVERIFY, OP_DROP, seckey.pub, OP_CHECKSIG]) print("redeem", b2x(txin_redeemScript)) # Create the magic P2SH scriptPubKey format from that redeemScript. You should # look at the CScript.to_p2sh_scriptPubKey() function in bitcoin.core.script to # understand what's happening, as well as read BIP16: # https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() # Convert the P2SH scriptPubKey to a base58 Bitcoin address and print it. # You'll need to send some funds to it to create a txout to spend. txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) print('Pay to:', str(txin_p2sh_address)) # Same as the txid:vout the createrawtransaction RPC call requires #
def deposit_redeemScript(self): return CScript([OP_IF] + list(self.exch_scriptPubkey) + [OP_ELSE, self.locktime, OP_NOP2, OP_DROP] + list(self.user_scriptPubKey) + [OP_ENDIF])
def T(hex_scriptpubkey, expected_str_address, expected_class): scriptPubKey = CScript(x(hex_scriptpubkey)) addr = CBitcoinAddress.from_scriptPubKey(scriptPubKey) self.assertEqual(str(addr), expected_str_address) self.assertEqual(addr.__class__, expected_class)
def build_2_of_3(pubkeys): txin_redeemScript = CScript([2, x(pubkeys[0]), x(pubkeys[1]), x(pubkeys[2]), 3, OP_CHECKMULTISIG]) txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) return (b2x(txin_redeemScript), str(txin_p2sh_address))
def build_claim_tx(self, txid_hex): if self.secret_info["plaintext"] == None: raise Exception("We don't know the secret yet.") #Create redeem script. redeem_script = self.fund_redeem_script(self.their) #Convert to p2sh address. their_fund_address = self.script_to_address(redeem_script, self.their) #Check there is enough in the p2sh address. their_fund_tx = self.jsonrpc[self.their].gettransaction( txid_hex)["details"] found = 0 for tx_input in their_fund_tx: #Check it's the right input. if tx_input["address"] == their_fund_address: found = 1 if tx_input["amount"] + self.recv_amount > decimal.Decimal( coinbend.config["mining_fee"]["standard"]): raise Exception( "Their contract has not been sufficently funded.") break else: continue #Their side of the contract hasn't been funded. if not found: raise Exception("Their contract fund output was not detected.") #Generate address to receive redeemed output. if "receive" not in self.key_pairs: self.key_pairs["receive"] = self.key_pair_from_address( self.jsonrpc[self.their].getnewaddress(), self.their) #Load private key for signing. seckey = CBitcoinSecret(self.key_pairs[self.my]["priv"]["wif"]) #Generate p2sh script pub key. redeem_script_hash160 = self.hash160_script(redeem_script) txin_script_pub_key = CScript( [OP_HASH160, redeem_script_hash160["bin"], OP_EQUAL]) #Setup tx inputs and outputs. txid = lx(txid_hex) vout = 0 txin = CTxIn(COutPoint(txid, vout)) txout = CTxOut( (self.recv_amount - decimal.Decimal(coinbend.config["mining_fee"]["standard"])) * COIN, CBitcoinAddress( self.key_pairs["receive"]["addr"]["base58"]).to_scriptPubKey()) #Create unsigned transaction. tx = CTransaction([txin], [txout]) #Sign transactions. sighash = SignatureHash(redeem_script["bin"], tx, 0, SIGHASH_ALL) sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) txin.scriptSig = CScript([ bytes(self.secret_info["plaintext"].encode("ascii")), sig, OP_3, redeem_script["bin"] ]) #Return signed transaction hex. return b2x(tx.serialize())
def test_from_redeemScript(self): addr = P2SHBitcoinAddress.from_redeemScript(CScript()) self.assertEqual(str(addr), '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
def __do_bitcoin(self): """Do Bitcoin-related maintenance""" # FIXME: we shouldn't have to create a new proxy each time, but with # current python-bitcoinlib and the RPC implementation it seems that # the proxy connection can timeout w/o recovering properly. proxy = bitcoin.rpc.Proxy() new_blocks = self.known_blocks.update_from_proxy(proxy) for (block_height, block_hash) in new_blocks: logging.info("New block %s at height %d" % (b2lx(block_hash), block_height)) # Save commitments to disk that have reached min_confirmations confirmed_tx = self.txs_waiting_for_confirmation.pop(block_height - self.min_confirmations + 1, None) if confirmed_tx is not None: self.__save_confirmed_timestamp_tx(confirmed_tx) # If there already are txs waiting for confirmation at this # block_height, there was a reorg and those pending commitments now # need to be added back to the pool reorged_tx = self.txs_waiting_for_confirmation.pop(block_height, None) if reorged_tx is not None: # FIXME: the reorged transaction might get mined in another # block, so just adding the commitments for it back to the pool # isn't ideal, but it is safe logging.info('tx %s at height %d removed by reorg, adding %d commitments back to pending' % (b2lx(reorged_tx.tx.GetHash()), block_height, len(reorged_tx.commitment_timestamps))) for reorged_commitment_timestamp in reorged_tx.commitment_timestamps: self.pending_commitments.add(reorged_commitment_timestamp.msg) # Check if this block contains any of the pending transactions try: block = proxy.getblock(block_hash) except KeyError: # Must have been a reorg or something, return logging.error("Failed to get block") return # Check all potential pending txs against this block. for unconfirmed_tx in self.unconfirmed_txs: block_timestamp = make_timestamp_from_block(unconfirmed_tx.tip_timestamp.msg, block, block_height) if block_timestamp is None: continue # Success! (tip_timestamp, commitment_timestamps) = self.__pending_to_merkle_tree(unconfirmed_tx.n) mined_tx = TimestampTx(unconfirmed_tx.tx, tip_timestamp, commitment_timestamps) assert tip_timestamp.msg == unconfirmed_tx.tip_timestamp.msg mined_tx.tip_timestamp.merge(block_timestamp) for commitment in tuple(self.pending_commitments)[0:unconfirmed_tx.n]: self.pending_commitments.remove(commitment) logging.debug("Removed commitment %s from pending" % b2x(commitment)) assert self.min_confirmations > 1 logging.info("Success! %d commitments timestamped, now waiting for %d more confirmations" % (len(mined_tx.commitment_timestamps), self.min_confirmations - 1)) # Add pending_tx to the list of timestamp transactions that # have been mined, and are waiting for confirmations. self.txs_waiting_for_confirmation[block_height] = mined_tx # Since all unconfirmed txs conflict with each other, we can clear the entire lot self.unconfirmed_txs.clear() # And finally, we can reset the last time a timestamp # transaction was mined to right now. self.last_timestamp_tx = time.time() break time_to_next_tx = int(self.last_timestamp_tx + self.min_tx_interval - time.time()) if time_to_next_tx > 0: # Minimum interval between transactions hasn't been reached, so do nothing logging.debug("Waiting %ds before next tx" % time_to_next_tx) return prev_tx = None if self.pending_commitments and not self.unconfirmed_txs: # Find the biggest unspent output that's confirmed unspent = find_unspent(proxy) if not len(unspent): logging.error("Can't timestamp; no spendable outputs") return # For the change scriptPubKey, we can save a few bytes by using # a pay-to-pubkey rather than the usual pay-to-pubkeyhash change_addr = proxy.getnewaddress() change_pubkey = proxy.validateaddress(change_addr)['pubkey'] change_scriptPubKey = CScript([change_pubkey, OP_CHECKSIG]) prev_tx = self.__create_new_timestamp_tx_template(unspent[-1]['outpoint'], unspent[-1]['amount'], change_scriptPubKey) logging.debug('New timestamp tx, spending output %r, value %s' % (unspent[-1]['outpoint'], str_money_value(unspent[-1]['amount']))) elif self.unconfirmed_txs: assert self.pending_commitments (prev_tx, prev_tip_timestamp, prev_commitment_timestamps) = self.unconfirmed_txs[-1] # Send the first transaction even if we don't have a new block if prev_tx and (new_blocks or not self.unconfirmed_txs): (tip_timestamp, commitment_timestamps) = self.__pending_to_merkle_tree(len(self.pending_commitments)) # make_merkle_tree() seems to take long enough on really big adds # that the proxy dies proxy = bitcoin.rpc.Proxy() sent_tx = None relay_feerate = self.relay_feerate while sent_tx is None: unsigned_tx = self.__update_timestamp_tx(prev_tx, tip_timestamp.msg, proxy.getblockcount(), relay_feerate) fee = _get_tx_fee(unsigned_tx, proxy) if fee is None: logging.debug("Can't determine txfee of transaction; skipping") return if fee > self.max_fee: logging.error("Maximum txfee reached!") return r = proxy.signrawtransaction(unsigned_tx) if not r['complete']: logging.error("Failed to sign transaction! r = %r" % r) return signed_tx = r['tx'] try: txid = proxy.sendrawtransaction(signed_tx) except bitcoin.rpc.JSONRPCError as err: if err.error['code'] == -26: logging.debug("Err: %r" % err.error) # Insufficient priority - basically means we didn't # pay enough, so try again with a higher feerate relay_feerate *= 2 continue else: raise err # something else, fail! sent_tx = signed_tx if self.unconfirmed_txs: logging.info("Sent timestamp tx %s, replacing %s; %d total commitments; %d prior tx versions" % (b2lx(sent_tx.GetHash()), b2lx(prev_tx.GetHash()), len(commitment_timestamps), len(self.unconfirmed_txs))) else: logging.info("Sent timestamp tx %s; %d total commitments" % (b2lx(sent_tx.GetHash()), len(commitment_timestamps))) self.unconfirmed_txs.append(UnconfirmedTimestampTx(sent_tx, tip_timestamp, len(commitment_timestamps)))
def create_txout(amount, scriptPubKey): return CMutableTxOut(amount * COIN, CScript(scriptPubKey))
def htlc_tx(self, commit_tx: CMutableTransaction, outnum: int, side: Side, amount_sat: int, locktime: int) -> CMutableTransaction: # BOLT #3: # ## HTLC-Timeout and HTLC-Success Transactions # # These HTLC transactions are almost identical, except the # HTLC-timeout transaction is timelocked. Both # HTLC-timeout/HTLC-success transactions can be spent by a valid # penalty transaction. # BOLT #3: # ## HTLC-Timeout and HTLC-Success Transactions # ... # * txin count: 1 # * `txin[0]` outpoint: `txid` of the commitment transaction and # `output_index` of the matching HTLC output for the HTLC transaction # * `txin[0]` sequence: `0` # * `txin[0]` script bytes: `0` txin = CTxIn(COutPoint(commit_tx.GetTxid(), outnum), nSequence=0x0) # BOLT #3: # ## HTLC-Timeout and HTLC-Success Transactions # ... # * txout count: 1 # * `txout[0]` amount: the HTLC amount minus fees (see [Fee # Calculation](#fee-calculation)) # * `txout[0]` script: version-0 P2WSH with witness script as shown below # # The witness script for the output is: # OP_IF # # Penalty transaction # <revocationpubkey> # OP_ELSE # `to_self_delay` # OP_CHECKSEQUENCEVERIFY # OP_DROP # <local_delayedpubkey> # OP_ENDIF # OP_CHECKSIG redeemscript = script.CScript([ script.OP_IF, self.revocation_pubkey(side).format(), script.OP_ELSE, self.self_delay[side], script.OP_CHECKSEQUENCEVERIFY, script.OP_DROP, self.delayed_pubkey(side).format(), script.OP_ENDIF, script.OP_CHECKSIG ]) print("htlc redeemscript = {}".format(redeemscript.hex())) txout = CTxOut(amount_sat, CScript([script.OP_0, sha256(redeemscript).digest()])) # BOLT #3: # ## HTLC-Timeout and HTLC-Success Transactions # ... # * version: 2 # * locktime: `0` for HTLC-success, `cltv_expiry` for HTLC-timeout return CMutableTransaction(vin=[txin], vout=[txout], nVersion=2, nLockTime=locktime)
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret OP_CHECKSEQUENCEVERIFY = OP_NOP3 #unlike other op-codes, python-bitcoinlib doesn't define OP_CHECKSEQUENCEVERIFY, so I define it myself here SelectParams('testnet') # parameterize these privKeySender = str(sys.argv[1]) addressReceiver = str(sys.argv[2]) secret = CBitcoinSecret(privKeySender) # per instructor message in the COMP541-01-S-2019 Moodle classroom forum, use '0xc800' (200 blocks) for relative timelock amount # 0xc800 is calculated as little-endian hex of 200, plus '00' tacked on the end because it's necessary to add sign for values which are less than half a byte, otherwise interpreted as negative redeem_script = CScript([ 0xc800, OP_CHECKSEQUENCEVERIFY, OP_DROP, OP_DUP, OP_HASH160, Hash160(secret.pub), OP_EQUALVERIFY, OP_CHECKSIG ]) txin_scriptPubKey = redeem_script.to_p2sh_scriptPubKey() txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) print('Pay to:', str(txin_p2sh_address)) proxy = bitcoin.rpc.Proxy() # the below functionality is to be run from cmd line in between send_to_p2sh_timelock.py and this program # uncomment to run programmatically for testing purposes # transactionid = proxy.sendtoaddress(str(txin_p2sh_address), 0.0001*COIN) # print('transaction id: ' + b2lx(transactionid)) # find the set of UTXO transactions that have been sent to the script address txins = [] # inputs to the spending transaction we will create
def _unsigned_tx( self, side: Side) -> Tuple[CMutableTransaction, List[Optional[HTLC]]]: """Create the commitment transaction. Returns it and a list of matching HTLCs for each output """ ocn = self.obscured_commit_num( self.keyset[self.opener].raw_payment_basepoint(), self.keyset[not self.opener].raw_payment_basepoint(), self.commitnum) # BOLT #3: # ## Commitment Transaction # ... # * txin count: 1 # * `txin[0]` outpoint: `txid` and `output_index` from `funding_created` message # * `txin[0]` sequence: upper 8 bits are 0x80, lower 24 bits are upper 24 bits of the obscured commitment number # * `txin[0]` script bytes: 0 # * `txin[0]` witness: `0 <signature_for_pubkey1> <signature_for_pubkey2>` txin = CTxIn(COutPoint(bytes.fromhex(self.funding.txid), self.funding.output_index), nSequence=0x80000000 | (ocn >> 24)) # txouts, with ctlv_timeouts (for htlc output tiebreak) and htlc txouts: List[Tuple[CTxOut, int, Optional[HTLC]]] = [] for htlc in self.untrimmed_htlcs(side): if htlc.owner == side: redeemscript, sats = self._offered_htlc_output(htlc, side) else: redeemscript, sats = self._received_htlc_output(htlc, side) print("*** Got htlc redeemscript {} / {}".format( redeemscript, redeemscript.hex())) txouts.append( (CTxOut(sats, CScript([script.OP_0, sha256(redeemscript).digest()])), htlc.cltv_expiry, htlc)) num_untrimmed_htlcs = len(txouts) fee = self._fee(num_untrimmed_htlcs) out_redeemscript, sats = self._to_local_output(fee, side) if sats >= self.dust_limit[side]: txouts.append((CTxOut( sats, CScript([script.OP_0, sha256(out_redeemscript).digest()])), 0, None)) # BOLT #3: # #### `to_remote` Output # # This output sends funds to the other peer and thus is a simple # P2WPKH to `remotepubkey`. amount_to_other = self.amounts[not side] // 1000 if not side == self.opener: amount_to_other -= fee if amount_to_other >= self.dust_limit[side]: txouts.append((CTxOut( amount_to_other, CScript([ script.OP_0, Hash160(self.to_remote_pubkey(side).format()) ])), 0, None)) # BOLT #3: # ## Transaction Input and Output Ordering # # Lexicographic ordering: see # [BIP69](https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki). # In the case of identical HTLC outputs, the outputs are ordered in # increasing `cltv_expiry` order. # First sort by cltv_expiry txouts.sort(key=lambda txout: txout[1]) # Now sort by BIP69 txouts.sort(key=lambda txout: txout[0].serialize()) # BOLT #3: # ## Commitment Transaction # # * version: 2 # * locktime: upper 8 bits are 0x20, lower 24 bits are the # lower 24 bits of the obscured commitment number return (CMutableTransaction(vin=[txin], vout=[txout[0] for txout in txouts], nVersion=2, nLockTime=0x20000000 | (ocn & 0x00FFFFFF)), [txout[2] for txout in txouts])
def create_signed_transaction(txin, txout, txin_scriptPubKey, txin_scriptSig): tx = CMutableTransaction([txin], [txout]) txin.scriptSig = CScript(txin_scriptSig) VerifyScript(txin.scriptSig, CScript(txin_scriptPubKey), tx, 0, (SCRIPT_VERIFY_P2SH, )) return tx
def create_OP_CHECKSIG_signature(txin, txout, txin_scriptPubKey, seckey): tx = CMutableTransaction([txin], [txout]) sighash = SignatureHash(CScript(txin_scriptPubKey), tx, 0, SIGHASH_ALL) sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) return sig
def sign_BCY(tx, txin_scriptPubKey): sighash = SignatureHash(CScript(txin_scriptPubKey), tx, 0, SIGHASH_ALL) sig = alice_secret_key_BCY.sign(sighash) + bytes([SIGHASH_ALL]) return sig
def build_fund_tx(self, redeem_script): if type(self.send_amount) != decimal.Decimal: raise Exception("Please only use decimal types for the amount.") if self.send_amount < decimal.Decimal( coinbend.config["minimum_trade"]): raise Exception("Amount is too small.") #Because every Satoshi counts. decimal.getcontext().prec = 50 #Create a change address. if "change" not in self.key_pairs: self.key_pairs["change"] = self.key_pair_from_address( self.jsonrpc[self.my].getnewaddress(), self.my) #Get wallet balance. balance = self.jsonrpc[self.my].getbalance() #Check we have enough. if balance < self.send_amount: raise Exception("Insufficent balance to cover fund.") #List unclaimed outputs. unspent_outputs = self.jsonrpc[self.my].listunspent() #Setup tx inputs. change_amount = decimal.Decimal("0") txins = [] total = decimal.Decimal("0") indexes = [] i = 0 for unspent_output in unspent_outputs: #Skip outputs that don't satisfy min confirmations. if unspent_output["confirmations"] < self.minimum_confirmations: i += 1 continue #Check scriptPubKey is pay to pub key hash. if self.jsonrpc[self.my].decodescript( unspent_output["scriptPubKey"])["type"] != "pubkeyhash": i += 1 continue #Record key pairs. if unspent_output["address"] not in self.key_pairs: self.key_pairs[ unspent_output["address"]] = self.key_pair_from_address( unspent_output["address"], self.my) #Build new txin. txid = lx(unspent_output["txid"]) vout = unspent_output["vout"] txin = CTxIn(COutPoint(txid, vout)) txins.append(txin) indexes.append(i) total += unspent_output["amount"] i += 1 if total > self.send_amount: break if total == self.send_amount: break #Insufficent funds. if total < self.send_amount: raise Exception("Not enough valid inputs to fund contract.") #Calculate change. change = total - self.send_amount #Build txouts. txouts = [] redeem_script_hash160 = self.hash160_script(redeem_script) p2sh_script_pub_key = CScript( [OP_HASH160, redeem_script_hash160["bin"], OP_EQUAL]) txouts.append( CTxOut( (self.send_amount - decimal.Decimal(coinbend.config["mining_fee"]["standard"])) * COIN, p2sh_script_pub_key)) if change > decimal.Decimal("0"): change_seckey = CBitcoinSecret( self.key_pairs["change"]["priv"]["wif"]) change_script_pub_key = CScript([ OP_DUP, OP_HASH160, Hash160(change_seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG ]) txouts.append(CTxOut(change * COIN, change_script_pub_key)) #Build unsigned transaction. tx = CTransaction(txins, txouts) unsigned_tx_hex = b2x(tx.serialize()) #Sign transaction. signed_tx_hex = self.jsonrpc[self.my].signrawtransaction( unsigned_tx_hex)["hex"] return signed_tx_hex
def create_timestamp(timestamp, calendar_urls, args): """Create a timestamp calendar_urls - List of calendar's to use setup_bitcoin - False if Bitcoin timestamp not desired; set to args.setup_bitcoin() otherwise. """ setup_bitcoin = args.setup_bitcoin if args.use_btc_wallet else False if setup_bitcoin: proxy = setup_bitcoin() unfunded_tx = CTransaction( [], [CTxOut(0, CScript([OP_RETURN, timestamp.msg]))]) r = proxy.fundrawtransaction(unfunded_tx) # FIXME: handle errors funded_tx = r['tx'] r = proxy.signrawtransaction(funded_tx) assert r['complete'] signed_tx = r['tx'] txid = proxy.sendrawtransaction(signed_tx) logging.info('Sent timestamp tx') blockhash = None while blockhash is None: logging.info('Waiting for timestamp tx %s to confirm...' % b2lx(txid)) time.sleep(1) r = proxy.gettransaction(txid) if 'blockhash' in r: # FIXME: this will break when python-bitcoinlib adds RPC # support for gettransaction, due to formatting differences blockhash = lx(r['blockhash']) logging.info('Confirmed by block %s' % b2lx(blockhash)) block = proxy.getblock(blockhash) r = proxy.getblockheader(blockhash, True) blockheight = r['height'] # We have a block hash! We can now generate the attestation from the block. block_timestamp = make_timestamp_from_block(timestamp.msg, block, blockheight) assert block_timestamp is not None timestamp.merge(block_timestamp) m = args.m n = len(calendar_urls) if m > n or m <= 0: logging.error( "m (%d) cannot be greater than available calendar%s (%d) neither less or equal 0" % (m, "" if n == 1 else "s", n)) sys.exit(1) logging.debug("Doing %d-of-%d request, timeout is %d second%s" % (m, n, args.timeout, "" if n == 1 else "s")) q = Queue() for calendar_url in calendar_urls: submit_async(calendar_url, timestamp.msg, q, args.timeout) start = time.time() merged = 0 for i in range(n): try: remaining = max(0, args.timeout - (time.time() - start)) result = q.get(block=True, timeout=remaining) try: if isinstance(result, Timestamp): timestamp.merge(result) merged += 1 else: logging.debug(str(result)) except Exception as error: logging.debug(str(error)) except Empty: # Timeout continue if merged < m: logging.error( "Failed to create timestamp: need at least %d attestation%s but received %s within timeout" % (m, "" if m == 1 else "s", merged)) sys.exit(1) logging.debug("%.2f seconds elapsed" % (time.time() - start))
def build_mandatory_multisig(mandatory_pubkey, other_pubkeys): txin_redeemScript = CScript([x(mandatory_pubkey), OP_CHECKSIGVERIFY, 1, x(other_pubkeys[0]), x(other_pubkeys[1]), 2, OP_CHECKMULTISIG]) txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) return (b2x(txin_redeemScript), str(txin_p2sh_address))
from io import BytesIO from riemann.tests import helpers from bitcoin.core import CMutableTransaction from bitcoin.core.script import SIGHASH_ANYONECANPAY, CScript from bitcoin.core.script import SignatureHash, SIGHASH_ALL, SIGHASH_SINGLE def parse_tx(hex_tx): # NB: The deserialize function reads from a stream. raw_tx = BytesIO(binascii.unhexlify(hex_tx)) tx = CMutableTransaction.stream_deserialize(raw_tx) return tx prevout_pk_script = \ CScript(helpers.P2WPKH['ser']['ins'][0]['pk_script']) # bytes tx_hex = helpers.P2WPKH['ser']['tx']['unsigned'].hex() # raw hex index = helpers.P2WPKH['human']['ins'][0]['index'] # integer a = parse_tx(tx_hex) int_value = helpers.P2WPKH['human']['ins'][0]['value'] # integer of tx value print('Sighash All:') print( SignatureHash(prevout_pk_script, a, index, SIGHASH_ALL, sigversion=1, amount=(int_value)).hex()) print('Sighash All, Anyone Can Pay:')
def create_signature(self) -> None: """Signs a given transaction""" # Keys are derived in base.py if self.testnet: SelectParams('testnet') else: SelectParams('mainnet') # Construct Inputs tx_inputs = [] parsed_redeem_scripts = {} for input in self.inputs: if input['redeem_script'] not in parsed_redeem_scripts: parsed_redeem_scripts[input['redeem_script']] = CScript( bitcoin.core.x(input['redeem_script'])) txid = bitcoin.core.lx(input['txid']) vout = input['index'] tx_inputs.append(CMutableTxIn(COutPoint(txid, vout))) # Construct Outputs tx_outputs = [] for output in self.outputs: output_script = (CBitcoinAddress( output['address']).to_scriptPubKey()) tx_outputs.append(CMutableTxOut(output['amount'], output_script)) # Construct Transaction tx = CTransaction(tx_inputs, tx_outputs) # Construct data for each signature (1 per input) signature_hashes = [] keys = {} for input_index, input in enumerate(self.inputs): redeem_script = input['redeem_script'] bip32_path = input['bip32_path'] # Signature Hash signature_hashes.append( SignatureHash(parsed_redeem_scripts[redeem_script], tx, input_index, SIGHASH_ALL)) # Only need to generate keys once per unique BIP32 path if keys.get(bip32_path) is None: keys[bip32_path] = self.generate_child_keys(bip32_path) keys[bip32_path]['signing_key'] = ecdsa.SigningKey.from_string( bytes.fromhex(keys[bip32_path]['private_key']), curve=ecdsa.SECP256k1) # Construct signatures (1 per input) # # WARNING: We do not append the SIGHASH_ALL byte, # transaction constructioin should account for that. # signatures = [] for input_index, input in enumerate(self.inputs): input = self.inputs[input_index] signature_hash = signature_hashes[input_index] signing_key = keys[input['bip32_path']]['signing_key'] signatures.append( signing_key.sign_digest_deterministic( signature_hash, sha256, sigencode=ecdsa.util.sigencode_der_canonize).hex()) # Assign result result = {"signatures": signatures} self.signature = result
from bitcoin import SelectParams from bitcoin.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160 from bitcoin.core.script import CScript, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret SelectParams('mainnet') # Create the (in)famous correct brainwallet secret key. h = hashlib.sha256(b'correct horse battery staple').digest() seckey = CBitcoinSecret.from_secret_bytes(h) # Create a redeemScript. Similar to a scriptPubKey the redeemScript must be # satisfied for the funds to be spent. txin_redeemScript = CScript([seckey.pub, OP_CHECKSIG]) print(b2x(txin_redeemScript)) # Create the magic P2SH scriptPubKey format from that redeemScript. You should # look at the CScript.to_p2sh_scriptPubKey() function in bitcoin.core.script to # understand what's happening, as well as read BIP16: # https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() # Convert the P2SH scriptPubKey to a base58 Bitcoin address and print it. # You'll need to send some funds to it to create a txout to spend. txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) print('Pay to:',str(txin_p2sh_address)) # Same as the txid:vout the createrawtransaction RPC call requires #
def parse_from_binary(self, script_data): self.script = CScript(script_data) return self.script
# proxy.getnewaddress() returns CBitcoinAddress recipientpubkey = proxy.getnewaddress() senderpubkey = proxy.getnewaddress() # privkey of the recipient, used to sign the redeemTx seckey = proxy.dumpprivkey(senderpubkey) #proxy.importprivkey(seckey) lockduration = 10 blocknum = proxy.getblockcount() print("current block num:", blocknum) redeemblocknum = blocknum + lockduration # Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be # satisfied for the funds to be spent. txin_redeemScript = CScript([ OP_IF, OP_SHA256, h, OP_EQUALVERIFY, OP_DUP, OP_HASH160, recipientpubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160, senderpubkey, OP_ENDIF, OP_EQUALVERIFY, OP_CHECKSIG ]) print("redeem script:", b2x(txin_redeemScript)) # Create P2SH scriptPubKey from redeemScript. txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() print("p2sh_scriptPubKey", b2x(txin_scriptPubKey)) # Convert the P2SH scriptPubKey to a base58 Bitcoin address txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) p2sh = str(txin_p2sh_address) print('Pay to:', p2sh) # AUTOMATE Send funds to p2sh amount = 1.0 * COIN
def parameterize_witness_template_by_signing(some_input, parameters): """ Take a specific witness template, a bag of parameters, and a transaction, and then produce a parameterized witness (including all necessary valid signatures). Make a sighash for the bitcoin transaction. """ p2wsh_redeem_script = some_input.utxo.p2wsh_redeem_script tx = some_input.transaction.bitcoin_transaction txin_index = some_input.transaction.inputs.index(some_input) computed_witness = [] selection = some_input.witness_template_selection script_template = some_input.utxo.script_template witness_template = script_template.witness_templates[selection] amount = some_input.utxo.amount # TODO: Might have to update the witness_templates values to give a # correct ordering for which signature should be supplied first. # (already did this? Re-check for VerifyScript errors) witness_tmp = witness_template.split(" ") for (idx, section) in enumerate(witness_tmp): if section[0] == "<" and section[-1] == ">": section = section[1:-1] if section == "user_key": computed_witness.append(parameters["user_key"]["public_key"]) continue elif section not in script_template.witness_template_map.keys(): raise VaultException( "Missing key mapping for {}".format(section)) key_param_name = script_template.witness_template_map[section] private_key = parameters[key_param_name]["private_key"] if script_template != UserScriptTemplate: # This is a P2WSH transaction. redeem_script = p2wsh_redeem_script elif script_template == UserScriptTemplate: # This is a P2WPKH transaction. user_address = P2WPKHBitcoinAddress.from_scriptPubKey( CScript( [OP_0, Hash160(parameters["user_key"]["public_key"])])) redeem_script = user_address.to_redeemScript() # P2WPKH redeemScript: OP_DUP OP_HASH160 .... sighash = SignatureHash(redeem_script, tx, txin_index, SIGHASH_ALL, amount=amount, sigversion=SIGVERSION_WITNESS_V0) signature = private_key.sign(sighash) + bytes([SIGHASH_ALL]) computed_witness.append(signature) else: # dunno what to do with this, probably just pass it on really.. computed_witness.append(section) if script_template == UserScriptTemplate: # P2WPKH # Witness already completed. No redeem_script to append. pass else: # P2WSH # Append the p2wsh redeem script. computed_witness.append(p2wsh_redeem_script) computed_witness = CScript(computed_witness) some_input.witness = computed_witness return computed_witness
# There's also a corresponding x() convenience function that takes big-endian # hex and converts it to bytes. txid = lx('7e195aa3de827814f172c362fcf838d92ba10e3f9fdd9c3ecaf79522b311b22d') vout = 0 # Create the txin structure, which includes the outpoint. The scriptSig # defaults to being empty. txin = CMutableTxIn(COutPoint(txid, vout)) # We also need the scriptPubKey of the output we're spending because # SignatureHash() replaces the transaction scriptSig's with it. # # Here we'll create that scriptPubKey from scratch using the pubkey that # corresponds to the secret key we generated above. txin_scriptPubKey = CScript( [OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG]) # Create the txout. This time we create the scriptPubKey from a Bitcoin # address. txout = CMutableTxOut( 0.001 * COIN, CBitcoinAddress('1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8').to_scriptPubKey()) # Create the unsigned transaction. tx = CMutableTransaction([txin], [txout]) # Calculate the signature hash for that transaction. sighash = SignatureHash(txin_scriptPubKey, tx, 0, SIGHASH_ALL) # Now sign it. We have to append the type of signature we want to the end, in
def parameterize_planned_utxo(planned_utxo, parameters=None): """ Parameterize a PlannedUTXO based on the runtime parameters. Populate and construct the output scripts based on the assigned script templates. """ script_template = planned_utxo.script_template miniscript_policy_definitions = script_template.miniscript_policy_definitions script = copy(planned_utxo.script_template.script_template) for some_variable in miniscript_policy_definitions.keys(): some_param = parameters[some_variable] if type(some_param) == dict: some_public_key = b2x(some_param["public_key"]) elif script_template == UserScriptTemplate and type( some_param) == str and some_variable == "user_key_hash160": some_public_key = some_param else: # some_param is already the public key some_public_key = b2x(some_param) script = script.replace("<" + some_variable + ">", some_public_key) # Insert the appropriate relative timelocks, based on the timelock # multiplier. relative_timelocks = planned_utxo.script_template.relative_timelocks timelock_multiplier = planned_utxo.timelock_multiplier if relative_timelocks not in [{}, None]: replacements = relative_timelocks["replacements"] # Update these values to take into account the timelock multiplier. replacements = dict((key, value * timelock_multiplier) for (key, value) in replacements.items()) # Insert the new value into the script. The value has to be # converted to the right value (vch), though. for (replacement_name, replacement_value) in replacements.items(): replacement_value = bitcoin.core._bignum.bn2vch(replacement_value) replacement_value = b2x(replacement_value) script = script.replace("<" + replacement_name + ">", replacement_value) # For testing later: # int.from_bytes(b"\x40\x38", byteorder="little") == 144*100 # b2x(bitcoin.core._bignum.bn2vch(144*100)) == "4038" # bitcoin.core._bignum.vch2bn(b"\x90\x00") == 144 # There might be other things in the script that need to be replaced. #script = script.replace("<", "") #script = script.replace(">", "") if "<" in script: raise VaultException("Script not finished cooking? {}".format(script)) # remove newlines script = script.replace("\n", " ") # reduce any excess whitespace while (" " * 2) in script: script = script.replace(" ", " ") # remove whitespace at the front, like for the cold storage UTXO script if script[0] == " ": script = script[1:] # remove trailing whitespace if script[-1] == " ": script = script[0:-1] # hack for python-bitcoinlib # see https://github.com/petertodd/python-bitcoinlib/pull/226 # TODO: this shouldn't be required anymore (v0.11.0 was released) script = script.replace("OP_CHECKSEQUENCEVERIFY", "OP_NOP3") # convert script into a parsed python object script = script.split(" ") script_items = [] for script_item in script: if script_item in bitcoin.core.script.OPCODES_BY_NAME.keys(): parsed_script_item = bitcoin.core.script.OPCODES_BY_NAME[ script_item] script_items.append(parsed_script_item) else: script_items.append(x(script_item)) p2wsh_redeem_script = CScript(script_items) scriptpubkey = CScript([OP_0, sha256(bytes(p2wsh_redeem_script))]) p2wsh_address = P2WSHBitcoinAddress.from_scriptPubKey(scriptpubkey) planned_utxo.scriptpubkey = scriptpubkey planned_utxo.p2wsh_redeem_script = p2wsh_redeem_script planned_utxo.p2wsh_address = p2wsh_address amount = planned_utxo.amount planned_utxo.bitcoin_output = CTxOut(amount, scriptpubkey) planned_utxo.is_finalized = True logger.info("UTXO name: {}".format(planned_utxo.name)) logger.info("final script: {}".format(script))
def script(self): return CScript([ OP_IF, OP_SHA256, self.secret, OP_EQUALVERIFY, self.paypubkey, OP_ELSE, self.timeout, OP_CLTV, OP_DROP, self.refundpubkey, OP_ENDIF, OP_CHECKSIG ])
def sign_spend(self, tx, vin): txhash = mkhash(self.script, tx, vin, SIGHASH_ALL) sig = mksig(self.privkey, txhash, SIGHASH_ALL) assert bitcoin.core.script.IsLowDERSignature(sig) return CScript([sig, self.script])
def script(self): return CScript( [self.timeout, OP_CLTV, OP_DROP, self.pubkey, OP_CHECKSIG])
def do_step(self): """Returns whether another step can be done.""" if self.step_counter == -1: txt = str(self.tx_script.toPlainText()) scr = CScript(txt.decode('hex')) # So we can show the opcode in the stack log self.script_ops = [i for i in scr.raw_iter()] self.stack.set_script(scr, self.tx, self.inIdx) self.stack_iterator = self.stack.step() self.stack_log.clear() self.step_counter += 1 step_again = False try: self.step_counter += 1 stack_state, action = self.stack_iterator.next() new_stack = [i.encode('hex') for i in reversed(stack_state)] self.stack_view.clear() self.stack_view.addItems(new_stack) op_name = OPCODE_NAMES.get(self.script_ops[self.step_counter - 1][0], 'PUSHDATA') self.highlight_step(self.script_ops[self.step_counter - 1]) item = QTreeWidgetItem(map(lambda i: str(i), [self.step_counter, op_name, action])) item.setTextAlignment(0, Qt.AlignLeft) item.setToolTip(1, 'Step {} operation'.format(self.step_counter)) item.setToolTip(2, 'Result of step {}'.format(self.step_counter)) self.stack_log.insertTopLevelItem(0, item) self.stack_result.setText(action) self.stack_result.setProperty('hasError', False) step_again = True except StopIteration: self.stack_result.setText('End of script.') except Exception as e: self.stack_result.setText(str(e)) self.stack_result.setProperty('hasError', True) finally: self.style().polish(self.stack_result) return step_again