def test_send_native_invalid_address(self):

        coin = coins.by_name('Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInputType(
            # 49'/1'/0'/0/0" - tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 0, 0],
            amount=12300000,
            prev_hash=unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'),
            prev_index=0,
            script_type=InputScriptType.SPENDWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        out1 = TxOutputType(
            address='TB1Q694CCP5QCC0UDMFWGP692U2S2HJPQ5H407URTU',  # Error: should be lower case
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=12300000 - 11000 - 5000000,
            address_n=[],
            multisig=None,
        )
        tx = SignTx(coin_name='Testnet', version=None, lock_time=None, inputs_count=1, outputs_count=1)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(inputs=[inp1])),

            helpers.UiConfirmForeignAddress(address_n=inp1.address_n),
            True,

            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(outputs=[out1])),
            None
        ]

        ns = get_namespaces_for_coin(coin)
        keychain = Keychain(seed, ns)
        signer = bitcoin.Bitcoin(tx, keychain, coin).signer()
        for request, response in chunks(messages, 2):
            if response is None:
                with self.assertRaises(wire.DataError):
                    signer.send(request)
            else:
                self.assertEqual(signer.send(request), response)
    def test_under_threshold(self):
        coin_bitcoin = coins.by_name('Bitcoin')

        ptx1 = TransactionType(version=1, lock_time=0, inputs_cnt=2, outputs_cnt=1)
        pinp1 = TxInputType(script_sig=unhexlify('483045022072ba61305fe7cb542d142b8f3299a7b10f9ea61f6ffaab5dca8142601869d53c0221009a8027ed79eb3b9bc13577ac2853269323434558528c6b6a7e542be46e7e9a820141047a2d177c0f3626fc68c53610b0270fa6156181f46586c679ba6a88b34c6f4874686390b4d92e5769fbb89c8050b984f4ec0b257a0e5c4ff8bd3b035a51709503'),
                            prev_hash=unhexlify('c16a03f1cf8f99f6b5297ab614586cacec784c2d259af245909dedb0e39eddcf'),
                            prev_index=1,
                            script_type=None,
                            sequence=None)
        pinp2 = TxInputType(script_sig=unhexlify('48304502200fd63adc8f6cb34359dc6cca9e5458d7ea50376cbd0a74514880735e6d1b8a4c0221008b6ead7fe5fbdab7319d6dfede3a0bc8e2a7c5b5a9301636d1de4aa31a3ee9b101410486ad608470d796236b003635718dfc07c0cac0cfc3bfc3079e4f491b0426f0676e6643a39198e8e7bdaffb94f4b49ea21baa107ec2e237368872836073668214'),
                            prev_hash=unhexlify('1ae39a2f8d59670c8fc61179148a8e61e039d0d9e8ab08610cb69b4a19453eaf'),
                            prev_index=1,
                            script_type=None,
                            sequence=None)
        pout1 = TxOutputBinType(script_pubkey=unhexlify('76a91424a56db43cf6f2b02e838ea493f95d8d6047423188ac'),
                                amount=390000,
                                address_n=None)

        inp1 = TxInputType(address_n=[0],  # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
                           # amount=390000,
                           prev_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'),
                           prev_index=0,
                           amount=None,
                           multisig=None,
                           script_type=None,
                           sequence=None)
        out1 = TxOutputType(address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
                            amount=390000 - 90000,  # fee increased to 90000, slightly less than the threshold
                            script_type=OutputScriptType.PAYTOADDRESS,
                            address_n=None)
        tx = SignTx(coin_name=None, version=None, lock_time=None, inputs_count=1, outputs_count=1)

        messages = [
            None,
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(request_type=TXMETA, details=TxRequestDetailsType(request_index=None, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
            TxAck(tx=ptx1),
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
            TxAck(tx=TransactionType(inputs=[pinp1])),
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=1, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
            TxAck(tx=TransactionType(inputs=[pinp2])),
            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
            TxAck(tx=TransactionType(bin_outputs=[pout1])),
            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
            TxAck(tx=TransactionType(outputs=[out1])),
            signing.UiConfirmOutput(out1, coin_bitcoin),
            True,
            signing.UiConfirmTotal(300000, 90000, coin_bitcoin),
            True,
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
        ]

        seed = bip39.seed('alcohol woman abuse must during monitor noble actual mixed trade anger aisle', '')
        root = bip32.from_seed(seed, 'secp256k1')

        signer = signing.sign_tx(tx, root)
        for request, response in chunks(messages, 2):
            self.assertEqualEx(signer.send(request), response)
Ejemplo n.º 3
0
    def __init__(self, tx: SignTx, keychain: seed.Keychain,
                 coin: coininfo.CoinInfo) -> None:
        self.coin = coin
        self.tx = helpers.sanitize_sign_tx(tx, coin)
        self.keychain = keychain

        # checksum of multisig inputs, used to validate change-output
        self.multisig_fingerprint = MultisigFingerprintChecker()

        # common prefix of input paths, used to validate change-output
        self.wallet_path = WalletPathChecker()

        # set of indices of inputs which are segwit
        self.segwit = set()  # type: Set[int]

        # set of indices of inputs which are external
        self.external = set()  # type: Set[int]

        # amounts
        self.total_in = 0  # sum of input amounts
        self.external_in = 0  # sum of external input amounts
        self.total_out = 0  # sum of output amounts
        self.change_out = 0  # change output amount
        self.change_count = 0  # the number of change-outputs
        self.weight = tx_weight.TxWeightCalculator(tx.inputs_count,
                                                   tx.outputs_count)

        # transaction and signature serialization
        self.serialized_tx = writers.empty_bytearray(
            _MAX_SERIALIZED_CHUNK_SIZE)
        self.tx_req = TxRequest()
        self.tx_req.details = TxRequestDetailsType()
        self.tx_req.serialized = TxRequestSerializedType()
        self.tx_req.serialized.serialized_tx = self.serialized_tx

        # h_confirmed is used to make sure that the inputs and outputs streamed for
        # confirmation in Steps 1 and 2 are the same as the ones streamed for signing
        # legacy inputs in Step 5.
        self.h_confirmed = self.create_hash_writer()  # not a real tx hash

        # h_external is used to make sure that the signed external inputs streamed for
        # confirmation in Step 1 are the same as the ones streamed for verification
        # in Step 3.
        self.h_external = self.create_hash_writer()

        # BIP-0143 transaction hashing
        self.init_hash143()

        progress.init(self.tx.inputs_count, self.tx.outputs_count)
Ejemplo n.º 4
0
    def __init__(
        self,
        tx: SignTx,
        keychain: Keychain,
        coin: CoinInfo,
        approver: approvers.Approver,
    ) -> None:
        self.tx = helpers.sanitize_sign_tx(tx, coin)
        self.keychain = keychain
        self.coin = coin
        self.approver = approver

        # checksum of multisig inputs, used to validate change-output
        self.multisig_fingerprint = MultisigFingerprintChecker()

        # common prefix of input paths, used to validate change-output
        self.wallet_path = WalletPathChecker()

        # set of indices of inputs which are segwit
        self.segwit = set()  # type: Set[int]

        # set of indices of inputs which are external
        self.external = set()  # type: Set[int]

        # transaction and signature serialization
        self.serialized_tx = writers.empty_bytearray(
            _MAX_SERIALIZED_CHUNK_SIZE)
        self.tx_req = TxRequest()
        self.tx_req.details = TxRequestDetailsType()
        self.tx_req.serialized = TxRequestSerializedType()
        self.tx_req.serialized.serialized_tx = self.serialized_tx

        # h_approved is used to make sure that the inputs and outputs streamed for
        # approval in Steps 1 and 2 are the same as the ones streamed for signing
        # legacy inputs in Step 4.
        self.h_approved = self.create_hash_writer()  # not a real tx hash

        # h_inputs is a digest of the inputs streamed for approval in Step 1, which
        # is used to ensure that the inputs streamed for verification in Step 3 are
        # the same as those in Step 1.
        self.h_inputs = None  # type: Optional[bytes]

        # BIP-0143 transaction hashing
        self.init_hash143()

        progress.init(self.tx.inputs_count, self.tx.outputs_count)
Ejemplo n.º 5
0
    def __init__(
        self,
        tx: SignTx,
        keychain: Keychain,
        coin: CoinInfo,
        approver: approvers.Approver | None,
    ) -> None:
        self.tx_info = TxInfo(self, helpers.sanitize_sign_tx(tx, coin))
        self.keychain = keychain
        self.coin = coin

        if approver is not None:
            self.approver = approver
        else:
            self.approver = approvers.BasicApprover(tx, coin)

        # set of indices of inputs which are segwit
        self.segwit: set[int] = set()

        # set of indices of inputs which are external
        self.external: set[int] = set()

        # transaction and signature serialization
        self.serialized_tx = writers.empty_bytearray(
            _MAX_SERIALIZED_CHUNK_SIZE)
        self.tx_req = TxRequest()
        self.tx_req.details = TxRequestDetailsType()
        self.tx_req.serialized = TxRequestSerializedType()
        self.tx_req.serialized.serialized_tx = self.serialized_tx

        # List of original transactions which are being replaced by the current transaction.
        # Note: A List is better than a Dict of TXID -> OriginalTxInfo. Dict ordering is
        # undefined so we would need to convert to a sorted list in several places to ensure
        # stable device tests.
        self.orig_txs: list[OriginalTxInfo] = []

        # h_inputs is a digest of the inputs streamed for approval in Step 1, which
        # is used to ensure that the inputs streamed for verification in Step 3 are
        # the same as those in Step 1.
        self.h_inputs: bytes | None = None

        progress.init(tx.inputs_count, tx.outputs_count)
Ejemplo n.º 6
0
    def test_send_native_p2wpkh(self):

        coin = coins.by_name('Groestlcoin Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInput(
            # 84'/1'/0'/0/0" - tgrs1qkvwu9g3k2pdxewfqr7syz89r3gj557l3ued7ja
            address_n=[84 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 0, 0],
            amount=12300000,
            prev_hash=unhexlify(
                '4f2f857f39ed1afe05542d058fb0be865a387446e32fc876d086203f483f61d1'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDWITNESS,
            sequence=0xfffffffe,
            multisig=None,
        )
        ptx1 = PrevTx(version=1,
                      lock_time=650645,
                      inputs_count=1,
                      outputs_count=2,
                      extra_data_len=0)
        pinp1 = PrevInput(
            script_sig=unhexlify(
                '483045022100d9615361c044e91f6dd7bb4455f3ad686cd5a663d7800bb74c448b2706500ccb022026bed24b81a501e8398411c5a9a793741d9bfe39617d51c363dde0a84f44f4f9012102659a6eefcc72d6f2eff92e57095388b17db0b06034946ecd44120e5e7a830ff4'
            ),
            prev_hash=unhexlify(
                '1c92508b38239e5c10b23fb46dcf765ee2f3a95b835edbf0943ec21b21711160'
            ),
            prev_index=1,
            sequence=4294967293)
        pout1 = PrevOutput(script_pubkey=unhexlify(
            '0014b31dc2a236505a6cb9201fa0411ca38a254a7bf1'),
                           amount=12300000)
        pout2 = PrevOutput(script_pubkey=unhexlify(
            '76a91438cc090e4a4b2e458c33fe35af1c5c0094699ac288ac'),
                           amount=9887699777)

        out1 = TxOutput(
            address='2N4Q5FhU2497BryFfUgbqkAJE87aKDv3V3e',
            amount=5000000,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=[],
            multisig=None,
        )
        out2 = TxOutput(
            address='tgrs1qejqxwzfld7zr6mf7ygqy5s5se5xq7vmt9lkd57',
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=12300000 - 11000 - 5000000,
            address_n=[],
            multisig=None,
        )
        tx = SignTx(coin_name='Groestlcoin Testnet',
                    version=1,
                    lock_time=650713,
                    inputs_count=1,
                    outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
            helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN),
            True,
            helpers.UiConfirmNonDefaultLocktime(tx.lock_time,
                                                lock_time_disabled=False),
            True,
            helpers.UiConfirmTotal(12300000, 11000, coin, AmountUnit.BITCOIN),
            True,

            # check prev tx
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXMETA,
                      details=TxRequestDetailsType(request_index=None,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevMeta(tx=ptx1),
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout2)),

            # sign tx
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized header
                    serialized_tx=unhexlify('01000000000101'), )),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized inp1
                    serialized_tx=unhexlify(
                        'd1613f483f2086d076c82fe34674385a86beb08f052d5405fe1aed397f852f4f0000000000feffffff02'
                    ), )),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=1, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out1
                    serialized_tx=unhexlify(
                        '404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),

            # segwit
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out2
                    serialized_tx=unhexlify(
                        'a8386f0000000000160014cc8067093f6f843d6d3e22004a4290cd0c0f336b'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXFINISHED,
                details=TxRequestDetailsType(),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify(
                        '02483045022100ea8780bc1e60e14e945a80654a41748bbf1aa7d6f2e40a88d91dfc2de1f34bd10220181a474a3420444bd188501d8d270736e1e9fe379da9970de992ff445b0972e3012103adc58245cf28406af0ef5cc24b8afba7f1be6c72f279b642d85c48798685f862d9ed0900'
                    ),
                    signature_index=0,
                    signature=unhexlify(
                        '3045022100ea8780bc1e60e14e945a80654a41748bbf1aa7d6f2e40a88d91dfc2de1f34bd10220181a474a3420444bd188501d8d270736e1e9fe379da9970de992ff445b0972e3'
                    ),
                )),
        ]

        ns = get_schemas_for_coin(coin)
        keychain = Keychain(seed, coin.curve_name, ns)
        approver = BasicApprover(tx, coin)
        signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, approver).signer()
        for request, expected_response in chunks(messages, 2):
            response = signer.send(request)
            if isinstance(response, tuple):
                _, response = response
            self.assertEqual(response, expected_response)
        with self.assertRaises(StopIteration):
            signer.send(None)
    def test_send_p2wpkh_in_p2sh(self):

        coin = coins.by_name('Groestlcoin Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInput(
            # 49'/1'/0'/1/0" - 2N1LGaGg836mqSQqiuUBLfcyGBhyZYBtBZ7
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            amount=123456789,
            prev_hash=unhexlify(
                '09a48bce2f9d5c6e4f0cb9ea1b32d0891855e8acfe5334f9ebd72b9ad2de60cf'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDP2SHWITNESS,
            sequence=0xfffffffe,
            multisig=None,
        )
        ptx1 = PrevTx(version=1,
                      lock_time=650749,
                      inputs_count=1,
                      outputs_count=2,
                      extra_data_len=0)
        pinp1 = PrevInput(
            script_sig=unhexlify(
                '47304402201f8f57f708144c3a11da322546cb37bd385aa825d940c37e8016f0efd6ec3e9402202a41bc02c29e4f3f13efd4bededbcd4308a6393279111d614ee1f7635cf3e65701210371546a36bdf6bc82087301b3f6e759736dc8790150673d2e7e2715d2ad72f3a4'
            ),
            prev_hash=unhexlify(
                '4f2f857f39ed1afe05542d058fb0be865a387446e32fc876d086203f483f61d1'
            ),
            prev_index=1,
            sequence=4294967294)
        pout1 = PrevOutput(script_pubkey=unhexlify(
            'a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'),
                           amount=123456789)
        pout2 = PrevOutput(script_pubkey=unhexlify(
            '76a91435528b20e9a793cf2c3a1cf9cff1f2127ad377da88ac'),
                           amount=9764242764)

        out1 = TxOutput(
            address='mvbu1Gdy8SUjTenqerxUaZyYjmvedc787y',
            amount=12300000,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=[],
            multisig=None,
        )
        out2 = TxOutput(
            address='2N1LGaGg836mqSQqiuUBLfcyGBhyZYBtBZ7',
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=123456789 - 11000 - 12300000,
            address_n=[],
            multisig=None,
        )
        tx = SignTx(coin_name='Groestlcoin Testnet',
                    version=1,
                    lock_time=650756,
                    inputs_count=1,
                    outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            helpers.UiConfirmOutput(out1, coin),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
            helpers.UiConfirmOutput(out2, coin),
            True,
            helpers.UiConfirmNonDefaultLocktime(tx.lock_time,
                                                lock_time_disabled=False),
            True,
            helpers.UiConfirmTotal(123445789 + 11000, 11000, coin),
            True,

            # check prev tx
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXMETA,
                      details=TxRequestDetailsType(request_index=None,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevMeta(tx=ptx1),
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout2)),

            # sign tx
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized header
                    serialized_tx=unhexlify('01000000000101'), )),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized inp1
                    serialized_tx=unhexlify(
                        'cf60ded29a2bd7ebf93453feace8551889d0321beab90c4f6e5c9d2fce8ba4090000000017160014d16b8c0680c61fc6ed2e407455715055e41052f5feffffff02'
                    ), )),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=1, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out1
                    serialized_tx=unhexlify(
                        'e0aebb00000000001976a914a579388225827d9f2fe9014add644487808c695d88ac'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),

            # segwit
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out2
                    serialized_tx=unhexlify(
                        '3df39f060000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXFINISHED,
                details=TxRequestDetailsType(),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify(
                        '02483045022100b7ce2972bcbc3a661fe320ba901e680913b2753fcb47055c9c6ba632fc4acf81022001c3cfd6c2fe92eb60f5176ce0f43707114dd7223da19c56f2df89c13c2fef80012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7904ee0900'
                    ),
                    signature_index=0,
                    signature=unhexlify(
                        '3045022100b7ce2972bcbc3a661fe320ba901e680913b2753fcb47055c9c6ba632fc4acf81022001c3cfd6c2fe92eb60f5176ce0f43707114dd7223da19c56f2df89c13c2fef80'
                    ),
                )),
        ]

        ns = get_namespaces_for_coin(coin)
        keychain = Keychain(seed, coin.curve_name, ns)
        approver = BasicApprover(tx, coin)
        signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, approver).signer()
        for request, expected_response in chunks(messages, 2):
            response = signer.send(request)
            if isinstance(response, tuple):
                _, response = response
            self.assertEqual(response, expected_response)
        with self.assertRaises(StopIteration):
            signer.send(None)
