Example #1
0
    def __bytes__(self):
        """ Serializes the object into a byte stream.

        Returns:
            b (bytes): The serialized transaction.
        """
        return (
            pack_u32(self.version) +                      # Version
            pack_compact_int(self.num_inputs) +           # Input count
            b''.join([bytes(i) for i in self.inputs]) +   # Inputs
            pack_compact_int(self.num_outputs) +          # Output count
            b''.join([bytes(o) for o in self.outputs]) +  # Outputs
            pack_u32(self.lock_time)                      # Lock time
        )
Example #2
0
File: txn.py Project: shayanb/two1
    def __bytes__(self):
        """ Serializes the object into a byte stream.

        Returns:
            b (bytes): The serialized transaction.
        """
        return (
            pack_u32(self.version) +                      # Version
            pack_compact_int(self.num_inputs) +           # Input count
            b''.join([bytes(i) for i in self.inputs]) +   # Inputs
            pack_compact_int(self.num_outputs) +          # Output count
            b''.join([bytes(o) for o in self.outputs]) +  # Outputs
            pack_u32(self.lock_time)                      # Lock time
        )
Example #3
0
    def create_payment_tx(self, deposit_tx, redeem_script, merchant_public_key,
                          customer_public_key, amount, fee):
        # Find P2SH output index in deposit_tx
        deposit_utxo_index = deposit_tx.output_index_for_address(
            redeem_script.hash160())

        # Look up deposit amount
        deposit_amount = deposit_tx.outputs[deposit_utxo_index].value - fee

        # Build unsigned payment transaction
        script_sig = Script()
        inp = TransactionInput(deposit_tx.hash, deposit_utxo_index, script_sig,
                               0xffffffff)
        out1 = TransactionOutput(
            amount, Script.build_p2pkh(merchant_public_key.hash160()))
        out2 = TransactionOutput(
            deposit_amount - amount,
            Script.build_p2pkh(customer_public_key.hash160()))
        payment_tx = Transaction(1, [inp], [out1, out2], 0x0)

        # Sign payment transaction
        public_key = redeem_script.customer_public_key
        private_key = self.get_private_for_public(public_key)
        sig = payment_tx.get_signature_for_input(0, Transaction.SIG_HASH_ALL,
                                                 private_key, redeem_script)[0]

        # Update input script sig
        script_sig = Script([
            sig.to_der() + utils.pack_compact_int(Transaction.SIG_HASH_ALL),
            'OP_1',
            bytes(redeem_script)
        ])
        payment_tx.inputs[0].script = script_sig

        return payment_tx
Example #4
0
    def create_payment_tx(self, deposit_tx, redeem_script, merchant_public_key,
                          customer_public_key, amount, fee):
        # Find P2SH output index in deposit_tx
        deposit_utxo_index = deposit_tx.output_index_for_address(redeem_script.hash160())

        # Look up deposit amount
        deposit_amount = deposit_tx.outputs[deposit_utxo_index].value - fee

        # Build unsigned payment transaction
        script_sig = Script()
        inp = TransactionInput(deposit_tx.hash, deposit_utxo_index, script_sig, 0xffffffff)
        out1 = TransactionOutput(amount, Script.build_p2pkh(merchant_public_key.hash160()))
        out2 = TransactionOutput(deposit_amount - amount, Script.build_p2pkh(customer_public_key.hash160()))
        payment_tx = Transaction(1, [inp], [out1, out2], 0x0)

        # Sign payment transaction
        public_key = redeem_script.customer_public_key
        private_key = self.get_private_for_public(public_key)
        sig = payment_tx.get_signature_for_input(0, Transaction.SIG_HASH_ALL, private_key, redeem_script)[0]

        # Update input script sig
        script_sig = Script(
            [sig.to_der() + utils.pack_compact_int(Transaction.SIG_HASH_ALL), 'OP_1', bytes(redeem_script)])
        payment_tx.inputs[0].script = script_sig

        return payment_tx
