def get_coinbase_info(blockheight): '''Gets coinbase tag and addresses of a block with specified height. Returns: addresses - A list of p2sh/p2pkh addresses corresponding to the outputs. Returns None in place of an unrecognizable scriptPubKey. tag - the UTF-8 decoded scriptSig. Raises: Exceptions originating from bitcoin.rpc.Proxy, if there is a problem with JSON-RPC. ''' block = proxy.getblock(proxy.getblockhash(blockheight)) coinbase_tx = block.vtx[0] assert coinbase_tx.is_coinbase() addresses = [] for output in coinbase_tx.vout: try: addr = str(CBitcoinAddress.from_scriptPubKey(output.scriptPubKey)) except (CBitcoinAddressError, ValueError): addr = None else: addr = addr.decode('ascii') addresses.append(addr) tag = str(coinbase_tx.vin[0].scriptSig).decode('utf-8', 'ignore') return addresses, tag
def _check_output_address(mytx): print for vout in mytx.vout: pubkey = vout.scriptPubKey address = b2lx(CBitcoinAddress.from_scriptPubKey(pubkey)) address = "".join(map(str.__add__, address[-2::-2], address[-1::-2])) #print address address = int(address, 16) for bl in blacklist: if hex(bl.start) <= hex(address) <= hex(bl.end): print "This address use by: " + bl.name return False return True
def payment_ack(serialized_Payment_message): """Generates a PaymentACK object, captures client refund address and returns a message""" pao = o.PaymentACK() pao.payment.ParseFromString(serialized_Payment_message) pao.memo = "String shown to user after payment confirmation" refund_address = CBitcoinAddress.from_scriptPubKey(CScript(pao.payment.refund_to[0].script)) sds_pa = pao.SerializeToString() open("sds_pa_blob", "wb").write(sds_pa) headers = {"Content-Type": "application/bitcoin-payment", "Accept": "application/bitcoin-paymentack"} http_response_object = urllib2.Request("file:sds_pa_blob", None, headers) return http_response_object
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 setup_preimage(self, payer_pubkey, redeemer_pubkey, hashes, amount, lock_time): ''' Setups a P2SH that can only be redeemed if the redeemer is able to provide the hash preimages. Also, sends a tx funding the escrow (Assumes payer calls the setup) ''' # Set locktime relative to current block if not self.test: lock = self.proxy.getblockcount() + lock_time else: lock = lock_time script = self.create_hash_script(redeemer_pubkey, hashes) redeem_script = CScript([OP_IF] + script + [ OP_ELSE, lock, OP_CHECKLOCKTIMEVERIFY, OP_DROP, payer_pubkey, OP_CHECKSIG, OP_ENDIF ]) redeem = b2x(redeem_script) self.logger.info("setup_preimage: Redeem script is %s", redeem) # Get P2SH address # 1. Get public key script_pub_key = redeem_script.to_p2sh_scriptPubKey() # 2. Get bitcoin address p2sh_address = CBitcoinAddress.from_scriptPubKey(script_pub_key) self.logger.info("setup_preimage: P2SH is %s", str(p2sh_address)) # 3. Fund address if not self.test: # funding_tx = self.proxy.call("sendtoaddress", str(p2sh_address), # amount) funding_tx = FUNDING_TX self.logger.info("setup_preimage: P2SH Fund TX is %s", funding_tx) else: funding_tx = FUNDING_TX return (redeem_script, str(funding_tx), str(p2sh_address), str(lock))
def payment_ack(serialized_Payment_message): """Generates a PaymentACK object, captures client refund address and returns a message""" pao = o.PaymentACK() pao.payment.ParseFromString(serialized_Payment_message) pao.memo = 'String shown to user after payment confirmation' refund_address = CBitcoinAddress.from_scriptPubKey( CScript(pao.payment.refund_to[0].script)) sds_pa = pao.SerializeToString() open('sds_pa_blob', 'wb').write(sds_pa) headers = { 'Content-Type': 'application/bitcoin-payment', 'Accept': 'application/bitcoin-paymentack' } http_response_object = urllib2.Request('file:sds_pa_blob', None, headers) return http_response_object
def htlc_p2sh(self) -> str: # TODO: cache the generated address # We create connections on the fly because they'll time out quickly if # we don't bitcoind = rpc.Proxy() # We can decipher the hash of the preimage without explicitly asking # for it by taking it out of the payment request supplied decoded_pr = json.loads( to_json(lnd.decode_payment_request(self.bolt11_invoice))) hashed_preimage = decoded_pr['payment_hash'] # Once these assignments are made, we want to lock them in so this # functions generates deterministically if not self.final_address_pubkey: final_address = bitcoind.getnewaddress() seckey = bitcoind.dumpprivkey(final_address) self.final_address_pubkey = seckey.pub.hex() if not self.redeemblock: curr_blockheight = bitcoind.getblockcount() self.redeemblock = curr_blockheight + self.lockduration # HTLC locking script txin_redeemScript = CScript([ OP_IF, OP_SHA256, bytes(hashed_preimage, 'utf8'), OP_EQUALVERIFY, OP_DUP, OP_HASH160, bytes(Hash160(bytes(self.final_address_pubkey, 'utf8'))), OP_ELSE, self.redeemblock, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160, bytes(Hash160(bytes(self.refund_address, 'utf8'))), OP_ENDIF, OP_EQUALVERIFY, OP_CHECKSIG ]) self.save() # Generate a P2SH address from the locking script txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() txin_p2sh_address = CBitcoinAddress.from_scriptPubKey( txin_scriptPubKey) return str(txin_p2sh_address)
def setup_escrow(self, payer_pubkey, redeemer_pubkey, amount, lock_time): ''' Setups a 2of2 escrow with payer and redeemer Also, sends a tx funding the escrow (Assumes payer calls the setup) ''' # Set locktime relative to current block if not self.test: lock = self.proxy.getblockcount() + lock_time self.logger.info("setup_escrow: Locktime is %d", lock) else: lock = lock_time redeem_script = CScript([ OP_IF, OP_2, payer_pubkey, redeemer_pubkey, OP_2, OP_CHECKMULTISIG, OP_ELSE, lock, OP_CHECKLOCKTIMEVERIFY, OP_DROP, payer_pubkey, OP_CHECKSIG, OP_ENDIF ]) redeem = b2x(redeem_script) self.logger.info("setup_escrow: Redeem script is %s", redeem) # Get P2SH address # 1. Get public key script_pub_key = redeem_script.to_p2sh_scriptPubKey() # 2. Get bitcoin address p2sh_address = CBitcoinAddress.from_scriptPubKey(script_pub_key) self.logger.info("setup_escrow: P2SH is %s", str(p2sh_address)) # 3. Fund address if not self.test: funding_tx = self.proxy.call("sendtoaddress", str(p2sh_address), amount) self.logger.info("setup_escrow: P2SH Fund TX is %s", funding_tx) else: funding_tx = FUNDING_TX return (redeem_script, str(funding_tx), str(p2sh_address), str(lock))
def show_details(self): return { 'contract': self.contract.hex(), 'contract_address': str( CBitcoinAddress.from_scriptPubKey( self.contract.to_p2sh_scriptPubKey())), 'contract_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}', 'locktime': self.locktime, 'recipient_address': self.recipient_address, 'refund_address': self.sender_address, 'secret': self.secret.hex() if self.secret else '', 'secret_hash': self.secret_hash.hex(), 'size': self.size, 'size_text': f'{self.size} bytes', 'value': self.value, 'value_text': f'{self.value:.8f} {self.symbol}', }
def hashtimelockcontract(self, funder, redeemer, commitment, locktime): funderAddr = CBitcoinAddress(funder) redeemerAddr = CBitcoinAddress(redeemer) if type(commitment) == str: commitment = x(commitment) # h = sha256(secret) blocknum = self.bitcoind.getblockcount() print("Current blocknum on Bitcoin: ", blocknum) redeemblocknum = blocknum + locktime print("Redeemblocknum on Bitcoin: ", redeemblocknum) redeemScript = CScript([OP_IF, OP_SHA256, commitment, OP_EQUALVERIFY,OP_DUP, OP_HASH160, redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160, funderAddr, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG]) # print("Redeem script for p2sh contract on Bitcoin blockchain: {0}".format(b2x(redeemScript))) txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey() # Convert the P2SH scriptPubKey to a base58 Bitcoin address txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) p2sh = str(txin_p2sh_address) # Import address at same time you create self.bitcoind.importaddress(p2sh, "", False) print("p2sh computed", p2sh) return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(redeemScript), 'redeemer': redeemer, 'funder': funder, 'locktime': locktime}
def add_input_output(bitcoind, tx, coin, fees): """Add another input to the transaction to bump the feerate.""" coin_amount = Decimal(coin["amount"]) * Decimal(COIN) # First get the private key from bitcoind's wallet. privkey = CKey(wif_decode(bitcoind.dumpprivkey(coin["address"]))) # Add the fetched coin as a new input. tx.vin.append(CTxIn(COutPoint(lx(coin["txid"]), coin["vout"]))) # And likely add an output, otherwise all goes to the fees. scriptPubKey = CScript([OP_0, Hash160(privkey.pub)]) if coin_amount > fees + 294: # For simplicity, pay to the same script tx.vout.append(CTxOut(coin_amount - Decimal(fees), scriptPubKey)) address = CBitcoinAddress.from_scriptPubKey(scriptPubKey) # We only do this once, sign it with ALL tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL, int(coin_amount), SIGVERSION_WITNESS_V0) sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL]) tx.wit.vtxinwit.append( CTxInWitness(CScriptWitness([sig, privkey.pub])) ) return tx
def get_address_by_path(self, path, key=None): path = path.replace("M/", "") key_index = path.split("/")[0].replace("'", "") if key is None: key = self.primary_private_key.subkey_for_path(path) backup_public_key = self.backup_public_key.subkey_for_path( path.replace("'", "")) blocktrail_public_key = self.blocktrail_public_keys[str( key_index)].subkey_for_path("/".join(path.split("/")[1:])) redeemScript = CScript([2] + sorted([ key.sec(use_uncompressed=False), backup_public_key.sec(use_uncompressed=False), blocktrail_public_key.sec(use_uncompressed=False), ]) + [3, OP_CHECKMULTISIG]) scriptPubKey = redeemScript.to_p2sh_scriptPubKey() address = CBitcoinAddress.from_scriptPubKey(scriptPubKey) return str(address)
def check_for_funding(self, address): """ Check to see if any of the outputs pay the given address Args: address: base58check encoded bitcoin address Returns: a `list` of `dict` outpoints if any of the outputs match the address else None. """ outpoints = [] for i in range(len(self.tx.vout)): addr = CBitcoinAddress.from_scriptPubKey(self.tx.vout[i].scriptPubKey) if str(addr) == address: o = { "txid": b2lx(self.tx.GetHash()), "vout": i, "value": self.tx.vout[i].nValue, "scriptPubKey": self.tx.vout[i].scriptPubKey.encode("hex") } outpoints.append(o) return outpoints if len(outpoints) > 0 else None
def check_for_funding(self, address): """ Check to see if any of the outputs pay the given address Args: address: base58check encoded bitcoin address Returns: a `list` of `dict` outpoints if any of the outputs match the address else None. """ outpoints = [] for i in range(len(self.tx.vout)): addr = CBitcoinAddress.from_scriptPubKey(self.tx.vout[i].scriptPubKey) if str(addr) == address: o = { "txid": b2lx(self.tx.GetHash()), "vout": i, "value": self.tx.vout[i].nValue, "scriptPubKey": self.tx.vout[i].scriptPubKey.encode("hex") } outpoints.append(o) return outpoints if len(outpoints) > 0 else None
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(rein, 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 setup_escrow(payer_pubkey, redeemer_pubkey, amount, lock_time): ''' Setups a 2of2 escrow with payer and redeemer Also, sends a tx funding the escrow (Assumes payer calls the setup) ''' redeem_script = CScript([OP_DEPTH, 3, OP_EQUAL, # Fixes a txid malliablity issue thanks to Nicolas OP_IF, OP_2, payer_pubkey, redeemer_pubkey, OP_2, OP_CHECKMULTISIG, OP_ELSE, lock_time, OP_CHECKLOCKTIMEVERIFY, OP_DROP, payer_pubkey, OP_CHECKSIG, OP_ENDIF]) redeem = b2x(redeem_script) print("setup_escrow: Redeem script is %s" % redeem) # Get P2SH address script_pub_key = redeem_script.to_p2sh_scriptPubKey() p2sh_address = CBitcoinAddress.from_scriptPubKey(script_pub_key) print("setup_escrow: P2SH is %s" % str(p2sh_address)) return (redeem_script, str(p2sh_address))
def setup_preimage(payer_pubkey, redeemer_pubkey, hashes, lock_time): """ Setups a P2SH that can only be redeemed if the redeemer is able to provide the hash preimages. Arguments: payer_pubkey (bytes): The public key of the party that funds the contract redeemer_pubkey (bytes): The public key of the party that wants to receive the funds hashes (list): The hashes that payer wants the preimages of lock_time (int): The time the refund should be activated at Returns: A tuple containing: 1/ redeem_script 2/ p2sh_address, which should be funded """ script = create_hash_script(redeemer_pubkey, hashes) redeem_script = CScript([OP_IF] + script + [ OP_ELSE, lock_time, OP_CHECKLOCKTIMEVERIFY, OP_DROP, payer_pubkey, OP_CHECKSIG, OP_ENDIF ]) redeem = b2x(redeem_script) print("setup_preimage: Redeem script is %s" % redeem) # Get P2SH address # 1. Get public key script_pub_key = redeem_script.to_p2sh_scriptPubKey() # 2. Get bitcoin address p2sh_address = CBitcoinAddress.from_scriptPubKey(script_pub_key) print("setup_preimage: P2SH is %s" % str(p2sh_address)) return (redeem_script, str(p2sh_address))
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( 'Mediator 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 amount <= 0: raise ValueError( 'Mediator escrow balance too low. Please inform client to add funds.' ) 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 poll_for_funds(self): """Polls bitcoind to check for received funds. If we just went to know of the possession of a new output, it will construct the corresponding emergency transaction and spawn a thread to fetch emergency transactions signatures. Note that this main loop used to be somewhat less naïvely implemented, but it was both a premature and useless optimisation. """ while not self.funds_poller_stop.wait(5.0): # What we think we have known_outputs = [v["txid"] for v in self.vaults] # What bitcoind tells we *actually* have current_utxos = self.bitcoind.listunspent( # Should not be 0 if it was "for real" (cancel_tx's id isn't # known for sure) minconf=0, addresses=self.vault_addresses) current_utxos_id = [u["txid"] for u in current_utxos] spent_vaults = [ v for v in self.vaults if v["txid"] not in current_utxos_id ] new_vaults = [ u for u in current_utxos if u["txid"] not in known_outputs ] for v in spent_vaults: # Is it an emergency broadcast ? if self.bitcoind.listunspent( minconf=0, addresses=[self.emergency_address]): # Game over. for v in self.vaults: tx = self.get_signed_emergency_tx(v).serialize().hex() if tx is not None: try: self.bitcoind.sendrawtransaction(tx) except bitcoin.rpc.JSONRPCError: # Already sent! pass unvtx = self.get_signed_unvault_emergency_tx(v) hextx = unvtx.serialize().hex() if tx is not None: try: self.bitcoind.sendrawtransaction(hextx) except bitcoin.rpc.JSONRPCError: # Already sent! pass self.stopped = True return # If not, it must be an unvault broadcast ! unvault_addr = CBitcoinAddress.from_scriptPubKey( v["unvault_tx"].vout[0].scriptPubKey) unvault_utxos = self.bitcoind.listunspent( minconf=0, addresses=[str(unvault_addr)]) if len(unvault_utxos) == 0: # Maybe someone has already broadcast the cancel # transaction cancel_addr = CBitcoinAddress.from_scriptPubKey( v["cancel_tx"].vout[0].scriptPubKey) assert len( self.bitcoind.listunspent( minconf=0, addresses=[str(cancel_addr)])) > 0 else: if v["txid"] not in self.acked_spends: try: tx = self.get_signed_cancel_tx(v).serialize().hex() if tx is not None: self.bitcoind.sendrawtransaction(tx) except bitcoin.rpc.JSONRPCError: # Already sent! pass # FIXME wait for it to be mined ? # These were unvaulted if len(spent_vaults) > 0: self.vaults_lock.acquire() self.vaults = [v for v in self.vaults if v not in spent_vaults] self.vaults_lock.release() for utxo in new_vaults: self.vaults_lock.acquire() self.add_new_vault(utxo) self.vaults_lock.release() # Do a new bunch of watchonly imports if we get closer to # the maximum index we originally derived. # FIXME: This doesn't take address reuse into account self.current_index += 1 self.max_index += 1 if self.current_index > self.index_treshold - 20: self.update_watched_addresses() # If we had new coins, restart the transactions signatures fetcher # with the updated list of vaults. if len(new_vaults) > 0: self.update_sigs_stop.set() try: self.update_sigs_thread.join() except RuntimeError: # Already dead pass self.update_sigs_stop.clear() del self.update_sigs_thread self.update_sigs_thread = \ threading.Thread(target=self.update_all_signatures, daemon=True) self.update_sigs_thread.start()
def getnewaddress_command(args): fund_addr = CBitcoinAddress.from_scriptPubKey(args.wallet.make_paytopubkeyhash()) args.wallet.save() print('Pay to %s to fund your wallet' % fund_addr)
def get_vault_address(self, index): """Get the vault address for index {index}""" pubkeys = self.get_pubkeys(index) txo = vault_txout(pubkeys, 0) return str(CBitcoinAddress.from_scriptPubKey(txo.scriptPubKey))
def __init__(self, xpriv, xpubs, emergency_pubkeys, bitcoin_conf_path, cosigning_url, sigserver_url, acked_addresses, current_index=0, birthdate=None): """ We need the xpub of all the other stakeholders to derive their pubkeys. :param xpriv: Who am I ? Has to correspond to one of the following xpub. As str. :param xpubs: A list of the xpub of all the stakeholders (as str), in the following order: 1) first trader 2) second trader 3) first "normie" stakeholder 4) second "normie" stakeholder. :param emergency_pubkeys: A list of the four offline keys of the stakeholders, as bytes. :param bitcoin_conf_path: Path to bitcoin.conf. :param cosigning_url: The url of the cosigning server. :param sigserver_url: The url of the server to post / get the sigs from other stakeholders. :param acked_addresses: Addresses to which we accept to spend. :param birthdate: The timestamp at which this wallet has been created. If not passed, will assume newly-created wallet. """ assert len(xpubs) == 4 self.our_bip32 = BIP32.from_xpriv(xpriv) self.keychains = [] for xpub in xpubs: if xpub != self.our_bip32.get_master_xpub(): self.keychains.append(BIP32.from_xpub(xpub)) else: self.keychains.append(None) self.all_xpubs = xpubs self.emergency_pubkeys = emergency_pubkeys # Ok, shitload of indexes. The current one is the lower bound of the # range we will import to bitcoind as watchonly. The max one is the # upper bond, the current "gen" one is to generate new addresses. self.current_index = current_index self.current_gen_index = self.current_index self.max_index = current_index + 500 self.index_treshold = self.max_index self.birthdate = int(time.time()) if birthdate is None else birthdate self.bitcoind = BitcoindApi(bitcoin_conf_path) # First of all, watch the emergency vault self.watch_emergency_vault() # And store the corresponding address.. txo = emergency_txout(self.emergency_pubkeys, 0) self.emergency_address = str( CBitcoinAddress.from_scriptPubKey(txo.scriptPubKey)) # The cosigning server, asked for its signature for the spend_tx self.cosigner = CosigningApi(cosigning_url) self.cosigner_pubkey = self.cosigner.get_pubkey() # The "sig" server, used to store and exchange signatures between # vaults and which provides us a feerate. # Who am I ? stk_id = self.keychains.index(None) + 1 self.sigserver = ServerApi(sigserver_url, stk_id) self.vault_addresses = [] self.unvault_addresses = [] self.update_watched_addresses() # We keep track of each vault, see below when we fill it for details # about what it contains. Basically all the transactions, the # signatures and some useful fields (like "are all txs signed ?"). self.vaults = [] self.vaults_lock = threading.Lock() # Poll for funds until we die self.funds_poller_stop = threading.Event() self.funds_poller = threading.Thread(target=self.poll_for_funds, daemon=True) self.funds_poller.start() # Poll for spends until we die self.acked_addresses = acked_addresses self.acked_spends = [] self.spends_poller_stop = threading.Event() self.spends_poller = threading.Thread(target=self.poll_for_spends, daemon=True) self.spends_poller.start() # Don't start polling for signatures just yet, we don't have any vault! self.update_sigs_stop = threading.Event() self.update_sigs_thread =\ threading.Thread(target=self.update_all_signatures, daemon=True) self.stopped = False
def make_addresses(): # Or just hardcode it ; hwi ; m/10141'/0' ; m/84'/1'/0'/0/0 seckey = CBitcoinSecret( 'cTqr6cjXsev49PWpHXctTbutZQg5kpTWoGZy8z3yZ7jYgmgKYVFo') witver = 0 print("wif", seckey) print("pubk", b2x(seckey.pub)) print("") # https://en.bitcoin.it/wiki/IP_transaction # https://en.bitcoin.it/wiki/Script#Obsolete_pay-to-pubkey_transaction script = CScript([seckey.pub, OP_CHECKSIG]) # p2pk = CBitcoinAddress.from_scriptPubKey(script) # not recognized Issue a PR # https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash script = CScript( [OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG]) p2pk = p2pkh = CBitcoinAddress.from_scriptPubKey(script) print("p2pk", p2pk) print("p2pkh", p2pkh) # https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki#specification redeemScript = CScript([seckey.pub, OP_CHECKSIG]) script = CScript([OP_HASH160, Hash160(redeemScript), OP_EQUAL]) print("p2sh", CBitcoinAddress.from_scriptPubKey(script)) p2sh_redeemScript = " ".join(deserialize([b2x(redeemScript)], [0])) # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wpkh script = CScript([OP_0, Hash160(seckey.pub)]) print("p2wpkh", CBitcoinAddress.from_scriptPubKey(script)) # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh witnessScript = CScript([seckey.pub, OP_CHECKSIG]) script = CScript([OP_0, Sha256(witnessScript)]) print("p2wsh", CBitcoinAddress.from_scriptPubKey(script)) p2wsh_witnessScript = " ".join(deserialize([b2x(witnessScript)], [0])) # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wpkh-nested-in-bip16-p2sh redeemScript = CScript([OP_0, Hash160(seckey.pub)]) script = CScript([OP_HASH160, Hash160(redeemScript), OP_EQUAL]) print("p2sh-p2wpkh", CBitcoinAddress.from_scriptPubKey(script)) p2sh_p2wpkh_redeemScript = " ".join(deserialize([b2x(redeemScript)], [0])) # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh-nested-in-bip16-p2sh witnessScript = CScript([seckey.pub, OP_CHECKSIG]) redeemScript = CScript([OP_0, Sha256(witnessScript)]) script = CScript([OP_HASH160, Hash160(redeemScript), OP_EQUAL]) print("p2sh-p2wsh", CBitcoinAddress.from_scriptPubKey(script)) print("", ) print("p2sh_redeemScript", p2sh_redeemScript) print("p2wsh_witnessScript", p2wsh_witnessScript) print("p2sh_p2wpkh_redeemScript", p2sh_p2wpkh_redeemScript) print("p2sh_p2wsh_witnessScript", " ".join(deserialize([b2x(witnessScript)], [0]))) print("p2sh_p2wsh_redeemScript", " ".join(deserialize([b2x(redeemScript)], [0])))
def watch_emergency_vault(self): """There is only one emergency script""" script = emergency_txout(self.emergency_pubkeys, COIN).scriptPubKey addr = CBitcoinAddress.from_scriptPubKey(script) self.bitcoind.importaddress(str(addr), "revault_emergency")
def __init__( self, network, contract: str, raw_transaction: Optional[str]=None, transaction_address: Optional[str]=None ): if not raw_transaction and not transaction_address: raise ValueError('Provide raw_transaction or transaction_address argument.') self.network = network self.symbol = self.network.default_symbol self.contract = contract self.tx = None self.vout = None self.confirmations = None self.tx_address = transaction_address if raw_transaction: self.tx = self.network.deserialize_raw_transaction(raw_transaction) try: self.vout = self.tx.vout[0] except IndexError: raise ValueError('Given transaction has no outputs.') else: tx_json = get_transaction(network.default_symbol, transaction_address, network.is_test_network()) if not tx_json: raise ValueError('No transaction found under given address.') if 'hex' in tx_json: # transaction from blockcypher or raven explorer self.tx = self.network.deserialize_raw_transaction(tx_json['hex']) self.vout = self.tx.vout[0] else: # transaction from cryptoid incorrect_cscript = script.CScript.fromhex(tx_json['outputs'][0]['script']) correct_cscript = script.CScript([script.OP_HASH160, list(incorrect_cscript)[2], script.OP_EQUAL]) nValue = to_base_units(tx_json['outputs'][0]['amount']) self.vout = CTxOut(nValue, correct_cscript) if 'confirmations' in tx_json: self.confirmations = tx_json['confirmations'] elif 'block_height' in tx_json: self.confirmations = self.network.latest_block - tx_json['block_height'] elif 'block' in tx_json: self.confirmations = self.network.latest_block - tx_json['block'] if not self.vout: raise ValueError('Given transaction has no outputs.') contract_tx_out = self.vout contract_script = script.CScript.fromhex(self.contract) script_pub_key = contract_script.to_p2sh_scriptPubKey() valid_p2sh = script_pub_key == contract_tx_out.scriptPubKey self.address = str(CBitcoinAddress.from_scriptPubKey(script_pub_key)) self.balance = get_balance(self.network, self.address) script_ops = list(contract_script) if valid_p2sh and self.is_valid_contract_script(script_ops): self.recipient_address = str(P2PKHBitcoinAddress.from_bytes(script_ops[6])) self.refund_address = str(P2PKHBitcoinAddress.from_bytes(script_ops[13])) self.locktime_timestamp = int.from_bytes(script_ops[8], byteorder='little') self.locktime = datetime.utcfromtimestamp(self.locktime_timestamp) self.secret_hash = b2x(script_ops[2]) self.value = from_base_units(contract_tx_out.nValue) else: raise ValueError('Given transaction is not a valid contract.')
# 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 # something that dumb and anti-privacy in a real wallet - but without a way # of keeping state this is the best we've got. try: addr = CBitcoinAddress.from_scriptPubKey(tx2.vout[0].scriptPubKey) except ValueError: pass else: # There is an edge case not handled: if we have multiple outputs but # didn't need a change output. But whatever, this is just a demo! if len(tx2.vout) > 1 and rpc.validateaddress(addr)['ismine']: tx2.vout = tx2.vout[1:] # Add the new output payment_txout = CMutableTxOut(args.amount, args.address.to_scriptPubKey()) tx2.vout.append(payment_txout) r = rpc.fundrawtransaction(tx2) tx2 = CMutableTransaction.from_tx(r['tx'])
seckey_multisig = CBitcoinSecret.from_secret_bytes(os.urandom(32)) pubkey_multisig = seckey_multisig.pub # Create script. multisig_script = [ OP_2, pubkey_multisig, pubkey_single, OP_2, OP_CHECKMULTISIG ] # Sign message "1" with seckey_single and prepend to script. HASH_ONE = b"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" sig = seckey_single.sign(HASH_ONE) + bytes([SIGHASH_SINGLE]) redeemScript = CScript([sig] + multisig_script) # Create address from redeem script. address = CBitcoinAddress.from_scriptPubKey( redeemScript.to_p2sh_scriptPubKey()) print("Burn address:", address) # Connect to bitcoind. bitcoin_rpc = rpc.RawProxy() try: info = bitcoin_rpc.getinfo() if info['blocks'] > 3000: print("Coinbase reward not large enough to proceed. Exiting. \ Consider cleaning your datadir (usually ~/.bitcoin/regtest)") exit(1) except Exception as e: print( "There are problems with the rpc connection to bitcoind (is it running?). \ Exception: %s" % str(e)) exit(1)
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 if change_txout is None: # No suitable change txout; no txout was an address in our wallet. # # Create a new txout for use as change. addr = rpc.getrawchangeaddress() change_txout = CMutableTxOut(0, addr.to_scriptPubKey()) tx.vout.append(change_txout)
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 if change_txout is None: # No suitable change txout; no txout was an address in our wallet. # # Create a new txout for use as change. addr = rpc.getrawchangeaddress() change_txout = CMutableTxOut(0, addr.to_scriptPubKey()) tx.vout.append(change_txout)
def get_address_from_script(self, script): address = CBitcoinAddress.from_scriptPubKey(CScript(script)) return address
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 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)
# 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 # something that dumb and anti-privacy in a real wallet - but without a way # of keeping state this is the best we've got. try: addr = CBitcoinAddress.from_scriptPubKey(tx2.vout[0].scriptPubKey) except ValueError: pass else: # There is an edge case not handled: if we have multiple outputs but # didn't need a change output. But whatever, this is just a demo! if len(tx2.vout) > 1 and rpc.validateaddress(addr)['ismine']: tx2.vout = tx2.vout[1:] # Add the new output payment_txout = CMutableTxOut(args.amount, args.address.to_scriptPubKey()) tx2.vout.append(payment_txout) r = rpc.fundrawtransaction(tx2) tx2 = CMutableTransaction.from_tx(r['tx']) tx2_fee = r['fee']
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))
def dataReceived(self, data): self.buffer += data header = MsgHeader.from_bytes(self.buffer) if len(self.buffer) < header.msglen + 24: return try: stream = BytesIO(self.buffer) m = MsgSerializable.stream_deserialize(stream) self.buffer = stream.read() if m.command == "verack": self.timeouts["verack"].cancel() del self.timeouts["verack"] if "version" not in self.timeouts: self.on_handshake_complete() elif m.command == "version": self.version = m if m.nVersion < 70001 or m.nServices != 1: self.transport.loseConnection() self.timeouts["version"].cancel() del self.timeouts["version"] msg_verack().stream_serialize(self.transport) if self.blockchain is not None: self.to_download = self.version.nStartingHeight - self.blockchain.get_height() if "verack" not in self.timeouts: self.on_handshake_complete() elif m.command == "getdata": for item in m.inv: if item.hash in self.inventory and item.type == 1: transaction = msg_tx() transaction.tx = self.inventory[item.hash] transaction.stream_serialize(self.transport) elif m.command == "inv": for item in m.inv: # This is either an announcement of tx we broadcast ourselves or a tx we have already downloaded. # In either case we only need to callback here. if item.type == 1 and item.hash in self.subscriptions: self.subscriptions[item.hash]["callback"](item.hash) # This is the first time we are seeing this txid. Let's download it and check to see if it sends # coins to any addresses in our subscriptions. elif item.type == 1 and item.hash not in self.inventory: self.timeouts[item.hash] = reactor.callLater(5, self.response_timeout, item.hash) cinv = CInv() cinv.type = 1 cinv.hash = item.hash getdata_packet = msg_getdata() getdata_packet.inv.append(cinv) getdata_packet.stream_serialize(self.transport) # The peer announced a new block. Unlike txs, we should download it, even if we've previously # downloaded it from another peer, to make sure it doesn't contain any txs we didn't know about. elif item.type == 2 or item.type == 3: if self.state == State.DOWNLOADING: self.download_tracker[0] += 1 cinv = CInv() cinv.type = 3 cinv.hash = item.hash getdata_packet = msg_getdata() getdata_packet.inv.append(cinv) getdata_packet.stream_serialize(self.transport) if self.state != State.DOWNLOADING: self.log.debug("Peer %s:%s announced new %s %s" % (self.transport.getPeer().host, self.transport.getPeer().port, CInv.typemap[item.type], b2lx(item.hash))) elif m.command == "tx": if m.tx.GetHash() in self.timeouts: self.timeouts[m.tx.GetHash()].cancel() for out in m.tx.vout: try: addr = str(CBitcoinAddress.from_scriptPubKey(out.scriptPubKey)) except Exception: addr = None if addr in self.subscriptions: if m.tx.GetHash() not in self.subscriptions: # It's possible the first time we are hearing about this tx is following block # inclusion. If this is the case, let's make sure we include the correct number # of confirmations. in_blocks = self.inventory[m.tx.GetHash()] if m.tx.GetHash() in self.inventory else [] confirms = [] if len(in_blocks) > 0: for block in in_blocks: confirms.append(self.blockchain.get_confirmations(block)) self.subscriptions[m.tx.GetHash()] = { "announced": 0, "ann_threshold": self.subscriptions[addr][0], "confirmations": max(confirms) if len(confirms) > 0 else 0, "last_confirmation": 0, "callback": self.subscriptions[addr][1], "in_blocks": in_blocks, "tx": m.tx } self.subscriptions[addr][1](m.tx.GetHash()) if m.tx.GetHash() in self.inventory: del self.inventory[m.tx.GetHash()] elif m.command == "merkleblock": if self.blockchain is not None: self.blockchain.process_block(m.block) if self.state != State.DOWNLOADING: self.blockchain.save() # check for block inclusion of subscribed txs for match in m.block.get_matched_txs(): if match in self.subscriptions: self.subscriptions[match]["in_blocks"].append(m.block.GetHash()) else: # stick the hash here in case this is the first we are hearing about this tx. # when the tx comes over the wire after this block, we will append this hash. self.inventory[match] = [m.block.GetHash()] # run through subscriptions and callback with updated confirmations for txid in self.subscriptions: try: confirms = [] for block in self.subscriptions[txid]["in_blocks"]: confirms.append(self.blockchain.get_confirmations(block)) self.subscriptions[txid]["confirmations"] = max(confirms) self.subscriptions[txid]["callback"](txid) except Exception: pass # If we are in the middle of an initial chain download, let's check to see if we have # either reached the end of the download or if we need to loop back around and make # another get_blocks call. if self.state == State.DOWNLOADING: self.download_count += 1 percent = int((self.download_count / float(self.to_download))*100) if self.download_listener is not None: self.download_listener.progress(percent, self.download_count) self.download_listener.on_block_downloaded((self.transport.getPeer().host, self.transport.getPeer().port), header, self.to_download - self.download_count + 1) if percent == 100: if self.download_listener is not None: self.download_listener.download_complete() self.log.info("Chain download 100% complete") self.download_tracker[1] += 1 # We've downloaded every block in the inv packet and still have more to go. if (self.download_tracker[0] == self.download_tracker[1] and self.blockchain.get_height() < self.version.nStartingHeight): if self.timeouts["download"].active(): self.timeouts["download"].cancel() self.download_blocks(self.callbacks["download"]) # We've downloaded everything so let's callback to the client. elif self.blockchain.get_height() >= self.version.nStartingHeight: self.blockchain.save() self.state = State.CONNECTED self.callbacks["download"]() if self.timeouts["download"].active(): self.timeouts["download"].cancel() elif m.command == "headers": if self.timeouts["download"].active(): self.timeouts["download"].cancel() for header in m.headers: # If this node sent a block with no parent then disconnect from it and callback # on client.check_for_more_blocks. if self.blockchain.process_block(header) is None: self.blockchain.save() self.callbacks["download"]() self.transport.loseConnection() return self.download_count += 1 percent = int((self.download_count / float(self.to_download))*100) if self.download_listener is not None: self.download_listener.progress(percent, self.download_count) self.download_listener.on_block_downloaded((self.transport.getPeer().host, self.transport.getPeer().port), header, self.to_download - self.download_count + 1) if percent == 100: if self.download_listener is not None: self.download_listener.download_complete() self.log.info("Chain download 100% complete") # The headers message only comes in batches of 500 blocks. If we still have more blocks to download # loop back around and call get_headers again. if self.blockchain.get_height() < self.version.nStartingHeight: self.download_blocks(self.callbacks["download"]) else: self.blockchain.save() self.callbacks["download"]() self.state = State.CONNECTED elif m.command == "ping": msg_pong(nonce=m.nonce).stream_serialize(self.transport) else: self.log.debug("Received message %s from %s:%s" % (m.command, self.transport.getPeer().host, self.transport.getPeer().port)) if len(self.buffer) >= 24: self.dataReceived("") except Exception: traceback.print_exc()
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 # # lx() takes *little-endian* hex and converts it to bytes; in Bitcoin # transaction hashes are shown little-endian rather than the usual big-endian. # There's also a corresponding x() convenience function that takes big-endian # hex and converts it to bytes. txid = lx('bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae') vout = 0 # Create the txin structure, which includes the outpoint. The scriptSig # defaults to being empty. txin = CMutableTxIn(COutPoint(txid, vout))
def dataReceived(self, data): self.buffer += data # if self.buffer.length >= sizeof(message header) # messageHeader := MessageHeader.deserialize(self.buffer) # if messageHeader.payloadLength > someBigNumber # throw DropConnection # if self.buffer.length < messageHeader.payloadLength # return try: m = MsgSerializable.from_bytes(self.buffer) self.buffer = "" if m.command == "verack": self.timeouts["verack"].cancel() del self.timeouts["verack"] if "version" not in self.timeouts: self.on_handshake_complete() elif m.command == "version": self.version = m if m.nVersion < 70001: self.transport.loseConnection() self.timeouts["version"].cancel() del self.timeouts["version"] msg_verack().stream_serialize(self.transport) if "verack" not in self.timeouts: self.on_handshake_complete() elif m.command == "getdata": for item in m.inv: if item.hash in self.inventory and item.type == 1: transaction = msg_tx() transaction.tx = self.inventory[item.hash] transaction.stream_serialize(self.transport) elif m.command == "inv": for item in m.inv: # callback tx if item.type == 1 and item.hash in self.subscriptions: self.subscriptions[item.hash]["callback"](item.hash) # download tx and check subscription elif item.type == 1 and item.hash not in self.inventory: self.timeouts[item.hash] = reactor.callLater(5, self.response_timeout, item.hash) cinv = CInv() cinv.type = 1 cinv.hash = item.hash getdata_packet = msg_getdata() getdata_packet.inv.append(cinv) getdata_packet.stream_serialize(self.transport) # download block elif item.type == 2 or item.type == 3: cinv = CInv() cinv.type = 3 cinv.hash = item.hash getdata_packet = msg_getdata() getdata_packet.inv.append(cinv) getdata_packet.stream_serialize(self.transport) print "Peer %s:%s announced new %s %s" % (self.transport.getPeer().host, self.transport.getPeer().port, CInv.typemap[item.type], b2lx(item.hash)) elif m.command == "tx": if m.tx.GetHash() in self.timeouts: self.timeouts[m.tx.GetHash()].cancel() for out in m.tx.vout: addr = str(CBitcoinAddress.from_scriptPubKey(out.scriptPubKey)) if addr in self.subscriptions: if m.tx.GetHash() not in self.subscriptions: self.subscriptions[m.tx.GetHash()] = { "announced": 0, "ann_threshold": self.subscriptions[addr][0], "confirmations": 0, "callback": self.subscriptions[addr][1], "in_blocks": self.inventory[m.tx.GetHash()] if m.tx.GetHash() in self.inventory else [], "tx": m.tx } self.subscriptions[addr][1](m.tx.GetHash()) if m.tx.GetHash() in self.inventory: del self.inventory[m.tx.GetHash()] elif m.command == "merkleblock": self.blockchain.process_block(m.block) if self.blockchain is not None: # check for block inclusion of subscribed txs for match in m.block.get_matched_txs(): if match in self.subscriptions: self.subscriptions[match]["in_blocks"].append(m.block.GetHash()) else: # stick the hash here in case this is a tx we missed on broadcast. # when the tx comes over the wire after this block, we will append this hash. self.inventory[match] = [m.block.GetHash()] # run through subscriptions and callback with updated confirmations for txid in self.subscriptions: if len(txid) == 32: confirms = [] for block in self.subscriptions[txid]["in_blocks"]: confirms.append(self.blockchain.get_confirmations(block)) self.subscriptions[txid]["confirmations"] = max(confirms) self.subscriptions[txid]["callback"](txid) elif m.command == "headers": self.timeouts["download"].cancel() to_download = self.version.nStartingHeight - self.blockchain.get_height() i = 1 for header in m.headers: self.blockchain.process_block(header) if i % 50 == 0 or int((i / float(to_download))*100) == 100: print "Chain download %s%% complete" % int((i / float(to_download))*100) i += 1 if self.blockchain.get_height() < self.version.nStartingHeight: self.download_blocks(self.callbacks["download"]) elif self.callbacks["download"]: self.callbacks["download"]() else: print "Received message %s from %s:%s" % (m.command, self.transport.getPeer().host, self.transport.getPeer().port) except Exception: pass