Ejemplo n.º 8
0
async def sign_tx(tx: SignTx, root: bip32.HDNode):
    tx = sanitize_sign_tx(tx)

    progress.init(tx.inputs_count, tx.outputs_count)

    # Phase 1

    h_first, bip143, segwit, authorized_in, wallet_path = await check_tx_fee(
        tx, root)

    # Phase 2
    # - sign inputs
    # - check that nothing changed

    coin = coins.by_name(tx.coin_name)
    tx_ser = TxRequestSerializedType()

    txo_bin = TxOutputBinType()
    tx_req = TxRequest()
    tx_req.details = TxRequestDetailsType()
    tx_req.serialized = None

    for i_sign in range(tx.inputs_count):
        progress.advance()
        txi_sign = None
        key_sign = None
        key_sign_pub = None

        if segwit[i_sign]:
            # STAGE_REQUEST_SEGWIT_INPUT
            txi_sign = await request_tx_input(tx_req, i_sign)

            is_segwit = (txi_sign.script_type == InputScriptType.SPENDWITNESS
                         or txi_sign.script_type
                         == InputScriptType.SPENDP2SHWITNESS)
            if not is_segwit:
                raise SigningError(FailureType.ProcessError,
                                   'Transaction has changed during signing')
            input_check_wallet_path(txi_sign, wallet_path)

            key_sign = node_derive(root, txi_sign.address_n)
            key_sign_pub = key_sign.public_key()
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub)

            w_txi = bytearray_with_cap(7 + len(txi_sign.prev_hash) + 4 +
                                       len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi, get_tx_header(tx, True))
            write_tx_input(w_txi, txi_sign)
            tx_ser.serialized_tx = w_txi
            tx_req.serialized = tx_ser

        elif coin.force_bip143:
            # STAGE_REQUEST_SEGWIT_INPUT
            txi_sign = await request_tx_input(tx_req, i_sign)
            input_check_wallet_path(txi_sign, wallet_path)

            is_bip143 = (txi_sign.script_type == InputScriptType.SPENDADDRESS
                         or txi_sign.script_type
                         == InputScriptType.SPENDMULTISIG)
            if not is_bip143 or txi_sign.amount > authorized_in:
                raise SigningError(FailureType.ProcessError,
                                   'Transaction has changed during signing')
            authorized_in -= txi_sign.amount

            key_sign = node_derive(root, txi_sign.address_n)
            key_sign_pub = key_sign.public_key()
            bip143_hash = bip143.preimage_hash(tx, txi_sign,
                                               ecdsa_hash_pubkey(key_sign_pub),
                                               get_hash_type(coin))

            # if multisig, check if singing with a key that is included in multisig
            if txi_sign.multisig:
                multisig_pubkey_index(txi_sign.multisig, key_sign_pub)

            signature = ecdsa_sign(key_sign, bip143_hash)
            tx_ser.signature_index = i_sign
            tx_ser.signature = signature

            # serialize input with correct signature
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub, signature)
            w_txi_sign = bytearray_with_cap(5 + len(txi_sign.prev_hash) + 4 +
                                            len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi_sign, get_tx_header(tx))
            write_tx_input(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign

            tx_req.serialized = tx_ser

        else:
            # hash of what we are signing with this input
            h_sign = HashWriter(sha256)
            # same as h_first, checked before signing the digest
            h_second = HashWriter(sha256)

            write_uint32(h_sign, tx.version)
            write_varint(h_sign, tx.inputs_count)

            for i in range(tx.inputs_count):
                # STAGE_REQUEST_4_INPUT
                txi = await request_tx_input(tx_req, i)
                input_check_wallet_path(txi, wallet_path)
                write_tx_input_check(h_second, txi)
                if i == i_sign:
                    txi_sign = txi
                    key_sign = node_derive(root, txi.address_n)
                    key_sign_pub = key_sign.public_key()
                    # for the signing process the script_sig is equal
                    # to the previous tx's scriptPubKey (P2PKH) or a redeem script (P2SH)
                    if txi_sign.script_type == InputScriptType.SPENDMULTISIG:
                        txi_sign.script_sig = output_script_multisig(
                            multisig_get_pubkeys(txi_sign.multisig),
                            txi_sign.multisig.m)
                    elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
                        txi_sign.script_sig = output_script_p2pkh(
                            ecdsa_hash_pubkey(key_sign_pub))
                    else:
                        raise SigningError(FailureType.ProcessError,
                                           'Unknown transaction type')
                else:
                    txi.script_sig = bytes()
                write_tx_input(h_sign, txi)

            write_varint(h_sign, tx.outputs_count)

            for o in range(tx.outputs_count):
                # STAGE_REQUEST_4_OUTPUT
                txo = await request_tx_output(tx_req, o)
                txo_bin.amount = txo.amount
                txo_bin.script_pubkey = output_derive_script(txo, coin, root)
                write_tx_output(h_second, txo_bin)
                write_tx_output(h_sign, txo_bin)

            write_uint32(h_sign, tx.lock_time)

            write_uint32(h_sign, get_hash_type(coin))

            # check the control digests
            if get_tx_hash(h_first, False) != get_tx_hash(h_second, False):
                raise SigningError(FailureType.ProcessError,
                                   'Transaction has changed during signing')

            # if multisig, check if singing with a key that is included in multisig
            if txi_sign.multisig:
                multisig_pubkey_index(txi_sign.multisig, key_sign_pub)

            # compute the signature from the tx digest
            signature = ecdsa_sign(key_sign, get_tx_hash(h_sign, True))
            tx_ser.signature_index = i_sign
            tx_ser.signature = signature

            # serialize input with correct signature
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub, signature)
            w_txi_sign = bytearray_with_cap(5 + len(txi_sign.prev_hash) + 4 +
                                            len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi_sign, get_tx_header(tx))
            write_tx_input(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign

            tx_req.serialized = tx_ser

    for o in range(tx.outputs_count):
        progress.advance()
        # STAGE_REQUEST_5_OUTPUT
        txo = await request_tx_output(tx_req, o)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, root)

        # serialize output
        w_txo_bin = bytearray_with_cap(5 + 8 + 5 + len(txo_bin.script_pubkey) +
                                       4)
        if o == 0:  # serializing first output => prepend outputs count
            write_varint(w_txo_bin, tx.outputs_count)
        write_tx_output(w_txo_bin, txo_bin)

        tx_ser.signature_index = None
        tx_ser.signature = None
        tx_ser.serialized_tx = w_txo_bin

        tx_req.serialized = tx_ser

    any_segwit = True in segwit.values()

    for i in range(tx.inputs_count):
        progress.advance()
        if segwit[i]:
            # STAGE_REQUEST_SEGWIT_WITNESS
            txi = await request_tx_input(tx_req, i)
            input_check_wallet_path(txi, wallet_path)

            is_segwit = (txi.script_type == InputScriptType.SPENDWITNESS or
                         txi.script_type == InputScriptType.SPENDP2SHWITNESS)
            if not is_segwit or txi.amount > authorized_in:
                raise SigningError(FailureType.ProcessError,
                                   'Transaction has changed during signing')
            authorized_in -= txi.amount

            key_sign = node_derive(root, txi.address_n)
            key_sign_pub = key_sign.public_key()
            bip143_hash = bip143.preimage_hash(tx, txi,
                                               ecdsa_hash_pubkey(key_sign_pub),
                                               get_hash_type(coin))

            signature = ecdsa_sign(key_sign, bip143_hash)
            if txi.multisig:
                # find out place of our signature based on the pubkey
                signature_index = multisig_pubkey_index(
                    txi.multisig, key_sign_pub)
                witness = witness_p2wsh(txi.multisig, signature,
                                        signature_index, get_hash_type(coin))
            else:
                witness = witness_p2wpkh(signature, key_sign_pub,
                                         get_hash_type(coin))

            tx_ser.serialized_tx = witness
            tx_ser.signature_index = i
            tx_ser.signature = signature
        elif any_segwit:
            tx_ser.serialized_tx = bytearray(
                1)  # empty witness for non-segwit inputs
            tx_ser.signature_index = None
            tx_ser.signature = None

        tx_req.serialized = tx_ser

    write_uint32(tx_ser.serialized_tx, tx.lock_time)

    await request_tx_finish(tx_req)
Ejemplo n.º 9
0
    def test_send_p2wpkh_in_p2sh(self):

        coin = coins.by_name('Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInput(
            # 49'/1'/0'/1/0" - 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            amount=123456789,
            prev_hash=unhexlify(
                '20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDP2SHWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        ptx1 = PrevTx(version=1,
                      lock_time=0,
                      inputs_count=1,
                      outputs_count=2,
                      extra_data_len=0)
        pinp1 = PrevInput(
            script_sig=unhexlify(
                '4730440220548e087d0426b20b8a571b03b9e05829f7558b80c53c12143e342f56ab29e51d02205b68cb7fb223981d4c999725ac1485a982c4259c4f50b8280f137878c232998a012102794a25b254a268e59a5869da57fbae2fadc6727cb3309321dab409b12b2fa17c'
            ),
            prev_hash=unhexlify(
                '802cabf0843b945eabe136d7fc7c89f41021658abf56cba000acbce88c41143a'
            ),
            prev_index=0,
            sequence=4294967295)
        pout1 = PrevOutput(script_pubkey=unhexlify(
            'a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'),
                           amount=123456789)
        pout2 = PrevOutput(script_pubkey=unhexlify(
            '76a914b84bacdcd8f4cc59274a5bfb73f804ca10f7fd1488ac'),
                           amount=865519308)

        out1 = TxOutput(
            address='mhRx1CeVfaayqRwq5zgRQmD7W5aWBfD5mC',
            amount=12300000,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=[],
            multisig=None,
        )
        out2 = TxOutput(
            address='2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX',
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=123456789 - 11000 - 12300000,
            address_n=[],
            multisig=None,
        )
        tx = SignTx(coin_name='Testnet',
                    version=1,
                    lock_time=0,
                    inputs_count=1,
                    outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            helpers.UiConfirmOutput(out1, coin),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
            helpers.UiConfirmOutput(out2, coin),
            True,
            helpers.UiConfirmTotal(123445789 + 11000, 11000, coin),
            True,

            # check prev tx
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXMETA,
                      details=TxRequestDetailsType(request_index=None,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevMeta(tx=ptx1),
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout2)),

            # sign tx
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized header
                    serialized_tx=unhexlify('01000000000101'), )),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized inp1
                    serialized_tx=unhexlify(
                        '37c361fb8f2d9056ba8c98c5611930fcb48cacfdd0fe2e0449d83eea982f91200000000017160014d16b8c0680c61fc6ed2e407455715055e41052f5ffffffff02'
                    ), )),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=1, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out1
                    serialized_tx=unhexlify(
                        'e0aebb00000000001976a91414fdede0ddc3be652a0ce1afbc1b509a55b6b94888ac'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),

            # segwit
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out2
                    serialized_tx=unhexlify(
                        '3df39f060000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXFINISHED,
                details=TxRequestDetailsType(),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify(
                        '02483045022100ccd253bfdf8a5593cd7b6701370c531199f0f05a418cd547dfc7da3f21515f0f02203fa08a0753688871c220648f9edadbdb98af42e5d8269364a326572cf703895b012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7900000000'
                    ),
                    signature_index=0,
                    signature=unhexlify(
                        '3045022100ccd253bfdf8a5593cd7b6701370c531199f0f05a418cd547dfc7da3f21515f0f02203fa08a0753688871c220648f9edadbdb98af42e5d8269364a326572cf703895b'
                    ),
                )),
        ]

        ns = get_namespaces_for_coin(coin)
        keychain = Keychain(seed, coin.curve_name, ns)
        approver = BasicApprover(tx, coin)
        signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer()
        for request, expected_response in chunks(messages, 2):
            response = signer.send(request)
            if isinstance(response, tuple):
                _, response = response
            self.assertEqual(response, expected_response)
        with self.assertRaises(StopIteration):
            signer.send(None)