Example #5
0
    def sign_half_signed_payment(self, payment_tx, redeem_script):
        """Sign a half-signed payment transaction.

        Args:
            payment_tx (two1.bitcoin.Transaction): an object that
                contains a transaction from a customer, whether for a refund
                or general payment, to be signed by the merchant.

        Returns:
            two1.bitcoin.Transaction: an object that contains a transaction
                that has been signed by both the customer and the merchant.
        """
        # Verify that the deposit spend has only one input
        if len(payment_tx.inputs) != 1:
            raise InvalidPaymentError('Transaction should have one input.')

        # Get the public and private keys associated with this transaction
        merchant_public_key = redeem_script.merchant_public_key
        private_key = self._wallet.get_private_for_public(merchant_public_key)

        # Sign the first (and only) input in the transaction
        sig = payment_tx.get_signature_for_input(0, Transaction.SIG_HASH_ALL, private_key, redeem_script)[0]

        # Update input script sig
        payment_tx.inputs[0].script.insert(1, sig.to_der() + utils.pack_compact_int(Transaction.SIG_HASH_ALL))

        # Return a Transaction containing the fully-signed transaction.
        return payment_tx
Example #6
0
    def __bytes__(self):
        """ Serializes the Block object into a byte stream.

        Returns:
            b (bytes): The serialized byte stream.
        """
        return (bytes(self.block_header) + pack_compact_int(len(self.txns)) +
                b''.join([bytes(t) for t in self.txns]))
Example #7
0
File: txn.py Project: shayanb/two1
    def sign_input(self, input_index, hash_type, private_key, sub_script):
        """ Signs an input.

        Args:
            input_index (int): The index of the input to sign.
            hash_type (int): What kind of signature hash to do.
            private_key (crypto.PrivateKey): private key with which
                to sign the transaction.
            sub_script (Script): the scriptPubKey of the corresponding
                utxo being spent if the outpoint is P2PKH or the redeem
                script if the outpoint is P2SH.
        """
        if input_index < 0 or input_index >= len(self.inputs):
            raise ValueError("Invalid input index.")

        inp = self.inputs[input_index]

        multisig = False
        if sub_script.is_multisig_redeem():
            multisig = True
        elif not sub_script.is_p2pkh():
            raise TypeError("Signing arbitrary redeem scripts is not currently supported.")

        tmp_script = sub_script.remove_op("OP_CODESEPARATOR")

        # Before signing we should verify that the address in the
        # sub_script corresponds to that of the private key
        m = self._match_public_key(private_key, tmp_script)
        if not m['match']:
            if multisig:
                msg = "Public key derived from private key does not match any of the public keys in redeem script."
            else:
                msg = "Address derived from private key does not match sub_script!"
            raise ValueError(msg)

        sig, signed_message = self.get_signature_for_input(
            input_index, hash_type, private_key, sub_script)

        if multisig:
            # For multisig, we need to determine if there are already
            # signatures and if so, where we insert this signature
            inp.script = self._do_multisig_script(
                [dict(index=m['info']['multisig_key_index'],
                      signature=sig)],
                signed_message,
                inp.script,
                tmp_script,
                hash_type)
        else:
            pub_key_bytes = self._get_public_key_bytes(private_key,
                                                       m['info']['compressed'])
            inp.script = Script([sig.to_der() + pack_compact_int(hash_type),
                                 pub_key_bytes])

        return True
