def extract_sig_info(self): """ Returns the signature and corresponding public key. Returns: dict: Contains three keys: 'hash_type': Integer 'signature': The DER-encoded signature 'public_key': The bytes corresponding the public key. """ if len(self.ast) != 2: raise TypeError("Script is not a P2PKH signature script") try: sig_hex = self.ast[0] if sig_hex.startswith("0x"): sig_hex = sig_hex[2:] sig_bytes = bytes.fromhex(sig_hex) hash_type = sig_bytes[-1] sig = Signature.from_der(sig_bytes[:-1]) except ValueError as e: raise TypeError("Signature does not appear to be valid") try: pub_key_hex = self.ast[1] if pub_key_hex.startswith("0x"): pub_key_hex = pub_key_hex[2:] pub_key_bytes = bytes.fromhex(pub_key_hex) pub_key = PublicKey.from_bytes(pub_key_bytes) except ValueError: raise TypeError("Public key does not appear to be valid") return dict(hash_type=hash_type, signature=sig_bytes, public_key=pub_key_bytes)
def extract_sig_info(self): """ Returns the signature and corresponding public key. Returns: dict: Contains three keys: 'hash_type': Integer 'signature': The DER-encoded signature 'public_key': The bytes corresponding the public key. """ if len(self) != 2: raise TypeError("Script is not a P2PKH signature script") if not isinstance(self[0], bytes) or \ not isinstance(self[1], bytes): raise TypeError( "Signature script must contain two push operations.") try: sig_bytes = self[0] hash_type = sig_bytes[-1] _ = Signature.from_der(sig_bytes[:-1]) except ValueError: raise TypeError("Signature does not appear to be valid") try: _ = PublicKey.from_bytes(self[1]) except ValueError: raise TypeError("Public key does not appear to be valid") return dict(hash_type=hash_type, signature=sig_bytes, public_key=self[1])
def _op_checksig(self): """ The entire transaction's outputs, inputs, and script (from the most recently-executed OP_CODESEPARATOR to the end) are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise. """ self._check_stack_len(2) self._check_txn() pub_key_bytes = self._stack.pop() s = self._stack.pop() sig_der, hash_type = s[:-1], s[-1] pub_key = PublicKey.from_bytes(pub_key_bytes) sig = Signature.from_der(sig_der) txn_copy = self._txn._copy_for_sig(input_index=self._input_index, hash_type=hash_type, sub_script=self._sub_script) msg = bytes(txn_copy) + utils.pack_u32(hash_type) tx_digest = hashlib.sha256(msg).digest() verified = pub_key.verify(tx_digest, sig) self._stack.append(verified)
def get_private_for_public(public_key): """ RPC method to get the private key for the given public_key, if it is a part of this wallet. Args: public_key (str): Base58Check encoded serialization of the public key. Returns: str: A Base58Check encoded serialization of the private key object or None. """ w = wallet['obj'] try: pub_key = HDPublicKey.from_b58check(public_key) except ValueError: pub_key = PublicKey.from_base64(public_key) priv_key = w.get_private_key(pub_key.address(testnet=w._testnet)) return priv_key.to_b58check() if priv_key is not None else None
def cmd_new(): # Get hex-encoded input (owner) public key try: owner_pubkey = PublicKey.from_bytes(request.args.get('owner')) except: abort(400) # Generate next available HD wallet index try: cursor = connection.cursor() next_idx = srvdb_last_idx(cursor) + 1 except: abort(500) # Derive HD public key acct = wallet._check_and_get_accounts([SRV_ACCT]) hd_pubkey = acct.get_public_key(False, next_idx) pubkey = binascii.hexlify(hd_public.compressed_bytes()) address = hd_pubkey.address() owner_b64 = owner_pubkey.to_base64() # Add public key to db try: cursor.execute("INSERT INTO metadata VALUES(?, ?, ?, ?)", (address, pubkey, next_idx, owner_b64)) except: abort(500) # Return contract id, public key obj = { 'id': next_idx, 'contract_key': pubkey, } body = json.dumps(ret_obj, indent=2) return (body, 200, { 'Content-length': len(body), 'Content-type': 'application/json', })
def receive_payment(self, deposit_txid, payment_tx): """Receive and process a payment within the channel. The customer makes a payment in the channel by sending the merchant a half-signed payment transaction. The merchant signs the other half of the transaction and saves it in its records (but does not broadcast it or send it to the customer). The merchant responds with 200 to verify that the payment was handled successfully. Args: deposit_txid (string): string representation of the deposit transaction hash. This is used to look up the payment channel. deposit_tx (two1.lib.bitcoin.txn.Transaction): half-signed deposit Transaction from a customer. This object is passed by reference and modified directly. Returns: (boolean): whether the payment was sucessfully processed. """ # Verify that the transaction is what we expect self._wallet.verify_half_signed_tx(payment_tx) # Get channel and addresses related to the deposit try: channel = self._db.pc.lookup(deposit_txid) except: raise PaymentServerNotFoundError('Related channel not found.') # Get merchant public key information from payment channel last_pmt_amt = channel['last_payment_amount'] merch = channel['merchant_pubkey'] merch_pubkey = PublicKey.from_bytes(codecs.decode(merch, 'hex_codec')) index = payment_tx.output_index_for_address(merch_pubkey.hash160()) # Verify that the payment channel is still open if (channel['state'] != 'confirming' and channel['state'] != 'ready'): raise ChannelClosedError('Payment channel closed.') # Find the payment amount associated with the merchant address if index is None: raise BadTransactionError('Payment must pay to merchant pubkey.') # Validate that the payment is more than the last one new_pmt_amt = payment_tx.outputs[index].value if new_pmt_amt <= last_pmt_amt: raise BadTransactionError('Micropayment must be greater than 0.') # Verify that the payment channel is still open if (channel['state'] != 'confirming' and channel['state'] != 'ready'): raise ChannelClosedError('Payment channel closed.') # Verify that the transaction has adequate fees net_pmt_amount = sum([d.value for d in payment_tx.outputs]) deposit_amount = channel['amount'] if deposit_amount < net_pmt_amount + PaymentServer.MIN_TX_FEE: raise BadTransactionError('Payment must have adequate fees.') # Sign the remaining half of the transaction self._wallet.sign_half_signed_tx(payment_tx, merch_pubkey) # Update the current payment transaction self._db.pc.update_payment(deposit_txid, payment_tx, new_pmt_amt) self._db.pmt.create(deposit_txid, payment_tx, new_pmt_amt-last_pmt_amt) return True
def _op_checkmultisig(self, partial=False): """ Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. """ self._check_stack_len(1) self._check_txn() num_keys = self._stack.pop() self._check_stack_len(num_keys) keys_bytes = [] for i in range(num_keys): keys_bytes.insert(0, self._stack.pop()) public_keys = [PublicKey.from_bytes(p) for p in keys_bytes] min_num_sigs = self._stack.pop() # Although "m" is the *minimum* number of required signatures, bitcoin # core only consumes "m" signatures and then expects an OP_0. This # means that if m < min_num_sigs <= n, bitcoin core will return a # script failure. See: # https://github.com/bitcoin/bitcoin/blob/0.10/src/script/interpreter.cpp#L840 # We will do the same. hash_types = set() sigs = [] for i in range(min_num_sigs): s = self._stack.pop() try: sig = Signature.from_der(s[:-1]) hash_types.add(s[-1]) sigs.insert(0, sig) except ValueError: if partial: # Put it back on stack self._stack.append(s) else: # If not a partial evaluation there are not enough # sigs rv = False break if len(hash_types) != 1: raise ScriptInterpreterError("Not all signatures have the same hash type!") hash_type = hash_types.pop() txn_copy = self._txn._copy_for_sig(input_index=self._input_index, hash_type=hash_type, sub_script=self._sub_script) msg = bytes(txn_copy) + utils.pack_u32(hash_type) txn_digest = hashlib.sha256(msg).digest() # Now we verify last_match = -1 rv = True match_count = 0 for sig in sigs: matched_any = False for i, pub_key in enumerate(public_keys[last_match+1:]): if pub_key.verify(txn_digest, sig): last_match = i match_count += 1 matched_any = True break if not matched_any: # Bail early if the sig couldn't be verified # by any public key rv = False break rv &= match_count >= min_num_sigs # Now make sure the last thing on the stack is OP_0 if len(self._stack) == 1: rv &= self._stack.pop() == b'' rv &= len(self._stack) == 0 else: rv = False self._stack.append(rv) if partial: self.match_count = match_count
def cmd_sign(id): body = request.data body_len = len(body) # get associated metadata try: cursor = connection.cursor() row = cursor.execute("SELECT * FROM metadata WHERE hd_index = ?", (id, )).fetchone() if row is None: abort(404) hd_index = int(row[0]) owner_key = PublicKey.from_bytes(row[3]) except: abort(500) # check content-length clen_str = request.headers.get('content-length') if clen_str is None: abort(400) clen = int(clen_str) if clen != body_len: abort(400) # check content-type ctype = request.headers.get('content-type') if ctype is None or ctype != 'application/json': abort(400) # parse JSON body try: in_obj = json.loads(body) if (not 'msg' in in_obj or not 'sig' in in_obj or not 'hash_type' in in_obj or not 'input_idx' in in_obj or not 'script' in in_obj): abort(400) hash_type = int(in_obj['hash_type']) input_idx = int(in_obj['input_idx']) script = Script.from_bytes(binascii.unhexlify(in_obj['script'])) tx_hex = binascii.unhexlify(in_obj['msg']) broadcast = False if 'broadcast' in in_obj and in_obj['broadcast'] == True: broadcast = True except: abort(400) # validate base64-encoded signature on hex-encoded transaction try: rc = PublicKey.verify_bitcoin(tx_hex, in_obj['sig'], owner_key.address()) if not rc: abort(400) tx = Transaction.from_hex(tx_hex) except: abort(400) # get HD wallet account, privkey for this contract acct = wallet._check_and_get_accounts([SRV_ACCT]) hd_privkey = acct.get_private_key(False, hd_index) # try to sign the input try: tx.sign_input(input_idx, hash_type, hd_privkey, script) except: abort(400) # broadcast transaction to network if broadcast: wallet.broadcast(tx) # return updated transaction output_data = tx.to_hex() return (output_data, 200, { 'Content-length': len(output_data), 'Content-type': 'text/plain', })
def cmd_sign(id): body = request.data body_len = len(body) # get associated metadata try: cursor = connection.cursor() row = cursor.execute("SELECT * FROM metadata WHERE hd_index = ?", (id,)).fetchone() if row is None: abort(404) hd_index = int(row[0]) owner_key = PublicKey.from_bytes(row[3]) except: abort(500) # check content-length clen_str = request.headers.get('content-length') if clen_str is None: abort(400) clen = int(clen_str) if clen != body_len: abort(400) # check content-type ctype = request.headers.get('content-type') if ctype is None or ctype != 'application/json': abort(400) # parse JSON body try: in_obj = json.loads(body) if (not 'msg' in in_obj or not 'sig' in in_obj or not 'hash_type' in in_obj or not 'input_idx' in in_obj or not 'script' in in_obj): abort(400) hash_type = int(in_obj['hash_type']) input_idx = int(in_obj['input_idx']) script = Script.from_bytes(binascii.unhexlify(in_obj['script'])) tx_hex = binascii.unhexlify(in_obj['msg']) broadcast = False if 'broadcast' in in_obj and in_obj['broadcast'] == True: broadcast = True except: abort(400) # validate base64-encoded signature on hex-encoded transaction try: rc = PublicKey.verify_bitcoin(tx_hex, in_obj['sig'], owner_key.address()) if not rc: abort(400) tx = Transaction.from_hex(tx_hex) except: abort(400) # get HD wallet account, privkey for this contract acct = wallet._check_and_get_accounts([SRV_ACCT]) hd_privkey = acct.get_private_key(False, hd_index) # try to sign the input try: tx.sign_input(input_idx, hash_type, hd_privkey, script) except: abort(400) # broadcast transaction to network if broadcast: wallet.broadcast(tx) # return updated transaction output_data = tx.to_hex() return (output_data, 200, { 'Content-length': len(output_data), 'Content-type': 'text/plain', })
def _op_checkmultisig(self, partial=False): """ Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. """ self._check_stack_len(1) self._check_txn() num_keys = self._stack.pop() self._check_stack_len(num_keys) keys_bytes = [] for i in range(num_keys): keys_bytes.insert(0, self._stack.pop()) public_keys = [PublicKey.from_bytes(p) for p in keys_bytes] min_num_sigs = self._stack.pop() # Although "m" is the *minimum* number of required signatures, bitcoin # core only consumes "m" signatures and then expects an OP_0. This # means that if m < min_num_sigs <= n, bitcoin core will return a # script failure. See: # https://github.com/bitcoin/bitcoin/blob/0.10/src/script/interpreter.cpp#L840 # We will do the same. hash_types = set() sigs = [] for i in range(min_num_sigs): s = self._stack.pop() try: sig = Signature.from_der(s[:-1]) hash_types.add(s[-1]) sigs.insert(0, sig) except ValueError: if partial: # Put it back on stack self._stack.append(s) else: # If not a partial evaluation there are not enough # sigs rv = False break if len(hash_types) != 1: raise ScriptInterpreterError( "Not all signatures have the same hash type!") hash_type = hash_types.pop() txn_copy = self._txn._copy_for_sig(input_index=self._input_index, hash_type=hash_type, sub_script=self._sub_script) msg = bytes(txn_copy) + utils.pack_u32(hash_type) txn_digest = hashlib.sha256(msg).digest() # Now we verify last_match = -1 rv = True match_count = 0 for sig in sigs: matched_any = False for i, pub_key in enumerate(public_keys[last_match + 1:]): if pub_key.verify(txn_digest, sig): last_match = i match_count += 1 matched_any = True break if not matched_any: # Bail early if the sig couldn't be verified # by any public key rv = False break rv &= match_count >= min_num_sigs # Now make sure the last thing on the stack is OP_0 if len(self._stack) == 1: rv &= self._stack.pop() == b'' rv &= len(self._stack) == 0 else: rv = False self._stack.append(rv) if partial: self.match_count = match_count
from two1.lib.bitcoin.txn import Transaction from two1.lib.wallet import Wallet from two1.lib.bitcoin.script import Script from two1.lib.blockchain.twentyone_provider import TwentyOneProvider import two1.lib.bitcoin as bitcoin provider = TwentyOneProvider() wallet = Wallet() pubkey = input("Please enter the public key that was used to create the script") tx_hex = input("Please enter the transaction hex") server_pubkey = input("Please enter server pub key") white_pubkey = input("Please enter white pub key") black_pubkey = input("Please enter black pub key") their_pubkey = PublicKey.from_bytes(pubkey) tx = Transaction.from_hex(tx_hex) private_key = wallet.get_private_for_public(their_pubkey) public_keys = [PublicKey.from_bytes(server_pubkey).compressed_bytes, PublicKey.from_bytes(white_pubkey).compressed_bytes, PublicKey.from_bytes(black_pubkey).compressed_bytes] redeem_script = Script.build_multisig_redeem(2, public_keys) for i, inp in enumerate(tx.inputs): tx.sign_input(i, bitcoin.Transaction.SIG_HASH_ALL, private_key, redeem_script) txid = provider.broadcast_transaction(tx.to_hex()) print("Transaction ID: {}".format(txid))