Ejemplo n.º 10
0
    def test_one_one_fee(self):
        # tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882
        # input 0: 0.0039 BTC

        coin_bitcoin = coins.by_name('Bitcoin')

        ptx1 = TransactionType(version=1,
                               lock_time=0,
                               inputs_cnt=2,
                               outputs_cnt=1,
                               extra_data_len=0)
        pinp1 = TxInputType(
            script_sig=unhexlify(
                '483045022072ba61305fe7cb542d142b8f3299a7b10f9ea61f6ffaab5dca8142601869d53c0221009a8027ed79eb3b9bc13577ac2853269323434558528c6b6a7e542be46e7e9a820141047a2d177c0f3626fc68c53610b0270fa6156181f46586c679ba6a88b34c6f4874686390b4d92e5769fbb89c8050b984f4ec0b257a0e5c4ff8bd3b035a51709503'
            ),
            prev_hash=unhexlify(
                'c16a03f1cf8f99f6b5297ab614586cacec784c2d259af245909dedb0e39eddcf'
            ),
            prev_index=1,
            script_type=None,
            sequence=None)
        pinp2 = TxInputType(
            script_sig=unhexlify(
                '48304502200fd63adc8f6cb34359dc6cca9e5458d7ea50376cbd0a74514880735e6d1b8a4c0221008b6ead7fe5fbdab7319d6dfede3a0bc8e2a7c5b5a9301636d1de4aa31a3ee9b101410486ad608470d796236b003635718dfc07c0cac0cfc3bfc3079e4f491b0426f0676e6643a39198e8e7bdaffb94f4b49ea21baa107ec2e237368872836073668214'
            ),
            prev_hash=unhexlify(
                '1ae39a2f8d59670c8fc61179148a8e61e039d0d9e8ab08610cb69b4a19453eaf'
            ),
            prev_index=1,
            script_type=None,
            sequence=None)
        pout1 = TxOutputBinType(script_pubkey=unhexlify(
            '76a91424a56db43cf6f2b02e838ea493f95d8d6047423188ac'),
                                amount=390000)

        inp1 = TxInputType(
            address_n=[44 | 0x80000000, 0 | 0x80000000, 0 | 0x80000000, 0, 0],
            # amount=390000,
            prev_hash=unhexlify(
                'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
            ),
            prev_index=0,
            amount=None,
            script_type=None,
            multisig=None,
            sequence=None)
        out1 = TxOutputType(address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
                            amount=390000 - 10000,
                            script_type=OutputScriptType.PAYTOADDRESS,
                            multisig=None)
        tx = SignTx(coin_name=None,
                    version=None,
                    lock_time=None,
                    inputs_count=1,
                    outputs_count=1)

        messages = [
            None,
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(
                request_type=TXMETA,
                details=TxRequestDetailsType(
                    request_index=None,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=EMPTY_SERIALIZED),
            TxAck(tx=ptx1),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(inputs=[pinp1])),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=1,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(inputs=[pinp2])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(bin_outputs=[pout1])),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(outputs=[out1])),
            helpers.UiConfirmOutput(out1, coin_bitcoin),
            True,
            helpers.UiConfirmTotal(380000 + 10000, 10000, coin_bitcoin),
            True,
            # ButtonRequest(code=ButtonRequest_ConfirmOutput),
            # ButtonRequest(code=ButtonRequest_SignTx),
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=TxRequestSerializedType(
                          serialized_tx=unhexlify('0100000001'))),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(outputs=[out1])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    signature_index=0,
                    signature=unhexlify(
                        '30440220198146fa987da8d78c4c7a471614fceb54d161ede244412f3369f436a7aec386022066bbede7644baa38abbdb4b1f3037f8db225c04e107099b625339a55614c3db3'
                    ),
                    serialized_tx=unhexlify(
                        '82488650ef25a58fef6788bd71b8212038d7f2bbe4750bc7bcb44701e85ef6d5000000006a4730440220198146fa987da8d78c4c7a471614fceb54d161ede244412f3369f436a7aec386022066bbede7644baa38abbdb4b1f3037f8db225c04e107099b625339a55614c3db30121027a4cebff51c97c047637cda66838e8b64421a4af6bf8ef3c99717f92d09b3c1dffffffff01'
                    ))),
            TxAck(tx=TransactionType(outputs=[out1])),
            TxRequest(
                request_type=TXFINISHED,
                details=TxRequestDetailsType(),
                serialized=TxRequestSerializedType(
                    signature_index=None,
                    signature=None,
                    serialized_tx=unhexlify(
                        '60cc0500000000001976a914de9b2a8da088824e8fe51debea566617d851537888ac00000000'
                    ),
                )),
        ]

        seed = bip39.seed(
            'alcohol woman abuse must during monitor noble actual mixed trade anger aisle',
            '')
        ns = get_namespaces_for_coin(coin_bitcoin)
        keychain = Keychain(seed, ns)
        signer = bitcoin.Bitcoin(tx, keychain, coin_bitcoin).signer()

        for request, response in chunks(messages, 2):
            res = signer.send(request)
            self.assertEqual(res, response)

        with self.assertRaises(StopIteration):
            signer.send(None)
    def test_one_one_fee(self):
        # http://groestlsight.groestlcoin.org/tx/f56521b17b828897f72b30dd21b0192fd942342e89acbb06abf1d446282c30f5
        # ptx1: http://groestlsight.groestlcoin.org/api/tx/cb74c8478c5814742c87cffdb4a21231869888f8042fb07a90e015a9db1f9d4a

        coin = coins.by_name('Groestlcoin')

        ptx1 = PrevTx(version=1, lock_time=2160993, inputs_count=1, outputs_count=1, extra_data_len=0)
        pinp1 = PrevInput(script_sig=unhexlify('48304502210096a287593b1212a188e778596eb8ecd4cc169b93a4d115226460d8e3deae431c02206c78ec09b3df977f04a6df5eb53181165c4ea5a0b35f826551349130f879d6b8012102cf5126ff54e38a80a919579d7091cafe24840eab1d30fe2b4d59bdd9d267cad8'),
                            prev_hash=unhexlify('7dc74a738c50c2ae1228ce9890841e5355fd6d7f2c1367e0a74403ab60db3224'),
                            prev_index=0,
                            sequence=4294967294)
        pout1 = PrevOutput(script_pubkey=unhexlify('76a914172b4e06e9b7881a48d2ee8062b495d0b2517fe888ac'),
                                amount=210016)

        inp1 = TxInput(address_n=[44 | 0x80000000, 17 | 0x80000000, 0 | 0x80000000, 0, 2],  #  FXHDsC5ZqWQHkDmShzgRVZ1MatpWhwxTAA
                           prev_hash=unhexlify('cb74c8478c5814742c87cffdb4a21231869888f8042fb07a90e015a9db1f9d4a'),
                           prev_index=0,
                           amount=210016)
        out1 = TxOutput(address='FtM4zAn9aVYgHgxmamWBgWPyZsb6RhvkA9',
                            amount=210016 - 192,
                            script_type=OutputScriptType.PAYTOADDRESS,
                            address_n=[])
        tx = SignTx(coin_name='Groestlcoin', version=1, lock_time=0, inputs_count=1, outputs_count=1)

        messages = [
            None,
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            helpers.UiConfirmOutput(out1, coin),
            True,
            helpers.UiConfirmTotal(210016, 192, coin),
            True,
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXMETA, details=TxRequestDetailsType(request_index=None, tx_hash=unhexlify('cb74c8478c5814742c87cffdb4a21231869888f8042fb07a90e015a9db1f9d4a')), serialized=EMPTY_SERIALIZED),
            TxAckPrevMeta(tx=ptx1),
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('cb74c8478c5814742c87cffdb4a21231869888f8042fb07a90e015a9db1f9d4a')), serialized=EMPTY_SERIALIZED),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp1)),
            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('cb74c8478c5814742c87cffdb4a21231869888f8042fb07a90e015a9db1f9d4a')), serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
            # ButtonRequest(code=ButtonRequest_ConfirmOutput),
            # ButtonRequest(code=ButtonRequest_SignTx),
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=TxRequestSerializedType(
                signature_index=None,
                signature=None,
                serialized_tx=unhexlify('0100000001'))),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=TxRequestSerializedType(
                signature_index=0,
                signature=unhexlify('304402201fb96d20d0778f54520ab59afe70d5fb20e500ecc9f02281cf57934e8029e8e10220383d5a3e80f2e1eb92765b6da0f23d454aecbd8236f083d483e9a74302368761'),
                serialized_tx=unhexlify('4a9d1fdba915e0907ab02f04f88898863112a2b4fdcf872c7414588c47c874cb000000006a47304402201fb96d20d0778f54520ab59afe70d5fb20e500ecc9f02281cf57934e8029e8e10220383d5a3e80f2e1eb92765b6da0f23d454aecbd8236f083d483e9a7430236876101210331693756f749180aeed0a65a0fab0625a2250bd9abca502282a4cf0723152e67ffffffff01'))),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            TxRequest(request_type=TXFINISHED, details=TxRequestDetailsType(), serialized=TxRequestSerializedType(
                signature_index=None,
                signature=None,
                serialized_tx=unhexlify('a0330300000000001976a914fe40329c95c5598ac60752a5310b320cb52d18e688ac00000000'),
            )),
        ]

        seed = bip39.seed(' '.join(['all'] * 12), '')
        ns = get_schemas_for_coin(coin)
        keychain = Keychain(seed, coin.curve_name, ns)
        approver = BasicApprover(tx, coin)
        signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, approver).signer()
        for request, expected_response in chunks(messages, 2):
            response = signer.send(request)
            if isinstance(response, tuple):
                _, response = response
            self.assertEqual(response, expected_response)
        with self.assertRaises(StopIteration):
            signer.send(None)
Ejemplo n.º 12
0
    def test_send_native_p2wpkh(self):

        coin = coins.by_name('Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInput(
            # 49'/1'/0'/0/0" - tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 0, 0],
            amount=12300000,
            prev_hash=unhexlify(
                '09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        ptx1 = PrevTx(version=1,
                      lock_time=0,
                      inputs_count=1,
                      outputs_count=2,
                      extra_data_len=0)
        pinp1 = PrevInput(
            script_sig=unhexlify(
                '160014d16b8c0680c61fc6ed2e407455715055e41052f5'),
            prev_hash=unhexlify(
                '20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'
            ),
            prev_index=0,
            sequence=4294967295)
        pout1 = PrevOutput(script_pubkey=unhexlify(
            '00140099a7ecbd938ed1839f5f6bf6d50933c6db9d5c'),
                           amount=12300000)
        pout2 = PrevOutput(script_pubkey=unhexlify(
            'a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'),
                           amount=111145789)

        out1 = TxOutput(
            address='2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp',
            amount=5000000,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=[],
            multisig=None,
        )
        out2 = TxOutput(
            address='tb1q694ccp5qcc0udmfwgp692u2s2hjpq5h407urtu',
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=12300000 - 11000 - 5000000,
            address_n=[],
            multisig=None,
        )
        tx = SignTx(coin_name='Testnet',
                    version=1,
                    lock_time=0,
                    inputs_count=1,
                    outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            helpers.UiConfirmForeignAddress(address_n=inp1.address_n),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            helpers.UiConfirmOutput(out1, coin),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
            helpers.UiConfirmOutput(out2, coin),
            True,
            helpers.UiConfirmTotal(12300000, 11000, coin),
            True,

            # check prev tx
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXMETA,
                      details=TxRequestDetailsType(request_index=None,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevMeta(tx=ptx1),
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout2)),

            # sign tx
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized header
                    serialized_tx=unhexlify('01000000000101'), )),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized inp1
                    serialized_tx=unhexlify(
                        '8a44999c07bba32df1cacdc50987944e68e3205b4429438fdde35c76024614090000000000ffffffff02'
                    ), )),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=1, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out1
                    serialized_tx=unhexlify(
                        '404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),

            # segwit
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out2
                    serialized_tx=unhexlify(
                        'a8386f0000000000160014d16b8c0680c61fc6ed2e407455715055e41052f5'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXFINISHED,
                details=TxRequestDetailsType(),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify(
                        '02483045022100a7ca8f097525f9044e64376dc0a0f5d4aeb8d15d66808ba97979a0475b06b66502200597c8ebcef63e047f9aeef1a8001d3560470cf896c12f6990eec4faec599b950121033add1f0e8e3c3136f7428dd4a4de1057380bd311f5b0856e2269170b4ffa65bf00000000'
                    ),
                    signature_index=0,
                    signature=unhexlify(
                        '3045022100a7ca8f097525f9044e64376dc0a0f5d4aeb8d15d66808ba97979a0475b06b66502200597c8ebcef63e047f9aeef1a8001d3560470cf896c12f6990eec4faec599b95'
                    ),
                )),
        ]

        ns = get_schemas_for_coin(coin)
        keychain = Keychain(seed, coin.curve_name, ns)
        approver = BasicApprover(tx, coin)
        signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer()
        for request, response in chunks(messages, 2):
            res = signer.send(request)
            if isinstance(res, tuple):
                _, res = res
            self.assertEqual(res, response)
        with self.assertRaises(StopIteration):
            signer.send(None)
    def test_send_p2wpkh_in_p2sh_attack_amount(self):

        coin = coins.by_name('Testnet')

        seed = bip39.seed(' '.join(['all'] * 12), '')
        root = bip32.from_seed(seed, 'secp256k1')

        inp1 = TxInputType(
            # 49'/1'/0'/1/0" - 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            amount=10,
            prev_hash=unhexlify(
                '20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDP2SHWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        inpattack = TxInputType(
            # 49'/1'/0'/1/0" - 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            amount=9,  # modified!
            prev_hash=unhexlify(
                '20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDP2SHWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        out1 = TxOutputType(
            address='mhRx1CeVfaayqRwq5zgRQmD7W5aWBfD5mC',
            amount=8,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=None,
        )
        out2 = TxOutputType(
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            script_type=OutputScriptType.PAYTOP2SHWITNESS,
            amount=1,
            address=None,
        )
        tx = SignTx(coin_name='Testnet',
                    version=None,
                    lock_time=None,
                    inputs_count=1,
                    outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None)),
            TxAck(tx=TransactionType(inputs=[inpattack])),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=None),
            TxAck(tx=TransactionType(outputs=[out1])),
            signing.UiConfirmOutput(out1, coin),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=None),
                      serialized=None),
            TxAck(tx=TransactionType(outputs=[out2])),
            signing.UiConfirmTotal(8, 0, coin),
            True,
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=None),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized inpattack
                    serialized_tx=unhexlify(
                        '0100000000010137c361fb8f2d9056ba8c98c5611930fcb48cacfdd0fe2e0449d83eea982f91200000000017160014d16b8c0680c61fc6ed2e407455715055e41052f5ffffffff'
                    ), )),
            # the out has to be cloned not to send the same object which was modified
            TxAck(tx=TransactionType(outputs=[TxOutputType(**out1.__dict__)])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=1, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out1
                    serialized_tx=unhexlify(
                        '0208000000000000001976a91414fdede0ddc3be652a0ce1afbc1b509a55b6b94888ac'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAck(tx=TransactionType(outputs=[TxOutputType(**out2.__dict__)])),

            # segwit
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out2
                    serialized_tx=unhexlify(
                        '010000000000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(request_type=TXFINISHED, details=None)
        ]

        signer = signing.sign_tx(tx, root)
        i = 0
        messages_count = int(len(messages) / 2)
        for request, response in chunks(messages, 2):
            if i == messages_count - 1:  # last message should throw SigningError
                self.assertRaises(signing.SigningError, signer.send, request)
            else:
                self.assertEqualEx(signer.send(request), response)
            i += 1
        with self.assertRaises(StopIteration):
            signer.send(None)
    def test_send_p2wpkh_in_p2sh(self):

        coin = coins.by_name('Testnet')

        seed = bip39.seed(' '.join(['all'] * 12), '')
        root = bip32.from_seed(seed, 'secp256k1')

        inp1 = TxInputType(
            # 49'/1'/0'/1/0" - 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            amount=123456789,
            prev_hash=unhexlify(
                '20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDP2SHWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        out1 = TxOutputType(
            address='mhRx1CeVfaayqRwq5zgRQmD7W5aWBfD5mC',
            amount=12300000,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=None,
        )
        out2 = TxOutputType(
            address='2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX',
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=123456789 - 11000 - 12300000,
            address_n=None,
        )
        tx = SignTx(coin_name='Testnet',
                    version=None,
                    lock_time=None,
                    inputs_count=1,
                    outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None)),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=None),
            TxAck(tx=TransactionType(outputs=[out1])),
            signing.UiConfirmOutput(out1, coin),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=None),
                      serialized=None),
            TxAck(tx=TransactionType(outputs=[out2])),
            signing.UiConfirmOutput(out2, coin),
            True,
            signing.UiConfirmTotal(123445789, 11000, coin),
            True,

            # sign tx
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=None),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized inp1
                    serialized_tx=unhexlify(
                        '0100000000010137c361fb8f2d9056ba8c98c5611930fcb48cacfdd0fe2e0449d83eea982f91200000000017160014d16b8c0680c61fc6ed2e407455715055e41052f5ffffffff'
                    ), )),
            TxAck(tx=TransactionType(outputs=[out1])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=1, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out1
                    serialized_tx=unhexlify(
                        '02e0aebb00000000001976a91414fdede0ddc3be652a0ce1afbc1b509a55b6b94888ac'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAck(tx=TransactionType(outputs=[out2])),

            # segwit
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out2
                    serialized_tx=unhexlify(
                        '3df39f060000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(
                request_type=TXFINISHED,
                details=None,
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify(
                        '02483045022100ccd253bfdf8a5593cd7b6701370c531199f0f05a418cd547dfc7da3f21515f0f02203fa08a0753688871c220648f9edadbdb98af42e5d8269364a326572cf703895b012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7900000000'
                    ),
                    signature_index=0,
                    signature=unhexlify(
                        '3045022100ccd253bfdf8a5593cd7b6701370c531199f0f05a418cd547dfc7da3f21515f0f02203fa08a0753688871c220648f9edadbdb98af42e5d8269364a326572cf703895b'
                    ),
                )),
        ]

        signer = signing.sign_tx(tx, root)
        for request, response in chunks(messages, 2):
            self.assertEqualEx(signer.send(request), response)
        with self.assertRaises(StopIteration):
            signer.send(None)
    def test_under_threshold(self):
        coin_bitcoin = coins.by_name('Bitcoin')

        ptx1 = PrevTx(version=1,
                      lock_time=0,
                      inputs_count=2,
                      outputs_count=1,
                      extra_data_len=0)
        pinp1 = PrevInput(
            script_sig=unhexlify(
                '483045022072ba61305fe7cb542d142b8f3299a7b10f9ea61f6ffaab5dca8142601869d53c0221009a8027ed79eb3b9bc13577ac2853269323434558528c6b6a7e542be46e7e9a820141047a2d177c0f3626fc68c53610b0270fa6156181f46586c679ba6a88b34c6f4874686390b4d92e5769fbb89c8050b984f4ec0b257a0e5c4ff8bd3b035a51709503'
            ),
            prev_hash=unhexlify(
                'c16a03f1cf8f99f6b5297ab614586cacec784c2d259af245909dedb0e39eddcf'
            ),
            prev_index=1,
            sequence=0xffff_ffff)
        pinp2 = PrevInput(
            script_sig=unhexlify(
                '48304502200fd63adc8f6cb34359dc6cca9e5458d7ea50376cbd0a74514880735e6d1b8a4c0221008b6ead7fe5fbdab7319d6dfede3a0bc8e2a7c5b5a9301636d1de4aa31a3ee9b101410486ad608470d796236b003635718dfc07c0cac0cfc3bfc3079e4f491b0426f0676e6643a39198e8e7bdaffb94f4b49ea21baa107ec2e237368872836073668214'
            ),
            prev_hash=unhexlify(
                '1ae39a2f8d59670c8fc61179148a8e61e039d0d9e8ab08610cb69b4a19453eaf'
            ),
            prev_index=1,
            sequence=0xffff_ffff)
        pout1 = PrevOutput(script_pubkey=unhexlify(
            '76a91424a56db43cf6f2b02e838ea493f95d8d6047423188ac'),
                           amount=390000)

        inp1 = TxInput(
            address_n=[0],  # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
            amount=390000,
            prev_hash=unhexlify(
                'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
            ),
            prev_index=0,
            multisig=None,
            sequence=0xffff_ffff)
        out1 = TxOutput(
            address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
            amount=390000 -
            90000,  # fee increased to 90000, slightly less than the threshold
            script_type=OutputScriptType.PAYTOADDRESS,
            multisig=None,
            address_n=[])
        tx = SignTx(coin_name=None,
                    version=1,
                    lock_time=0,
                    inputs_count=1,
                    outputs_count=1)

        messages = [
            None,
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            helpers.UiConfirmForeignAddress(address_n=inp1.address_n),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN),
            True,
            helpers.UiConfirmTotal(300000 + 90000, 90000, coin_bitcoin,
                                   AmountUnit.BITCOIN),
            True,
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXMETA,
                details=TxRequestDetailsType(
                    request_index=None,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=EMPTY_SERIALIZED),
            TxAckPrevMeta(tx=ptx1),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=EMPTY_SERIALIZED),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp1)),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=1,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=EMPTY_SERIALIZED),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp2)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=TxRequestSerializedType(
                          serialized_tx=unhexlify('0100000001'))),
        ]

        seed = bip39.seed(
            'alcohol woman abuse must during monitor noble actual mixed trade anger aisle',
            '')

        keychain = Keychain(seed, coin_bitcoin.curve_name,
                            [AlwaysMatchingSchema])
        approver = BasicApprover(tx, coin_bitcoin)
        signer = bitcoin.Bitcoin(tx, keychain, coin_bitcoin, approver).signer()
        for request, response in chunks(messages, 2):
            res = signer.send(request)
            if isinstance(res, tuple):
                _, res = res
            self.assertEqual(res, response)