Example #8
0
    def sign_input(self, input_index, hash_type, private_key, sub_script):
        """ Signs an input.

        Args:
            input_index (int): The index of the input to sign.
            hash_type (int): What kind of signature hash to do.
            private_key (crypto.PrivateKey): private key with which
                to sign the transaction.
            sub_script (Script): the scriptPubKey of the corresponding
                utxo being spent if the outpoint is P2PKH or the redeem
                script if the outpoint is P2SH.
        """
        if input_index < 0 or input_index >= len(self.inputs):
            raise ValueError("Invalid input index.")

        inp = self.inputs[input_index]

        multisig = False
        if sub_script.is_multisig_redeem():
            multisig = True
        elif not sub_script.is_p2pkh():
            raise TypeError("Signing arbitrary redeem scripts is not currently supported.")

        tmp_script = sub_script.remove_op("OP_CODESEPARATOR")

        # Before signing we should verify that the address in the
        # sub_script corresponds to that of the private key
        m = self._match_public_key(private_key, tmp_script)
        if not m['match']:
            if multisig:
                msg = "Public key derived from private key does not match any of the public keys in redeem script."
            else:
                msg = "Address derived from private key does not match sub_script!"
            raise ValueError(msg)

        sig, signed_message = self.get_signature_for_input(
            input_index, hash_type, private_key, sub_script)

        if multisig:
            # For multisig, we need to determine if there are already
            # signatures and if so, where we insert this signature
            inp.script = self._do_multisig_script(
                [dict(index=m['info']['multisig_key_index'],
                      signature=sig)],
                signed_message,
                inp.script,
                tmp_script,
                hash_type)
        else:
            pub_key_bytes = self._get_public_key_bytes(private_key,
                                                       m['info']['compressed'])
            inp.script = Script([sig.to_der() + pack_compact_int(hash_type),
                                 pub_key_bytes])

        return True
Example #9
0
    def __bytes__(self):
        """ Serializes the Block object into a byte stream.

        Returns:
            b (bytes): The serialized byte stream.
        """
        return (
            bytes(self.block_header) +
            pack_compact_int(len(self.txns)) +
            b''.join([bytes(t) for t in self.txns])
        )
