Example #1
0
    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)
Example #2
0
    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])
Example #3
0
    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])
Example #4
0
    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)
Example #5
0
    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)
Example #6
0
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
Example #7
0
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 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',
    })
Example #9
0
    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
Example #10
0
    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
Example #11
0
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',
    })
Example #12
0
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',
    })
Example #13
0
    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
Example #14
0
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))