Ejemplo n.º 16
0
async def sign_tx(tx: SignTx, root):

    tx = sanitize_sign_tx(tx)
    coin = coins.by_name(tx.coin_name)

    # Phase 1
    # - check inputs, previous transactions, and outputs
    # - ask for confirmations
    # - check fee

    total_in = 0  # sum of input amounts
    total_out = 0  # sum of output amounts
    change_out = 0  # change output amount

    # h_first is used to make sure the inputs and outputs streamed in Phase 1
    # are the same as in Phase 2.  it is thus not required to fully hash the
    # tx, as the SignTx info is streamed only once
    h_first = HashWriter(sha256)  # not a real tx hash

    txo_bin = TxOutputBinType()
    tx_req = TxRequest()
    tx_req.details = TxRequestDetailsType()

    for i in range(tx.inputs_count):
        # STAGE_REQUEST_1_INPUT
        txi = await request_tx_input(tx_req, i)
        write_tx_input_check(h_first, txi)
        total_in += await get_prevtx_output_value(
            tx_req, txi.prev_hash, txi.prev_index)

    for o in range(tx.outputs_count):
        # STAGE_REQUEST_3_OUTPUT
        txo = await request_tx_output(tx_req, o)
        if output_is_change(txo):
            if change_out != 0:
                raise SigningError(FailureType.ProcessError,
                                   'Only one change output is valid')
            change_out = txo.amount
        else:
            if not await confirm_output(txo, coin):
                raise SigningError(FailureType.ActionCancelled,
                                   'Output cancelled')
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, root)
        write_tx_output(h_first, txo_bin)
        total_out += txo_bin.amount

    fee = total_in - total_out

    if fee < 0:
        raise SigningError(FailureType.NotEnoughFunds,
                           'Not enough funds')

    if fee > coin.maxfee_kb * ((estimate_tx_size(tx.inputs_count, tx.outputs_count) + 999) // 1000):
        if not await confirm_feeoverthreshold(fee, coin):
            raise SigningError(FailureType.ActionCancelled,
                               'Signing cancelled')

    if not await confirm_total(total_out - change_out, fee, coin):
        raise SigningError(FailureType.ActionCancelled,
                           'Total cancelled')

    # Phase 2
    # - sign inputs
    # - check that nothing changed

    tx_ser = TxRequestSerializedType()

    for i_sign in range(tx.inputs_count):
        # hash of what we are signing with this input
        h_sign = HashWriter(sha256)
        # same as h_first, checked at the end of this iteration
        h_second = HashWriter(sha256)

        txi_sign = None
        key_sign = None
        key_sign_pub = None

        write_uint32(h_sign, tx.version)

        write_varint(h_sign, tx.inputs_count)

        for i in range(tx.inputs_count):
            # STAGE_REQUEST_4_INPUT
            txi = await request_tx_input(tx_req, i)
            write_tx_input_check(h_second, txi)
            if i == i_sign:
                txi_sign = txi
                key_sign = node_derive(root, txi.address_n)
                key_sign_pub = key_sign.public_key()
                txi.script_sig = input_derive_script(txi, key_sign_pub)
            else:
                txi.script_sig = bytes()
            write_tx_input(h_sign, txi)

        write_varint(h_sign, tx.outputs_count)

        for o in range(tx.outputs_count):
            # STAGE_REQUEST_4_OUTPUT
            txo = await request_tx_output(tx_req, o)
            txo_bin.amount = txo.amount
            txo_bin.script_pubkey = output_derive_script(txo, coin, root)
            write_tx_output(h_second, txo_bin)
            write_tx_output(h_sign, txo_bin)

        write_uint32(h_sign, tx.lock_time)

        write_uint32(h_sign, 0x00000001)  # SIGHASH_ALL hash_type

        # check the control digests
        if get_tx_hash(h_first, False) != get_tx_hash(h_second, False):
            raise SigningError(FailureType.ProcessError,
                               'Transaction has changed during signing')

        # compute the signature from the tx digest
        signature = ecdsa_sign(key_sign, get_tx_hash(h_sign, True))
        tx_ser.signature_index = i_sign
        tx_ser.signature = signature

        # serialize input with correct signature
        txi_sign.script_sig = input_derive_script(
            txi_sign, key_sign_pub, signature)
        w_txi_sign = bytearray_with_cap(
            len(txi_sign.prev_hash) + 4 + 5 + len(txi_sign.script_sig) + 4)
        if i_sign == 0:  # serializing first input => prepend tx version and inputs count
            write_uint32(w_txi_sign, tx.version)
            write_varint(w_txi_sign, tx.inputs_count)
        write_tx_input(w_txi_sign, txi_sign)
        tx_ser.serialized_tx = w_txi_sign

        tx_req.serialized = tx_ser

    for o in range(tx.outputs_count):
        # STAGE_REQUEST_5_OUTPUT
        txo = await request_tx_output(tx_req, o)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, root)

        # serialize output
        w_txo_bin = bytearray_with_cap(
            5 + 8 + 5 + len(txo_bin.script_pubkey) + 4)
        if o == 0:  # serializing first output => prepend outputs count
            write_varint(w_txo_bin, tx.outputs_count)
        write_tx_output(w_txo_bin, txo_bin)
        if o == tx.outputs_count - 1:  # serializing last output => append tx lock_time
            write_uint32(w_txo_bin, tx.lock_time)
        tx_ser.signature_index = None
        tx_ser.signature = None
        tx_ser.serialized_tx = w_txo_bin

        tx_req.serialized = tx_ser

    await request_tx_finish(tx_req)
Ejemplo n.º 17
0
async def sign_tx(tx: SignTx, root):

    tx = sanitize_sign_tx(tx)

    # Phase 1

    h_first, bip143, segwit, authorized_in, wallet_path = await check_tx_fee(
        tx, root)

    # Phase 2
    # - sign inputs
    # - check that nothing changed

    coin = coins.by_name(tx.coin_name)
    tx_ser = TxRequestSerializedType()

    txo_bin = TxOutputBinType()
    tx_req = TxRequest()
    tx_req.details = TxRequestDetailsType()
    tx_req.serialized = None

    for i_sign in range(tx.inputs_count):
        txi_sign = None
        key_sign = None
        key_sign_pub = None

        if segwit[i_sign]:
            # STAGE_REQUEST_SEGWIT_INPUT
            txi_sign = await request_tx_input(tx_req, i_sign)

            is_segwit = (txi_sign.script_type == InputScriptType.SPENDWITNESS
                         or txi_sign.script_type
                         == InputScriptType.SPENDP2SHWITNESS)
            if not is_segwit:
                raise SigningError(FailureType.ProcessError,
                                   'Transaction has changed during signing')
            input_check_wallet_path(txi_sign, wallet_path)

            key_sign = node_derive(root, txi_sign.address_n)
            key_sign_pub = key_sign.public_key()
            txi_sign.script_sig = input_derive_script(txi_sign, key_sign_pub)
            w_txi = bytearray_with_cap(7 + len(txi_sign.prev_hash) + 4 +
                                       len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi, get_tx_header(tx, True))
            write_tx_input(w_txi, txi_sign)
            tx_ser.serialized_tx = w_txi
            tx_req.serialized = tx_ser

        else:
            # hash of what we are signing with this input
            h_sign = HashWriter(sha256)
            # same as h_first, checked before signing the digest
            h_second = HashWriter(sha256)

            write_uint32(h_sign, tx.version)
            write_varint(h_sign, tx.inputs_count)

            for i in range(tx.inputs_count):
                # STAGE_REQUEST_4_INPUT
                txi = await request_tx_input(tx_req, i)
                input_check_wallet_path(txi, wallet_path)
                write_tx_input_check(h_second, txi)
                if i == i_sign:
                    txi_sign = txi
                    key_sign = node_derive(root, txi.address_n)
                    key_sign_pub = key_sign.public_key()
                    txi_sign.script_sig = output_script_p2pkh(
                        ecdsa_hash_pubkey(key_sign_pub))
                else:
                    txi.script_sig = bytes()
                write_tx_input(h_sign, txi)

            write_varint(h_sign, tx.outputs_count)

            for o in range(tx.outputs_count):
                # STAGE_REQUEST_4_OUTPUT
                txo = await request_tx_output(tx_req, o)
                txo_bin.amount = txo.amount
                txo_bin.script_pubkey = output_derive_script(txo, coin, root)
                write_tx_output(h_second, txo_bin)
                write_tx_output(h_sign, txo_bin)

            write_uint32(h_sign, tx.lock_time)

            write_uint32(h_sign, 0x00000001)  # SIGHASH_ALL hash_type

            # check the control digests
            if get_tx_hash(h_first, False) != get_tx_hash(h_second, False):
                raise SigningError(FailureType.ProcessError,
                                   'Transaction has changed during signing')

            # compute the signature from the tx digest
            signature = ecdsa_sign(key_sign, get_tx_hash(h_sign, True))
            tx_ser.signature_index = i_sign
            tx_ser.signature = signature

            # serialize input with correct signature
            txi_sign.script_sig = input_derive_script(txi_sign, key_sign_pub,
                                                      signature)
            w_txi_sign = bytearray_with_cap(5 + len(txi_sign.prev_hash) + 4 +
                                            len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi_sign, get_tx_header(tx))
            write_tx_input(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign

            tx_req.serialized = tx_ser

    for o in range(tx.outputs_count):
        # STAGE_REQUEST_5_OUTPUT
        txo = await request_tx_output(tx_req, o)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, root)

        # serialize output
        w_txo_bin = bytearray_with_cap(5 + 8 + 5 + len(txo_bin.script_pubkey) +
                                       4)
        if o == 0:  # serializing first output => prepend outputs count
            write_varint(w_txo_bin, tx.outputs_count)
        write_tx_output(w_txo_bin, txo_bin)

        tx_ser.signature_index = None
        tx_ser.signature = None
        tx_ser.serialized_tx = w_txo_bin

        tx_req.serialized = tx_ser

    any_segwit = True in segwit.values()

    for i in range(tx.inputs_count):
        if segwit[i]:
            # STAGE_REQUEST_SEGWIT_WITNESS
            txi = await request_tx_input(tx_req, i)
            input_check_wallet_path(txi, wallet_path)

            is_segwit = (txi.script_type == InputScriptType.SPENDWITNESS or
                         txi.script_type == InputScriptType.SPENDP2SHWITNESS)
            if not is_segwit or txi.amount > authorized_in:
                raise SigningError(FailureType.ProcessError,
                                   'Transaction has changed during signing')
            authorized_in -= txi.amount

            key_sign = node_derive(root, txi.address_n)
            key_sign_pub = key_sign.public_key()
            bip143_hash = bip143.preimage_hash(tx, txi,
                                               ecdsa_hash_pubkey(key_sign_pub))

            signature = ecdsa_sign(key_sign, bip143_hash)
            witness = get_p2wpkh_witness(signature, key_sign_pub)

            tx_ser.serialized_tx = witness
            tx_ser.signature_index = i
            tx_ser.signature = signature
        elif any_segwit:
            tx_ser.serialized_tx = bytearray(
                1)  # empty witness for non-segwit inputs
            tx_ser.signature_index = None
            tx_ser.signature = None

        tx_req.serialized = tx_ser

    write_uint32(tx_ser.serialized_tx, tx.lock_time)

    await request_tx_finish(tx_req)