Example #10
0
File: txn.py Project: shayanb/two1
    def _do_multisig_script(self, sigs, message, current_script_sig,
                            redeem_script, hash_type):
        # If the current script is empty or None, create it
        sig_script = None
        if current_script_sig is None or not str(current_script_sig):
            sig_bytes = [s['signature'].to_der() + pack_compact_int(hash_type)
                         for s in sigs]

            sig_script = Script.build_multisig_sig(sigs=sig_bytes,
                                                   redeem_script=redeem_script)
        else:
            # Need to extract all the sigs already present
            multisig_params = redeem_script.extract_multisig_redeem_info()
            sig_info = current_script_sig.extract_multisig_sig_info()

            # Do a few quick sanity checks
            if str(sig_info['redeem_script']) != str(redeem_script):
                raise ValueError(
                    "Redeem script in signature script does not match redeem_script!")

            if len(sig_info['signatures']) == multisig_params['n']:
                # Already max number of signatures
                return current_script_sig

            # Go through the signatures and match them up to the public keys
            # in the redeem script
            pub_keys = []
            for pk in multisig_params['public_keys']:
                pub_keys.append(crypto.PublicKey.from_bytes(pk))

            existing_sigs = []
            for s in sig_info['signatures']:
                s1, h = s[:-1], s[-1]  # Last byte is hash_type
                existing_sigs.append(crypto.Signature.from_der(s1))
                if h != hash_type:
                    raise ValueError("hash_type does not match that of the existing signatures.")

            # Match them up
            existing_sig_indices = self._match_sigs_to_pub_keys(existing_sigs,
                                                                pub_keys,
                                                                message)
            sig_indices = {s['index']: s['signature'] for s in sigs}

            # Make sure there are no dups
            all_indices = set(list(existing_sig_indices.keys()) +
                              list(sig_indices.keys()))
            if len(all_indices) < len(existing_sig_indices) + len(sig_indices):
                raise ValueError("At least one signature matches an existing signature.")

            if len(all_indices) > multisig_params['n']:
                raise ValueError("There are too many signatures.")

            all_sigs = []
            for i in sorted(all_indices):
                if i in existing_sig_indices:
                    all_sigs.append(existing_sig_indices[i])
                elif i in sig_indices:
                    all_sigs.append(sig_indices[i])

            all_sigs_bytes = [s.to_der() + pack_compact_int(hash_type)
                              for s in all_sigs]
            sig_script = Script.build_multisig_sig(all_sigs_bytes,
                                                   redeem_script)

        return sig_script
    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.
            payment_tx (string): half-signed payment transaction from a
                customer.
        Returns:
            (string): payment transaction id
        """
        # Parse payment channel `payment` parameters
        payment_tx = Transaction.from_hex(payment_tx)

        # Get channel and addresses related to the deposit
        channel = self._db.pc.lookup(deposit_txid)

        if not channel:
            raise PaymentChannelNotFoundError('Related channel not found.')

        # Get merchant public key information from payment channel
        merchant_public_key = PublicKey.from_hex(channel.merchant_pubkey)

        # Verify that redeem script contains the merchant public key
        redeem_script = PaymentChannelRedeemScript.from_bytes(payment_tx.inputs[0].script[-1])
        if redeem_script.merchant_public_key.to_hex() != merchant_public_key.to_hex():
            raise BadTransactionError('Invalid merchant pubkey.')

        # Verify that the payment has a valid signature from the customer
        txn_copy = payment_tx._copy_for_sig(0, Transaction.SIG_HASH_ALL, redeem_script)
        msg_to_sign = bytes(Hash.dhash(bytes(txn_copy) + pack_u32(Transaction.SIG_HASH_ALL)))
        sig = Signature.from_der(payment_tx.inputs[0].script[0][:-1])
        if not redeem_script.customer_public_key.verify(msg_to_sign, sig, False):
            raise BadTransactionError('Invalid payment signature.')

        # Verify the length of the script is what we expect
        if len(payment_tx.inputs[0].script) != 3:
            raise BadTransactionError('Invalid payment channel transaction structure.')

        # Verify the script template is valid for accepting a merchant signature
        if (not Script.validate_template(payment_tx.inputs[0].script, [bytes, 'OP_1', bytes]) and
                not Script.validate_template(payment_tx.inputs[0].script, [bytes, 'OP_TRUE', bytes])):
            raise BadTransactionError('Invalid payment channel transaction structure.')

        # Verify that the payment channel is ready
        if channel.state == ChannelSQLite3.CONFIRMING:
            confirmed = self._blockchain.check_confirmed(channel.deposit_txid)
            if confirmed:
                self._db.pc.update_state(channel.deposit_txid, ChannelSQLite3.READY)
            else:
                raise ChannelClosedError('Payment channel not ready.')
        elif channel.state == ChannelSQLite3.CLOSED:
            raise ChannelClosedError('Payment channel closed.')

        # Verify that payment is made to the merchant public key
        index = payment_tx.output_index_for_address(merchant_public_key.hash160())
        if index is None:
            raise BadTransactionError('Payment must pay to merchant pubkey.')

        # Verify that both payments are not below the dust limit
        for output_index, output in enumerate(payment_tx.outputs):
            if output.value < PaymentServer.DUST_LIMIT:
                # Payment to merchant is less than dust limit
                if output_index == index:
                    raise BadTransactionError(
                        'Initial payment must be greater than {}.'.format(PaymentServer.DUST_LIMIT))
                # Payment to customer is less than dust limit
                else:
                    raise BadTransactionError(
                        'Payment channel balance is not large enough to make payment.')

        # Validate that the payment is more than the last one
        new_pmt_amt = payment_tx.outputs[index].value
        if new_pmt_amt <= channel.last_payment_amount:
            raise BadTransactionError('Payment must be greater than 0.')

        # Verify that the transaction has adequate fees
        net_pmt_amount = sum([d.value for d in payment_tx.outputs])
        deposit_amount = channel.amount
        fee = deposit_amount - net_pmt_amount
        if fee < PaymentServer.MIN_TX_FEE:
            raise BadTransactionError('Payment must have adequate fees.')

        # Recreate redeem script from merchant side
        redeem_script_copy = PaymentChannelRedeemScript(
            merchant_public_key,
            redeem_script.customer_public_key,
            channel.expires_at)
        if redeem_script.to_hex() != redeem_script_copy.to_hex():
            raise BadTransactionError('Invalid redeem script.')

        # Recreate customer payment from merchant side
        payment_tx_copy = self._wallet.create_unsigned_payment_tx(
            channel.deposit_tx, redeem_script, new_pmt_amt, fee)

        # Recreate signed input script using signature from customer
        hash_type = pack_compact_int(Transaction.SIG_HASH_ALL)
        signed_input_script = Script(
            [sig.to_der() + hash_type, "OP_1", bytes(redeem_script)])
        payment_tx_copy.inputs[0].script = signed_input_script

        if payment_tx.to_hex() != payment_tx_copy.to_hex():
            raise BadTransactionError('Invalid payment channel transaction structure.')

        # Update the current payment transaction
        self._db.pc.update_payment(deposit_txid, payment_tx_copy, new_pmt_amt)
        self._db.pmt.create(deposit_txid, payment_tx_copy, new_pmt_amt - channel.last_payment_amount)

        return str(payment_tx_copy.hash)
Example #12
0
    def _do_multisig_script(self, sigs, message, current_script_sig,
                            redeem_script, hash_type):
        # If the current script is empty or None, create it
        sig_script = None
        if current_script_sig is None or not str(current_script_sig):
            sig_bytes = [s['signature'].to_der() + pack_compact_int(hash_type)
                         for s in sigs]

            sig_script = Script.build_multisig_sig(sigs=sig_bytes,
                                                   redeem_script=redeem_script)
        else:
            # Need to extract all the sigs already present
            multisig_params = redeem_script.extract_multisig_redeem_info()
            sig_info = current_script_sig.extract_multisig_sig_info()

            # Do a few quick sanity checks
            if str(sig_info['redeem_script']) != str(redeem_script):
                raise ValueError(
                    "Redeem script in signature script does not match redeem_script!")

            if len(sig_info['signatures']) == multisig_params['n']:
                # Already max number of signatures
                return current_script_sig

            # Go through the signatures and match them up to the public keys
            # in the redeem script
            pub_keys = []
            for pk in multisig_params['public_keys']:
                pub_keys.append(crypto.PublicKey.from_bytes(pk))

            existing_sigs = []
            for s in sig_info['signatures']:
                s1, h = s[:-1], s[-1]  # Last byte is hash_type
                existing_sigs.append(crypto.Signature.from_der(s1))
                if h != hash_type:
                    raise ValueError("hash_type does not match that of the existing signatures.")

            # Match them up
            existing_sig_indices = self._match_sigs_to_pub_keys(existing_sigs,
                                                                pub_keys,
                                                                message)
            sig_indices = {s['index']: s['signature'] for s in sigs}

            # Make sure there are no dups
            all_indices = set(list(existing_sig_indices.keys()) +
                              list(sig_indices.keys()))
            if len(all_indices) < len(existing_sig_indices) + len(sig_indices):
                raise ValueError("At least one signature matches an existing signature.")

            if len(all_indices) > multisig_params['n']:
                raise ValueError("There are too many signatures.")

            all_sigs = []
            for i in sorted(all_indices):
                if i in existing_sig_indices:
                    all_sigs.append(existing_sig_indices[i])
                elif i in sig_indices:
                    all_sigs.append(sig_indices[i])

            all_sigs_bytes = [s.to_der() + pack_compact_int(hash_type)
                              for s in all_sigs]
            sig_script = Script.build_multisig_sig(all_sigs_bytes,
                                                   redeem_script)

        return sig_script