Ejemplo n.º 18
0
    def test_purchase_ticket(self):
        inp1 = TxInput(
            address_n=[44 | 0x80000000, 42 | 0x80000000, 0 | 0x80000000, 0, 0],
            prev_hash=unhexlify(
                "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
            ),
            prev_index=0,
            amount=390000,
            multisig=None,
            sequence=0xFFFF_FFFF,
        )
        out1 = TxOutput(
            address="DsaHnKa418BeeQmyhpQEGG4cxGAPrneydfv",
            amount=390000 - 10000,
            script_type=OutputScriptType.PAYTOADDRESS,
            multisig=None,
        )
        out2 = TxOutput(
            address_n=[44 | 0x80000000, 42 | 0x80000000, 0 | 0x80000000, 0, 0],
            amount=390000,
            script_type=OutputScriptType.PAYTOADDRESS,
            multisig=None,
        )
        out3 = TxOutput(
            address="DsQxuVRvS4eaJ42dhQEsCXauMWjvopWgrVg",
            amount=0,
            script_type=OutputScriptType.PAYTOADDRESS,
            multisig=None,
        )
        tx = SignTx(coin_name="Decred",
                    version=1,
                    lock_time=0,
                    inputs_count=1,
                    outputs_count=3,
                    decred_staking_ticket=True)

        messages = [
            None,
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify("0100000001")),
            ),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(serialized_tx=unhexlify(
                    "4ac247307a054c37525169a78d690a7a7f87d709bef7d722aae85584f59c8fdf0000000000ffffffff03"
                )),
            ),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            helpers.UiConfirmDecredSSTXSubmission(out1, coin_decred,
                                                  AmountUnit.BITCOIN),
            True,
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=1, tx_hash=None),
                serialized=TxRequestSerializedType(serialized_tx=unhexlify(
                    "60cc05000000000000001aba76a914664b0cd46741a695a38f8ed37db2a20327471beb88ac"
                )),
            ),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=2, tx_hash=None),
                serialized=TxRequestSerializedType(serialized_tx=unhexlify(
                    "00000000000000000000206a1e762e46655536d93ad13f88a49bde9a2df45fe62e70f30500000000000058"
                )),
            ),
            TxAckOutput(tx=TxAckOutputWrapper(output=out3)),
            helpers.UiConfirmTotal(380000 + 10000, 10000, coin_decred,
                                   AmountUnit.BITCOIN),
            True,
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(serialized_tx=unhexlify(
                    "000000000000000000001abd76a914000000000000000000000000000000000000000088ac0000000000000000"
                )),
            ),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXMETA,
                details=TxRequestDetailsType(
                    request_index=None,
                    tx_hash=unhexlify(
                        "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
                    ),
                ),
                serialized=EMPTY_SERIALIZED,
            ),
            TxAckPrevMeta(tx=ptx1),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
                    ),
                ),
                serialized=EMPTY_SERIALIZED,
            ),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp1)),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=1,
                    tx_hash=unhexlify(
                        "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
                    ),
                ),
                serialized=EMPTY_SERIALIZED,
            ),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp2)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
                    ),
                ),
                serialized=EMPTY_SERIALIZED,
            ),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify("01")),
            ),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXFINISHED,
                details=TxRequestDetailsType(),
                serialized=TxRequestSerializedType(
                    signature_index=0,
                    signature=unhexlify(
                        "3045022100d2a6baadc88ea67ec94a1f6dca70882e647e9af68d24e1bc72f9c27359e5e6ff02207b8a939e7cf82e79e2947e8fe59a14c11ee0b3a9cd1ff084d9bd54e23291b6be"
                    ),
                    serialized_tx=unhexlify(
                        "70f305000000000000000000ffffffff6b483045022100d2a6baadc88ea67ec94a1f6dca70882e647e9af68d24e1bc72f9c27359e5e6ff02207b8a939e7cf82e79e2947e8fe59a14c11ee0b3a9cd1ff084d9bd54e23291b6be012103fc15aa2f684457332c0ef1fe44d908ab97208102a1792caa13bcc5e886c4b321"
                    )),
            ),
        ]

        seed = bip39.seed(
            "alcohol woman abuse must during monitor noble actual mixed trade anger aisle",
            "",
        )
        ns = get_schemas_for_coin(coin_decred)
        keychain = Keychain(seed, coin_decred.curve_name, ns)
        signer = decred.Decred(tx, keychain, coin_decred, None).signer()

        for request, response in chunks(messages, 2):
            res = signer.send(request)
            if isinstance(res, tuple):
                _, res = res

            self.assertEqual(res, response)

        with self.assertRaises(StopIteration):
            signer.send(None)
Ejemplo n.º 19
0
    def test_one_one_fee(self):

        inp1 = TxInput(
            address_n=[44 | 0x80000000, 42 | 0x80000000, 0 | 0x80000000, 0, 0],
            prev_hash=unhexlify(
                "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
            ),
            prev_index=0,
            amount=390000,
            multisig=None,
            sequence=0xFFFF_FFFF,
        )
        out1 = TxOutput(
            address="DsaHnKa418BeeQmyhpQEGG4cxGAPrneydfv",
            amount=390000 - 10000,
            script_type=OutputScriptType.PAYTOADDRESS,
            multisig=None,
        )
        tx = SignTx(coin_name="Decred",
                    version=1,
                    lock_time=0,
                    inputs_count=1,
                    outputs_count=1)

        messages = [
            None,
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify("0100000001")),
            ),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(serialized_tx=unhexlify(
                    "4ac247307a054c37525169a78d690a7a7f87d709bef7d722aae85584f59c8fdf0000000000ffffffff01"
                )),
            ),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            helpers.UiConfirmOutput(out1, coin_decred, AmountUnit.BITCOIN),
            True,
            helpers.UiConfirmTotal(380000 + 10000, 10000, coin_decred,
                                   AmountUnit.BITCOIN),
            True,
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(serialized_tx=unhexlify(
                    "60cc05000000000000001976a914664b0cd46741a695a38f8ed37db2a20327471beb88ac0000000000000000"
                )),
            ),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXMETA,
                details=TxRequestDetailsType(
                    request_index=None,
                    tx_hash=unhexlify(
                        "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
                    ),
                ),
                serialized=EMPTY_SERIALIZED,
            ),
            TxAckPrevMeta(tx=ptx1),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
                    ),
                ),
                serialized=EMPTY_SERIALIZED,
            ),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp1)),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=1,
                    tx_hash=unhexlify(
                        "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
                    ),
                ),
                serialized=EMPTY_SERIALIZED,
            ),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp2)),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        "df8f9cf58455e8aa22d7f7be09d7877f7a0a698da7695152374c057a3047c24a"
                    ),
                ),
                serialized=EMPTY_SERIALIZED,
            ),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify("01")),
            ),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(
                request_type=TXFINISHED,
                details=TxRequestDetailsType(request_index=None, tx_hash=None),
                serialized=TxRequestSerializedType(
                    signature_index=0,
                    signature=unhexlify(
                        "3044022078a5c388838796562eb9dad176b00e6d9425bc360083f633a14948685ca8a5ce02202a1b49cd44104a9d40aee8f988281a8aac94a497b5bc7337c77cc7ddbab16f23"
                    ),
                    serialized_tx=unhexlify(
                        "70f305000000000000000000ffffffff6a473044022078a5c388838796562eb9dad176b00e6d9425bc360083f633a14948685ca8a5ce02202a1b49cd44104a9d40aee8f988281a8aac94a497b5bc7337c77cc7ddbab16f23012103fc15aa2f684457332c0ef1fe44d908ab97208102a1792caa13bcc5e886c4b321"
                    ),
                ),
            ),
        ]

        seed = bip39.seed(
            "alcohol woman abuse must during monitor noble actual mixed trade anger aisle",
            "",
        )
        ns = get_schemas_for_coin(coin_decred)
        keychain = Keychain(seed, coin_decred.curve_name, ns)
        signer = decred.Decred(tx, keychain, coin_decred, None).signer()

        for request, response in chunks(messages, 2):
            res = signer.send(request)
            if isinstance(res, tuple):
                _, res = res

            self.assertEqual(res, response)

        with self.assertRaises(StopIteration):
            signer.send(None)
Ejemplo n.º 20
0
    def test_one_one_fee(self):
        # tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882
        # input 0: 0.0039 BTC

        coin_bitcoin = coins.by_name('Bitcoin')

        ptx1 = TransactionType(version=1,
                               lock_time=0,
                               inputs_cnt=2,
                               outputs_cnt=1)
        pinp1 = TxInputType(
            script_sig=unhexlify(
                '483045022072ba61305fe7cb542d142b8f3299a7b10f9ea61f6ffaab5dca8142601869d53c0221009a8027ed79eb3b9bc13577ac2853269323434558528c6b6a7e542be46e7e9a820141047a2d177c0f3626fc68c53610b0270fa6156181f46586c679ba6a88b34c6f4874686390b4d92e5769fbb89c8050b984f4ec0b257a0e5c4ff8bd3b035a51709503'
            ),
            prev_hash=unhexlify(
                'c16a03f1cf8f99f6b5297ab614586cacec784c2d259af245909dedb0e39eddcf'
            ),
            prev_index=1,
            script_type=None,
            sequence=None)
        pinp2 = TxInputType(
            script_sig=unhexlify(
                '48304502200fd63adc8f6cb34359dc6cca9e5458d7ea50376cbd0a74514880735e6d1b8a4c0221008b6ead7fe5fbdab7319d6dfede3a0bc8e2a7c5b5a9301636d1de4aa31a3ee9b101410486ad608470d796236b003635718dfc07c0cac0cfc3bfc3079e4f491b0426f0676e6643a39198e8e7bdaffb94f4b49ea21baa107ec2e237368872836073668214'
            ),
            prev_hash=unhexlify(
                '1ae39a2f8d59670c8fc61179148a8e61e039d0d9e8ab08610cb69b4a19453eaf'
            ),
            prev_index=1,
            script_type=None,
            sequence=None)
        pout1 = TxOutputBinType(script_pubkey=unhexlify(
            '76a91424a56db43cf6f2b02e838ea493f95d8d6047423188ac'),
                                amount=390000,
                                address_n=None)

        inp1 = TxInputType(
            address_n=[0],  # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
            # amount=390000,
            prev_hash=unhexlify(
                'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
            ),
            prev_index=0,
            script_type=None,
            sequence=None)
        out1 = TxOutputType(address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
                            amount=390000 - 10000,
                            script_type=OutputScriptType.PAYTOADDRESS,
                            address_n=None)
        tx = SignTx(coin_name=None,
                    version=None,
                    lock_time=None,
                    inputs_count=1,
                    outputs_count=1)

        messages = [
            None,
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None)),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(
                request_type=TXMETA,
                details=TxRequestDetailsType(
                    request_index=None,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=None),
            TxAck(tx=ptx1),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=None),
            TxAck(tx=TransactionType(inputs=[pinp1])),
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(
                    request_index=1,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=None),
            TxAck(tx=TransactionType(inputs=[pinp2])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(
                    request_index=0,
                    tx_hash=unhexlify(
                        'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'
                    )),
                serialized=None),
            TxAck(tx=TransactionType(bin_outputs=[pout1])),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=None),
            TxAck(tx=TransactionType(outputs=[out1])),
            signing.UiConfirmOutput(out1, coin_bitcoin),
            True,
            signing.UiConfirmTotal(380000, 10000, coin_bitcoin),
            True,
            # ButtonRequest(code=ButtonRequest_ConfirmOutput),
            # ButtonRequest(code=ButtonRequest_SignTx),
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=None),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=None),
            TxAck(tx=TransactionType(outputs=[out1])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    signature_index=0,
                    signature=unhexlify(
                        '30450221009a0b7be0d4ed3146ee262b42202841834698bb3ee39c24e7437df208b8b7077102202b79ab1e7736219387dffe8d615bbdba87e11477104b867ef47afed1a5ede781'
                    ),
                    serialized_tx=unhexlify(
                        '010000000182488650ef25a58fef6788bd71b8212038d7f2bbe4750bc7bcb44701e85ef6d5000000006b4830450221009a0b7be0d4ed3146ee262b42202841834698bb3ee39c24e7437df208b8b7077102202b79ab1e7736219387dffe8d615bbdba87e11477104b867ef47afed1a5ede7810121023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43dffffffff'
                    ))),
            TxAck(tx=TransactionType(outputs=[out1])),
            TxRequest(
                request_type=TXFINISHED,
                details=None,
                serialized=TxRequestSerializedType(
                    signature_index=None,
                    signature=None,
                    serialized_tx=unhexlify(
                        '0160cc0500000000001976a914de9b2a8da088824e8fe51debea566617d851537888ac00000000'
                    ),
                )),
        ]

        seed = bip39.seed(
            'alcohol woman abuse must during monitor noble actual mixed trade anger aisle',
            '')
        root = bip32.from_seed(seed, 'secp256k1')

        signer = signing.sign_tx(tx, root)
        for request, response in chunks(messages, 2):
            self.assertEqualEx(signer.send(request), response)
        with self.assertRaises(StopIteration):
            signer.send(None)
Ejemplo n.º 21
0
    def test_send_native_invalid_address(self):

        coin = coins.by_name('Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInput(
            # 49'/1'/0'/0/0" - tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 0, 0],
            amount=12300000,
            prev_hash=unhexlify(
                '09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        ptx1 = PrevTx(version=1,
                      lock_time=0,
                      inputs_count=1,
                      outputs_count=2,
                      extra_data_len=0)
        pinp1 = PrevInput(
            script_sig=unhexlify(
                '160014d16b8c0680c61fc6ed2e407455715055e41052f5'),
            prev_hash=unhexlify(
                '20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'
            ),
            prev_index=0,
            sequence=4294967295)
        pout1 = PrevOutput(script_pubkey=unhexlify(
            '00140099a7ecbd938ed1839f5f6bf6d50933c6db9d5c'),
                           amount=12300000)
        pout2 = PrevOutput(script_pubkey=unhexlify(
            'a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'),
                           amount=111145789)

        out1 = TxOutput(
            address=
            'TB1Q694CCP5QCC0UDMFWGP692U2S2HJPQ5H407URTU',  # Error: should be lower case
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=12300000 - 11000 - 5000000,
            address_n=[],
            multisig=None,
        )
        tx = SignTx(coin_name='Testnet',
                    version=1,
                    lock_time=0,
                    inputs_count=1,
                    outputs_count=1)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            helpers.UiConfirmForeignAddress(address_n=inp1.address_n),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            None
        ]

        ns = get_schemas_for_coin(coin)
        keychain = Keychain(seed, coin.curve_name, ns)
        approver = BasicApprover(tx, coin)
        signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer()
        for request, expected_response in chunks(messages, 2):
            if expected_response is None:
                with self.assertRaises(wire.DataError):
                    signer.send(request)
            else:
                response = signer.send(request)
                if isinstance(response, tuple):
                    _, response = response
                self.assertEqual(response, expected_response)
Ejemplo n.º 22
0
async def sign_tx(tx: SignTx, root: bip32.HDNode):
    tx = sanitize_sign_tx(tx)

    progress.init(tx.inputs_count, tx.outputs_count)

    # Phase 1

    h_first, hash143, segwit, authorized_in, wallet_path = await check_tx_fee(
        tx, root)

    # Phase 2
    # - sign inputs
    # - check that nothing changed

    coin = coins.by_name(tx.coin_name)
    tx_ser = TxRequestSerializedType()

    txo_bin = TxOutputBinType()
    tx_req = TxRequest()
    tx_req.details = TxRequestDetailsType()
    tx_req.serialized = None

    if coin.decred:
        prefix_hash = hash143.prefix_hash()

    for i_sign in range(tx.inputs_count):
        progress.advance()
        txi_sign = None
        key_sign = None
        key_sign_pub = None

        if segwit[i_sign]:
            # STAGE_REQUEST_SEGWIT_INPUT
            txi_sign = await request_tx_input(tx_req, i_sign)

            is_segwit = (txi_sign.script_type == InputScriptType.SPENDWITNESS
                         or txi_sign.script_type
                         == InputScriptType.SPENDP2SHWITNESS)
            if not is_segwit:
                raise SigningError(FailureType.ProcessError,
                                   "Transaction has changed during signing")
            input_check_wallet_path(txi_sign, wallet_path)

            key_sign = node_derive(root, txi_sign.address_n)
            key_sign_pub = key_sign.public_key()
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub)

            w_txi = empty_bytearray(7 + len(txi_sign.prev_hash) + 4 +
                                    len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi, get_tx_header(coin, tx, True))
            write_tx_input(w_txi, txi_sign)
            tx_ser.serialized_tx = w_txi
            tx_req.serialized = tx_ser

        elif coin.force_bip143 or tx.overwintered:
            # STAGE_REQUEST_SEGWIT_INPUT
            txi_sign = await request_tx_input(tx_req, i_sign)
            input_check_wallet_path(txi_sign, wallet_path)

            is_bip143 = (txi_sign.script_type == InputScriptType.SPENDADDRESS
                         or txi_sign.script_type
                         == InputScriptType.SPENDMULTISIG)
            if not is_bip143 or txi_sign.amount > authorized_in:
                raise SigningError(FailureType.ProcessError,
                                   "Transaction has changed during signing")
            authorized_in -= txi_sign.amount

            key_sign = node_derive(root, txi_sign.address_n)
            key_sign_pub = key_sign.public_key()
            hash143_hash = hash143.preimage_hash(
                coin,
                tx,
                txi_sign,
                ecdsa_hash_pubkey(key_sign_pub, coin),
                get_hash_type(coin),
            )

            # if multisig, check if singing with a key that is included in multisig
            if txi_sign.multisig:
                multisig_pubkey_index(txi_sign.multisig, key_sign_pub)

            signature = ecdsa_sign(key_sign, hash143_hash)
            tx_ser.signature_index = i_sign
            tx_ser.signature = signature

            # serialize input with correct signature
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub, signature)
            w_txi_sign = empty_bytearray(5 + len(txi_sign.prev_hash) + 4 +
                                         len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi_sign, get_tx_header(coin, tx))
            write_tx_input(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign

            tx_req.serialized = tx_ser

        elif coin.decred:
            txi_sign = await request_tx_input(tx_req, i_sign)

            input_check_wallet_path(txi_sign, wallet_path)

            key_sign = node_derive(root, txi_sign.address_n)
            key_sign_pub = key_sign.public_key()

            if txi_sign.script_type == InputScriptType.SPENDMULTISIG:
                prev_pkscript = output_script_multisig(
                    multisig_get_pubkeys(txi_sign.multisig),
                    txi_sign.multisig.m)
            elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
                prev_pkscript = output_script_p2pkh(
                    ecdsa_hash_pubkey(key_sign_pub, coin))
            else:
                raise ValueError("Unknown input script type")

            h_witness = HashWriter(blake256())
            write_uint32(h_witness,
                         tx.version | DECRED_SERIALIZE_WITNESS_SIGNING)
            write_varint(h_witness, tx.inputs_count)

            for ii in range(tx.inputs_count):
                if ii == i_sign:
                    write_varint(h_witness, len(prev_pkscript))
                    write_bytes(h_witness, prev_pkscript)
                else:
                    write_varint(h_witness, 0)

            witness_hash = get_tx_hash(h_witness,
                                       double=coin.sign_hash_double,
                                       reverse=False)

            h_sign = HashWriter(blake256())
            write_uint32(h_sign, DECRED_SIGHASHALL)
            write_bytes(h_sign, prefix_hash)
            write_bytes(h_sign, witness_hash)

            sig_hash = get_tx_hash(h_sign, double=coin.sign_hash_double)
            signature = ecdsa_sign(key_sign, sig_hash)
            tx_ser.signature_index = i_sign
            tx_ser.signature = signature

            # serialize input with correct signature
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub, signature)
            w_txi_sign = empty_bytearray(
                8 + 4 +
                len(hash143.get_last_output_bytes()) if i_sign == 0 else 0 +
                16 + 4 + len(txi_sign.script_sig))

            if i_sign == 0:
                write_bytes(w_txi_sign, hash143.get_last_output_bytes())
                write_uint32(w_txi_sign, tx.lock_time)
                write_uint32(w_txi_sign, tx.expiry)
                write_varint(w_txi_sign, tx.inputs_count)

            write_tx_input_decred_witness(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign
            tx_req.serialized = tx_ser

        else:
            # hash of what we are signing with this input
            h_sign = HashWriter(sha256())
            # same as h_first, checked before signing the digest
            h_second = HashWriter(sha256())

            if tx.overwintered:
                write_uint32(h_sign, tx.version
                             | OVERWINTERED)  # nVersion | fOverwintered
                write_uint32(h_sign, tx.version_group_id)  # nVersionGroupId
            else:
                write_uint32(h_sign, tx.version)  # nVersion

            write_varint(h_sign, tx.inputs_count)

            for i in range(tx.inputs_count):
                # STAGE_REQUEST_4_INPUT
                txi = await request_tx_input(tx_req, i)
                input_check_wallet_path(txi, wallet_path)
                write_tx_input_check(h_second, txi)
                if i == i_sign:
                    txi_sign = txi
                    key_sign = node_derive(root, txi.address_n)
                    key_sign_pub = key_sign.public_key()
                    # for the signing process the script_sig is equal
                    # to the previous tx's scriptPubKey (P2PKH) or a redeem script (P2SH)
                    if txi_sign.script_type == InputScriptType.SPENDMULTISIG:
                        txi_sign.script_sig = output_script_multisig(
                            multisig_get_pubkeys(txi_sign.multisig),
                            txi_sign.multisig.m)
                    elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
                        txi_sign.script_sig = output_script_p2pkh(
                            ecdsa_hash_pubkey(key_sign_pub, coin))
                        if coin.bip115:
                            txi_sign.script_sig += script_replay_protection_bip115(
                                txi_sign.prev_block_hash_bip115,
                                txi_sign.prev_block_height_bip115,
                            )
                    else:
                        raise SigningError(FailureType.ProcessError,
                                           "Unknown transaction type")
                else:
                    txi.script_sig = bytes()
                write_tx_input(h_sign, txi)

            write_varint(h_sign, tx.outputs_count)

            for o in range(tx.outputs_count):
                # STAGE_REQUEST_4_OUTPUT
                txo = await request_tx_output(tx_req, o)
                txo_bin.amount = txo.amount
                txo_bin.script_pubkey = output_derive_script(txo, coin, root)
                write_tx_output(h_second, txo_bin)
                write_tx_output(h_sign, txo_bin)

            write_uint32(h_sign, tx.lock_time)
            if tx.overwintered:
                write_uint32(h_sign, tx.expiry)  # expiryHeight
                write_varint(h_sign, 0)  # nJoinSplit

            write_uint32(h_sign, get_hash_type(coin))

            # check the control digests
            if get_tx_hash(h_first, False) != get_tx_hash(h_second):
                raise SigningError(FailureType.ProcessError,
                                   "Transaction has changed during signing")

            # if multisig, check if singing with a key that is included in multisig
            if txi_sign.multisig:
                multisig_pubkey_index(txi_sign.multisig, key_sign_pub)

            # compute the signature from the tx digest
            signature = ecdsa_sign(
                key_sign, get_tx_hash(h_sign, double=coin.sign_hash_double))
            tx_ser.signature_index = i_sign
            tx_ser.signature = signature

            # serialize input with correct signature
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub, signature)
            w_txi_sign = empty_bytearray(5 + len(txi_sign.prev_hash) + 4 +
                                         len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi_sign, get_tx_header(coin, tx))
            write_tx_input(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign

            tx_req.serialized = tx_ser

    if coin.decred:
        return await request_tx_finish(tx_req)

    for o in range(tx.outputs_count):
        progress.advance()
        # STAGE_REQUEST_5_OUTPUT
        txo = await request_tx_output(tx_req, o)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, root)

        # serialize output
        w_txo_bin = empty_bytearray(5 + 8 + 5 + len(txo_bin.script_pubkey) + 4)
        if o == 0:  # serializing first output => prepend outputs count
            write_varint(w_txo_bin, tx.outputs_count)
        write_tx_output(w_txo_bin, txo_bin)

        tx_ser.signature_index = None
        tx_ser.signature = None
        tx_ser.serialized_tx = w_txo_bin

        tx_req.serialized = tx_ser

    any_segwit = True in segwit.values()

    for i in range(tx.inputs_count):
        progress.advance()
        if segwit[i]:
            # STAGE_REQUEST_SEGWIT_WITNESS
            txi = await request_tx_input(tx_req, i)
            input_check_wallet_path(txi, wallet_path)

            is_segwit = (txi.script_type == InputScriptType.SPENDWITNESS or
                         txi.script_type == InputScriptType.SPENDP2SHWITNESS)
            if not is_segwit or txi.amount > authorized_in:
                raise SigningError(FailureType.ProcessError,
                                   "Transaction has changed during signing")
            authorized_in -= txi.amount

            key_sign = node_derive(root, txi.address_n)
            key_sign_pub = key_sign.public_key()
            hash143_hash = hash143.preimage_hash(
                coin,
                tx,
                txi,
                ecdsa_hash_pubkey(key_sign_pub, coin),
                get_hash_type(coin),
            )

            signature = ecdsa_sign(key_sign, hash143_hash)
            if txi.multisig:
                # find out place of our signature based on the pubkey
                signature_index = multisig_pubkey_index(
                    txi.multisig, key_sign_pub)
                witness = witness_p2wsh(txi.multisig, signature,
                                        signature_index, get_hash_type(coin))
            else:
                witness = witness_p2wpkh(signature, key_sign_pub,
                                         get_hash_type(coin))

            tx_ser.serialized_tx = witness
            tx_ser.signature_index = i
            tx_ser.signature = signature
        elif any_segwit:
            tx_ser.serialized_tx = bytearray(
                1)  # empty witness for non-segwit inputs
            tx_ser.signature_index = None
            tx_ser.signature = None

        tx_req.serialized = tx_ser

    write_uint32(tx_ser.serialized_tx, tx.lock_time)

    if tx.overwintered:
        if tx.version == 3:
            write_uint32(tx_ser.serialized_tx, tx.expiry)  # expiryHeight
            write_varint(tx_ser.serialized_tx, 0)  # nJoinSplit
        elif tx.version == 4:
            write_uint32(tx_ser.serialized_tx, tx.expiry)  # expiryHeight
            write_uint64(tx_ser.serialized_tx, 0)  # valueBalance
            write_varint(tx_ser.serialized_tx, 0)  # nShieldedSpend
            write_varint(tx_ser.serialized_tx, 0)  # nShieldedOutput
            write_varint(tx_ser.serialized_tx, 0)  # nJoinSplit
        else:
            raise SigningError(
                FailureType.DataError,
                "Unsupported version for overwintered transaction",
            )

    await request_tx_finish(tx_req)
    def test_send_p2wpkh_in_p2sh(self):

        coin = coins.by_name('Groestlcoin Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInputType(
            # 49'/1'/0'/1/0" - 2N1LGaGg836mqSQqiuUBLfcyGBhyZYBtBZ7
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            amount=123456789,
            prev_hash=unhexlify(
                '09a48bce2f9d5c6e4f0cb9ea1b32d0891855e8acfe5334f9ebd72b9ad2de60cf'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDP2SHWITNESS,
            sequence=0xfffffffe,
            multisig=None,
        )
        out1 = TxOutputType(
            address='mvbu1Gdy8SUjTenqerxUaZyYjmvedc787y',
            amount=12300000,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=[],
            multisig=None,
        )
        out2 = TxOutputType(
            address='2N1LGaGg836mqSQqiuUBLfcyGBhyZYBtBZ7',
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=123456789 - 11000 - 12300000,
            address_n=[],
            multisig=None,
        )
        tx = SignTx(coin_name='Groestlcoin Testnet',
                    version=None,
                    lock_time=650756,
                    inputs_count=1,
                    outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(outputs=[out1])),
            helpers.UiConfirmOutput(out1, coin),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(outputs=[out2])),
            helpers.UiConfirmOutput(out2, coin),
            True,
            helpers.UiConfirmNonDefaultLocktime(tx.lock_time),
            True,
            helpers.UiConfirmTotal(123445789 + 11000, 11000, coin),
            True,

            # sign tx
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized header
                    serialized_tx=unhexlify('01000000000101'), )),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized inp1
                    serialized_tx=unhexlify(
                        'cf60ded29a2bd7ebf93453feace8551889d0321beab90c4f6e5c9d2fce8ba4090000000017160014d16b8c0680c61fc6ed2e407455715055e41052f5feffffff02'
                    ), )),
            TxAck(tx=TransactionType(outputs=[out1])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=1, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out1
                    serialized_tx=unhexlify(
                        'e0aebb00000000001976a914a579388225827d9f2fe9014add644487808c695d88ac'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAck(tx=TransactionType(outputs=[out2])),

            # segwit
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out2
                    serialized_tx=unhexlify(
                        '3df39f060000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(
                request_type=TXFINISHED,
                details=TxRequestDetailsType(),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify(
                        '02483045022100b7ce2972bcbc3a661fe320ba901e680913b2753fcb47055c9c6ba632fc4acf81022001c3cfd6c2fe92eb60f5176ce0f43707114dd7223da19c56f2df89c13c2fef80012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7904ee0900'
                    ),
                    signature_index=0,
                    signature=unhexlify(
                        '3045022100b7ce2972bcbc3a661fe320ba901e680913b2753fcb47055c9c6ba632fc4acf81022001c3cfd6c2fe92eb60f5176ce0f43707114dd7223da19c56f2df89c13c2fef80'
                    ),
                )),
        ]

        ns = get_namespaces_for_coin(coin)
        keychain = Keychain(seed, ns)
        signer = bitcoinlike.Bitcoinlike(tx, keychain, coin).signer()
        for request, response in chunks(messages, 2):
            self.assertEqual(signer.send(request), response)
        with self.assertRaises(StopIteration):
            signer.send(None)
    def test_send_native_p2wpkh_change(self):

        coin = coins.by_name('Testnet')

        seed = bip39.seed(' '.join(['all'] * 12), '')
        root = bip32.from_seed(seed, 'secp256k1')

        inp1 = TxInputType(
            # 49'/1'/0'/0/0" - tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 0, 0],
            amount=12300000,
            prev_hash=unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'),
            prev_index=0,
            script_type=InputScriptType.SPENDWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        out1 = TxOutputType(
            address='2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp',
            amount=5000000,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=[],
            multisig=None,
        )
        out2 = TxOutputType(
            address=None,
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            script_type=OutputScriptType.PAYTOWITNESS,
            amount=12300000 - 11000 - 5000000,
            multisig=None,
        )
        tx = SignTx(coin_name='Testnet', version=None, lock_time=None, inputs_count=1, outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
            TxAck(tx=TransactionType(inputs=[inp1])),

            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
            TxAck(tx=TransactionType(outputs=[out1])),

            signing.UiConfirmOutput(out1, coin),
            True,

            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=None),
            TxAck(tx=TransactionType(outputs=[out2])),

            signing.UiConfirmTotal(5000000, 11000, coin),
            True,

            # sign tx
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
            TxAck(tx=TransactionType(inputs=[inp1])),

            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=TxRequestSerializedType(
                # returned serialized inp1
                serialized_tx=unhexlify('010000000001018a44999c07bba32df1cacdc50987944e68e3205b4429438fdde35c76024614090000000000ffffffff'),
            )),
            # the out has to be cloned not to send the same object which was modified
            TxAck(tx=TransactionType(outputs=[TxOutputType(**out1.__dict__)])),

            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=TxRequestSerializedType(
                # returned serialized out1
                serialized_tx=unhexlify('02404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987'),
                signature_index=None,
                signature=None,
            )),
            TxAck(tx=TransactionType(outputs=[TxOutputType(**out2.__dict__)])),

            # segwit
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=TxRequestSerializedType(
                # returned serialized out2
                serialized_tx=unhexlify('a8386f0000000000160014d16b8c0680c61fc6ed2e407455715055e41052f5'),
                signature_index=None,
                signature=None,
            )),
            TxAck(tx=TransactionType(inputs=[inp1])),

            TxRequest(request_type=TXFINISHED, details=None, serialized=TxRequestSerializedType(
                serialized_tx=unhexlify('02483045022100a7ca8f097525f9044e64376dc0a0f5d4aeb8d15d66808ba97979a0475b06b66502200597c8ebcef63e047f9aeef1a8001d3560470cf896c12f6990eec4faec599b950121033add1f0e8e3c3136f7428dd4a4de1057380bd311f5b0856e2269170b4ffa65bf00000000'),
                signature_index=0,
                signature=unhexlify('3045022100a7ca8f097525f9044e64376dc0a0f5d4aeb8d15d66808ba97979a0475b06b66502200597c8ebcef63e047f9aeef1a8001d3560470cf896c12f6990eec4faec599b95'),
            )),
        ]

        signer = signing.sign_tx(tx, root)
        for request, response in chunks(messages, 2):
            self.assertEqualEx(signer.send(request), response)
        with self.assertRaises(StopIteration):
            signer.send(None)
Ejemplo n.º 25
0
async def check_tx_fee(tx: SignTx, root: bip32.HDNode):
    coin = coins.by_name(tx.coin_name)

    # h_first is used to make sure the inputs and outputs streamed in Phase 1
    # are the same as in Phase 2.  it is thus not required to fully hash the
    # tx, as the SignTx info is streamed only once
    h_first = HashWriter(sha256())  # not a real tx hash

    if coin.decred:
        hash143 = DecredPrefixHasher(tx)  # pseudo bip143 prefix hashing
        tx_ser = TxRequestSerializedType()
    elif tx.overwintered:
        if tx.version == 3:
            hash143 = Zip143()  # ZIP-0143 transaction hashing
        elif tx.version == 4:
            hash143 = Zip243()  # ZIP-0243 transaction hashing
        else:
            raise SigningError(
                FailureType.DataError,
                "Unsupported version for overwintered transaction",
            )
    else:
        hash143 = Bip143()  # BIP-0143 transaction hashing

    multifp = MultisigFingerprint()  # control checksum of multisig inputs
    weight = TxWeightCalculator(tx.inputs_count, tx.outputs_count)

    total_in = 0  # sum of input amounts
    segwit_in = 0  # sum of segwit input amounts
    total_out = 0  # sum of output amounts
    change_out = 0  # change output amount
    wallet_path = []  # common prefix of input paths
    segwit = {}  # dict of booleans stating if input is segwit

    # output structures
    txo_bin = TxOutputBinType()
    tx_req = TxRequest()
    tx_req.details = TxRequestDetailsType()

    for i in range(tx.inputs_count):
        progress.advance()
        # STAGE_REQUEST_1_INPUT
        txi = await request_tx_input(tx_req, i)
        wallet_path = input_extract_wallet_path(txi, wallet_path)
        write_tx_input_check(h_first, txi)
        weight.add_input(txi)
        hash143.add_prevouts(
            txi)  # all inputs are included (non-segwit as well)
        hash143.add_sequence(txi)

        if not address_n_matches_coin(txi.address_n, coin):
            await confirm_foreign_address(txi.address_n, coin)

        if txi.multisig:
            multifp.add(txi.multisig)

        if txi.script_type in (
                InputScriptType.SPENDWITNESS,
                InputScriptType.SPENDP2SHWITNESS,
        ):
            if not coin.segwit:
                raise SigningError(FailureType.DataError,
                                   "Segwit not enabled on this coin")
            if not txi.amount:
                raise SigningError(FailureType.DataError,
                                   "Segwit input without amount")
            segwit[i] = True
            segwit_in += txi.amount
            total_in += txi.amount

        elif txi.script_type in (
                InputScriptType.SPENDADDRESS,
                InputScriptType.SPENDMULTISIG,
        ):
            if coin.force_bip143 or tx.overwintered:
                if not txi.amount:
                    raise SigningError(FailureType.DataError,
                                       "Expected input with amount")
                segwit[i] = False
                segwit_in += txi.amount
                total_in += txi.amount
            else:
                segwit[i] = False
                total_in += await get_prevtx_output_value(
                    coin, tx_req, txi.prev_hash, txi.prev_index)

        else:
            raise SigningError(FailureType.DataError,
                               "Wrong input script type")

        if coin.decred:
            w_txi = empty_bytearray(8 if i == 0 else 0 + 9 +
                                    len(txi.prev_hash))
            if i == 0:  # serializing first input => prepend headers
                write_bytes(w_txi, get_tx_header(coin, tx))
            write_tx_input_decred(w_txi, txi)
            tx_ser.serialized_tx = w_txi
            tx_req.serialized = tx_ser

    if coin.decred:
        hash143.add_output_count(tx)

    for o in range(tx.outputs_count):
        # STAGE_REQUEST_3_OUTPUT
        txo = await request_tx_output(tx_req, o)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, root)
        weight.add_output(txo_bin.script_pubkey)

        if change_out == 0 and is_change(txo, wallet_path, segwit_in, multifp):
            # output is change and does not need confirmation
            change_out = txo.amount
        elif not await confirm_output(txo, coin):
            raise SigningError(FailureType.ActionCancelled, "Output cancelled")

        if coin.decred:
            if txo.decred_script_version is not None and txo.decred_script_version != 0:
                raise SigningError(
                    FailureType.ActionCancelled,
                    "Cannot send to output with script version != 0",
                )
            txo_bin.decred_script_version = txo.decred_script_version

            w_txo_bin = empty_bytearray(4 + 8 + 2 + 4 +
                                        len(txo_bin.script_pubkey))
            if o == 0:  # serializing first output => prepend outputs count
                write_varint(w_txo_bin, tx.outputs_count)
            write_tx_output(w_txo_bin, txo_bin)
            tx_ser.serialized_tx = w_txo_bin
            tx_req.serialized = tx_ser
            hash143.set_last_output_bytes(w_txo_bin)

        write_tx_output(h_first, txo_bin)
        hash143.add_output(txo_bin)
        total_out += txo_bin.amount

    fee = total_in - total_out
    if fee < 0:
        raise SigningError(FailureType.NotEnoughFunds, "Not enough funds")

    # fee > (coin.maxfee per byte * tx size)
    if fee > (coin.maxfee_kb / 1000) * (weight.get_total() / 4):
        if not await confirm_feeoverthreshold(fee, coin):
            raise SigningError(FailureType.ActionCancelled,
                               "Signing cancelled")

    if not await confirm_total(total_in - change_out, fee, coin):
        raise SigningError(FailureType.ActionCancelled, "Total cancelled")

    if coin.decred:
        hash143.add_locktime_expiry(tx)

    return h_first, hash143, segwit, total_in, wallet_path
    def test_send_native_p2wpkh(self):

        coin = coins.by_name('Groestlcoin Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInputType(
            # 84'/1'/0'/0/0" - tgrs1qkvwu9g3k2pdxewfqr7syz89r3gj557l3ued7ja
            address_n=[84 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 0, 0],
            amount=12300000,
            prev_hash=unhexlify(
                '4f2f857f39ed1afe05542d058fb0be865a387446e32fc876d086203f483f61d1'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDWITNESS,
            sequence=0xfffffffe,
            multisig=None,
        )
        out1 = TxOutputType(
            address='2N4Q5FhU2497BryFfUgbqkAJE87aKDv3V3e',
            amount=5000000,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=[],
            multisig=None,
        )
        out2 = TxOutputType(
            address='tgrs1qejqxwzfld7zr6mf7ygqy5s5se5xq7vmt9lkd57',
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=12300000 - 11000 - 5000000,
            address_n=[],
            multisig=None,
        )
        tx = SignTx(coin_name='Groestlcoin Testnet',
                    version=None,
                    lock_time=650713,
                    inputs_count=1,
                    outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(outputs=[out1])),
            helpers.UiConfirmOutput(out1, coin),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(outputs=[out2])),
            helpers.UiConfirmOutput(out2, coin),
            True,
            helpers.UiConfirmNonDefaultLocktime(tx.lock_time),
            True,
            helpers.UiConfirmTotal(12300000, 11000, coin),
            True,

            # sign tx
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized header
                    serialized_tx=unhexlify('01000000000101'), )),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized inp1
                    serialized_tx=unhexlify(
                        'd1613f483f2086d076c82fe34674385a86beb08f052d5405fe1aed397f852f4f0000000000feffffff02'
                    ), )),
            TxAck(tx=TransactionType(outputs=[out1])),
            TxRequest(
                request_type=TXOUTPUT,
                details=TxRequestDetailsType(request_index=1, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out1
                    serialized_tx=unhexlify(
                        '404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAck(tx=TransactionType(outputs=[out2])),

            # segwit
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized out2
                    serialized_tx=unhexlify(
                        'a8386f0000000000160014cc8067093f6f843d6d3e22004a4290cd0c0f336b'
                    ),
                    signature_index=None,
                    signature=None,
                )),
            TxAck(tx=TransactionType(inputs=[inp1])),
            TxRequest(
                request_type=TXFINISHED,
                details=TxRequestDetailsType(),
                serialized=TxRequestSerializedType(
                    serialized_tx=unhexlify(
                        '02483045022100ea8780bc1e60e14e945a80654a41748bbf1aa7d6f2e40a88d91dfc2de1f34bd10220181a474a3420444bd188501d8d270736e1e9fe379da9970de992ff445b0972e3012103adc58245cf28406af0ef5cc24b8afba7f1be6c72f279b642d85c48798685f862d9ed0900'
                    ),
                    signature_index=0,
                    signature=unhexlify(
                        '3045022100ea8780bc1e60e14e945a80654a41748bbf1aa7d6f2e40a88d91dfc2de1f34bd10220181a474a3420444bd188501d8d270736e1e9fe379da9970de992ff445b0972e3'
                    ),
                )),
        ]

        keychain = Keychain(seed, [[coin.curve_name]])
        signer = bitcoinlike.Bitcoinlike(tx, keychain, coin).signer()
        for request, response in chunks(messages, 2):
            self.assertEqual(signer.send(request), response)
        with self.assertRaises(StopIteration):
            signer.send(None)
Ejemplo n.º 27
0
    def test_send_p2wpkh_in_p2sh_attack_amount(self):

        coin = coins.by_name('Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInput(
            # 49'/1'/0'/1/0" - 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            amount=10,
            prev_hash=unhexlify(
                '20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDP2SHWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        ptx1 = PrevTx(version=1,
                      lock_time=0,
                      inputs_count=1,
                      outputs_count=2,
                      extra_data_len=0)
        pinp1 = PrevInput(
            script_sig=unhexlify(
                '4730440220548e087d0426b20b8a571b03b9e05829f7558b80c53c12143e342f56ab29e51d02205b68cb7fb223981d4c999725ac1485a982c4259c4f50b8280f137878c232998a012102794a25b254a268e59a5869da57fbae2fadc6727cb3309321dab409b12b2fa17c'
            ),
            prev_hash=unhexlify(
                '802cabf0843b945eabe136d7fc7c89f41021658abf56cba000acbce88c41143a'
            ),
            prev_index=0,
            sequence=4294967295)
        pout1 = PrevOutput(script_pubkey=unhexlify(
            'a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87'),
                           amount=123456789)
        pout2 = PrevOutput(script_pubkey=unhexlify(
            '76a914b84bacdcd8f4cc59274a5bfb73f804ca10f7fd1488ac'),
                           amount=865519308)

        inpattack = TxInput(
            # 49'/1'/0'/1/0" - 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            amount=9,  # modified!
            prev_hash=unhexlify(
                '20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'
            ),
            prev_index=0,
            script_type=InputScriptType.SPENDP2SHWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        out1 = TxOutput(
            address='mhRx1CeVfaayqRwq5zgRQmD7W5aWBfD5mC',
            amount=8,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=[],
            multisig=None,
        )
        out2 = TxOutput(
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
            script_type=OutputScriptType.PAYTOP2SHWITNESS,
            amount=1,
            address=None,
            multisig=None,
        )
        tx = SignTx(coin_name='Testnet',
                    version=1,
                    lock_time=0,
                    inputs_count=1,
                    outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inpattack)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
            helpers.UiConfirmOutput(out1, coin),
            True,
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
            helpers.UiConfirmTotal(9 - 1, 9 - 8 - 1, coin),
            True,

            # check prev tx
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=None),
                      serialized=EMPTY_SERIALIZED),
            TxAckInput(tx=TxAckInputWrapper(input=inp1)),
            TxRequest(request_type=TXMETA,
                      details=TxRequestDetailsType(request_index=None,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevMeta(tx=ptx1),
            TxRequest(request_type=TXINPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevInput(tx=TxAckPrevInputWrapper(input=pinp1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=0,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
            TxRequest(request_type=TXOUTPUT,
                      details=TxRequestDetailsType(request_index=1,
                                                   tx_hash=inp1.prev_hash),
                      serialized=EMPTY_SERIALIZED),
            TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout2)),

            # sign tx
            TxRequest(
                request_type=TXINPUT,
                details=TxRequestDetailsType(request_index=0, tx_hash=None),
                serialized=TxRequestSerializedType(
                    # returned serialized header
                    serialized_tx=unhexlify('01000000000101'), )),
        ]

        ns = get_namespaces_for_coin(coin)
        keychain = Keychain(seed, coin.curve_name, ns)
        approver = BasicApprover(tx, coin)
        signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer()
        i = 0
        messages_count = int(len(messages) / 2)
        for request, expected_response in chunks(messages, 2):
            if i == messages_count - 1:  # last message should throw wire.Error
                self.assertRaises(wire.DataError, signer.send, request)
            else:
                response = signer.send(request)
                if isinstance(response, tuple):
                    _, response = response
                self.assertEqual(response, expected_response)
            i += 1
        with self.assertRaises(StopIteration):
            signer.send(None)
    def test_send_native_p2wpkh(self):

        coin = coins.by_name('Testnet')
        seed = bip39.seed(' '.join(['all'] * 12), '')

        inp1 = TxInputType(
            # 49'/1'/0'/0/0" - tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s
            address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 0, 0],
            amount=12300000,
            prev_hash=unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'),
            prev_index=0,
            script_type=InputScriptType.SPENDWITNESS,
            sequence=0xffffffff,
            multisig=None,
        )
        out1 = TxOutputType(
            address='2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp',
            amount=5000000,
            script_type=OutputScriptType.PAYTOADDRESS,
            address_n=[],
            multisig=None,
        )
        out2 = TxOutputType(
            address='tb1q694ccp5qcc0udmfwgp692u2s2hjpq5h407urtu',
            script_type=OutputScriptType.PAYTOADDRESS,
            amount=12300000 - 11000 - 5000000,
            address_n=[],
            multisig=None,
        )
        tx = SignTx(coin_name='Testnet', version=None, lock_time=None, inputs_count=1, outputs_count=2)

        messages = [
            None,

            # check fee
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(inputs=[inp1])),

            helpers.UiConfirmForeignAddress(address_n=inp1.address_n),
            True,

            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(outputs=[out1])),

            helpers.UiConfirmOutput(out1, coin),
            True,

            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
            TxAck(tx=TransactionType(outputs=[out2])),

            helpers.UiConfirmOutput(out2, coin),
            True,

            helpers.UiConfirmTotal(12300000, 11000, coin),
            True,

            # sign tx
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=TxRequestSerializedType(
                # returned serialized header
                serialized_tx=unhexlify('01000000000101'),
            )),
            TxAck(tx=TransactionType(inputs=[inp1])),

            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=TxRequestSerializedType(
                # returned serialized inp1
                serialized_tx=unhexlify('8a44999c07bba32df1cacdc50987944e68e3205b4429438fdde35c76024614090000000000ffffffff02'),
            )),
            TxAck(tx=TransactionType(outputs=[out1])),

            TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=TxRequestSerializedType(
                # returned serialized out1
                serialized_tx=unhexlify('404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987'),
                signature_index=None,
                signature=None,
            )),
            TxAck(tx=TransactionType(outputs=[out2])),

            # segwit
            TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=TxRequestSerializedType(
                # returned serialized out2
                serialized_tx=unhexlify('a8386f0000000000160014d16b8c0680c61fc6ed2e407455715055e41052f5'),
                signature_index=None,
                signature=None,
            )),
            TxAck(tx=TransactionType(inputs=[inp1])),

            TxRequest(request_type=TXFINISHED, details=TxRequestDetailsType(), serialized=TxRequestSerializedType(
                serialized_tx=unhexlify('02483045022100a7ca8f097525f9044e64376dc0a0f5d4aeb8d15d66808ba97979a0475b06b66502200597c8ebcef63e047f9aeef1a8001d3560470cf896c12f6990eec4faec599b950121033add1f0e8e3c3136f7428dd4a4de1057380bd311f5b0856e2269170b4ffa65bf00000000'),
                signature_index=0,
                signature=unhexlify('3045022100a7ca8f097525f9044e64376dc0a0f5d4aeb8d15d66808ba97979a0475b06b66502200597c8ebcef63e047f9aeef1a8001d3560470cf896c12f6990eec4faec599b95'),
            )),
        ]

        ns = get_namespaces_for_coin(coin)
        keychain = Keychain(seed, ns)
        signer = bitcoin.Bitcoin(tx, keychain, coin).signer()
        for request, response in chunks(messages, 2):
            res = signer.send(request)
            self.assertEqual(res, response)
        with self.assertRaises(StopIteration):
            signer.send(None)
Ejemplo n.º 29
0
async def check_tx_fee(tx: SignTx, root: bip32.HDNode):
    coin = coins.by_name(tx.coin_name)

    # h_first is used to make sure the inputs and outputs streamed in Phase 1
    # are the same as in Phase 2.  it is thus not required to fully hash the
    # tx, as the SignTx info is streamed only once
    h_first = HashWriter(sha256)  # not a real tx hash

    bip143 = Bip143()  # bip143 transaction hashing
    multifp = MultisigFingerprint()  # control checksum of multisig inputs
    weight = TxWeightCalculator(tx.inputs_count, tx.outputs_count)

    total_in = 0  # sum of input amounts
    segwit_in = 0  # sum of segwit input amounts
    total_out = 0  # sum of output amounts
    change_out = 0  # change output amount
    wallet_path = []  # common prefix of input paths
    segwit = {}  # dict of booleans stating if input is segwit

    # output structures
    txo_bin = TxOutputBinType()
    tx_req = TxRequest()
    tx_req.details = TxRequestDetailsType()

    for i in range(tx.inputs_count):
        progress.advance()
        # STAGE_REQUEST_1_INPUT
        txi = await request_tx_input(tx_req, i)
        wallet_path = input_extract_wallet_path(txi, wallet_path)
        write_tx_input_check(h_first, txi)
        weight.add_input(txi)
        bip143.add_prevouts(
            txi)  # all inputs are included (non-segwit as well)
        bip143.add_sequence(txi)

        if txi.multisig:
            multifp.add(txi.multisig)

        if txi.script_type in (InputScriptType.SPENDWITNESS,
                               InputScriptType.SPENDP2SHWITNESS):
            if not coin.segwit:
                raise SigningError(FailureType.DataError,
                                   'Segwit not enabled on this coin')
            if not txi.amount:
                raise SigningError(FailureType.DataError,
                                   'Segwit input without amount')
            segwit[i] = True
            segwit_in += txi.amount
            total_in += txi.amount

        elif txi.script_type in (InputScriptType.SPENDADDRESS,
                                 InputScriptType.SPENDMULTISIG):
            if coin.force_bip143:
                if not txi.amount:
                    raise SigningError(FailureType.DataError,
                                       'BIP 143 input without amount')
                segwit[i] = False
                segwit_in += txi.amount
                total_in += txi.amount
            else:
                segwit[i] = False
                total_in += await get_prevtx_output_value(
                    tx_req, txi.prev_hash, txi.prev_index)

        else:
            raise SigningError(FailureType.DataError,
                               'Wrong input script type')

    for o in range(tx.outputs_count):
        # STAGE_REQUEST_3_OUTPUT
        txo = await request_tx_output(tx_req, o)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, root)
        weight.add_output(txo_bin.script_pubkey)

        if change_out == 0 and is_change(txo, wallet_path, segwit_in, multifp):
            # output is change and does not need confirmation
            change_out = txo.amount
        elif not await confirm_output(txo, coin):
            raise SigningError(FailureType.ActionCancelled, 'Output cancelled')

        write_tx_output(h_first, txo_bin)
        bip143.add_output(txo_bin)
        total_out += txo_bin.amount

    fee = total_in - total_out
    if fee < 0:
        raise SigningError(FailureType.NotEnoughFunds, 'Not enough funds')

    # fee > (coin.maxfee per byte * tx size)
    if fee > (coin.maxfee_kb / 1000) * (weight.get_total() / 4):
        if not await confirm_feeoverthreshold(fee, coin):
            raise SigningError(FailureType.ActionCancelled,
                               'Signing cancelled')

    if not await confirm_total(total_out - change_out, fee, coin):
        raise SigningError(FailureType.ActionCancelled, 'Total cancelled')

    return h_first, bip143, segwit, total_in, wallet_path
Ejemplo n.º 30
0
async def check_tx_fee(tx: SignTx, keychain: seed.Keychain, coin: coininfo.CoinInfo):
    # h_first is used to make sure the inputs and outputs streamed in Phase 1
    # are the same as in Phase 2.  it is thus not required to fully hash the
    # tx, as the SignTx info is streamed only once
    h_first = utils.HashWriter(sha256())  # not a real tx hash

    if not utils.BITCOIN_ONLY and coin.decred:
        hash143 = decred.DecredPrefixHasher(tx)  # pseudo BIP-0143 prefix hashing
        tx_ser = TxRequestSerializedType()
    elif not utils.BITCOIN_ONLY and coin.overwintered:
        if tx.version == 3:
            branch_id = tx.branch_id or 0x5BA81B19  # Overwinter
            hash143 = zcash.Zip143(branch_id)  # ZIP-0143 transaction hashing
        elif tx.version == 4:
            branch_id = tx.branch_id or 0x76B809BB  # Sapling
            hash143 = zcash.Zip243(branch_id)  # ZIP-0243 transaction hashing
        else:
            raise SigningError(
                FailureType.DataError,
                "Unsupported version for overwintered transaction",
            )
    else:
        hash143 = segwit_bip143.Bip143()  # BIP-0143 transaction hashing

    multisig_fp = multisig.MultisigFingerprint()  # control checksum of multisig inputs
    weight = tx_weight.TxWeightCalculator(tx.inputs_count, tx.outputs_count)

    total_in = 0  # sum of input amounts
    bip143_in = 0  # sum of segwit input amounts
    total_out = 0  # sum of output amounts
    change_out = 0  # change output amount
    wallet_path = []  # common prefix of input paths
    segwit = {}  # dict of booleans stating if input is segwit

    # output structures
    txo_bin = TxOutputBinType()
    tx_req = TxRequest()
    tx_req.details = TxRequestDetailsType()

    for i in range(tx.inputs_count):
        progress.advance()
        # STAGE_REQUEST_1_INPUT
        txi = await helpers.request_tx_input(tx_req, i, coin)
        wallet_path = input_extract_wallet_path(txi, wallet_path)
        writers.write_tx_input_check(h_first, txi)
        weight.add_input(txi)
        hash143.add_prevouts(txi)  # all inputs are included (non-segwit as well)
        hash143.add_sequence(txi)

        if not addresses.validate_full_path(txi.address_n, coin, txi.script_type):
            await helpers.confirm_foreign_address(txi.address_n)

        if txi.multisig:
            multisig_fp.add(txi.multisig)
        else:
            multisig_fp.mismatch = True

        if txi.script_type in (
            InputScriptType.SPENDWITNESS,
            InputScriptType.SPENDP2SHWITNESS,
        ):
            if not coin.segwit:
                raise SigningError(
                    FailureType.DataError, "Segwit not enabled on this coin"
                )
            if not txi.amount:
                raise SigningError(FailureType.DataError, "Segwit input without amount")
            segwit[i] = True
            bip143_in += txi.amount
            total_in += txi.amount

        elif txi.script_type in (
            InputScriptType.SPENDADDRESS,
            InputScriptType.SPENDMULTISIG,
        ):
            if not utils.BITCOIN_ONLY and (coin.force_bip143 or coin.overwintered):
                if not txi.amount:
                    raise SigningError(
                        FailureType.DataError, "Expected input with amount"
                    )
                segwit[i] = False
                bip143_in += txi.amount
                total_in += txi.amount
            else:
                segwit[i] = False
                total_in += await get_prevtx_output_value(
                    coin, tx_req, txi.prev_hash, txi.prev_index
                )

        else:
            raise SigningError(FailureType.DataError, "Wrong input script type")

        if not utils.BITCOIN_ONLY and coin.decred:
            w_txi = writers.empty_bytearray(8 if i == 0 else 0 + 9 + len(txi.prev_hash))
            if i == 0:  # serializing first input => prepend headers
                # decred doesn't support segwit
                write_tx_header(w_txi, coin, tx, False)
            writers.write_tx_input_decred(w_txi, txi)
            tx_ser.serialized_tx = w_txi
            tx_req.serialized = tx_ser

    if not utils.BITCOIN_ONLY and coin.decred:
        hash143.add_output_count(tx)

    for o in range(tx.outputs_count):
        # STAGE_REQUEST_3_OUTPUT
        txo = await helpers.request_tx_output(tx_req, o, coin)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, keychain)
        weight.add_output(txo_bin.script_pubkey)

        if change_out == 0 and output_is_change(txo, wallet_path, multisig_fp):
            # output is change and does not need confirmation
            change_out = txo.amount
        elif not await helpers.confirm_output(txo, coin):
            raise SigningError(FailureType.ActionCancelled, "Output cancelled")

        if not utils.BITCOIN_ONLY and coin.decred:
            if txo.decred_script_version is not None and txo.decred_script_version != 0:
                raise SigningError(
                    FailureType.ActionCancelled,
                    "Cannot send to output with script version != 0",
                )
            txo_bin.decred_script_version = txo.decred_script_version

            w_txo_bin = writers.empty_bytearray(
                4 + 8 + 2 + 4 + len(txo_bin.script_pubkey)
            )
            if o == 0:  # serializing first output => prepend outputs count
                writers.write_varint(w_txo_bin, tx.outputs_count)
            writers.write_tx_output(w_txo_bin, txo_bin)
            tx_ser.serialized_tx = w_txo_bin
            tx_req.serialized = tx_ser
            hash143.set_last_output_bytes(w_txo_bin)

        writers.write_tx_output(h_first, txo_bin)
        hash143.add_output(txo_bin)
        total_out += txo_bin.amount

    fee = total_in - total_out

    if not utils.BITCOIN_ONLY and coin.negative_fee:
        pass  # bypass check for negative fee coins, required for reward TX
    else:
        if fee < 0:
            raise SigningError(FailureType.NotEnoughFunds, "Not enough funds")

    # fee > (coin.maxfee per byte * tx size)
    if fee > (coin.maxfee_kb / 1000) * (weight.get_total() / 4):
        if not await helpers.confirm_feeoverthreshold(fee, coin):
            raise SigningError(FailureType.ActionCancelled, "Signing cancelled")

    if tx.lock_time > 0:
        if not await helpers.confirm_nondefault_locktime(tx.lock_time):
            raise SigningError(FailureType.ActionCancelled, "Locktime cancelled")

    if not await helpers.confirm_total(total_in - change_out, fee, coin):
        raise SigningError(FailureType.ActionCancelled, "Total cancelled")

    if not utils.BITCOIN_ONLY and coin.decred:
        hash143.add_locktime_expiry(tx)

    return h_first, hash143, segwit, bip143_in, wallet_path, multisig_fp