示例#1
0
 def precompute_subaddr(self, account, indices):
     """
     Precomputes subaddresses for account (major) and list of indices (minors)
     Subaddresses have to be stored in encoded form - unique representation.
     Single point can have multiple extended coordinates representation - would not match during subaddress search.
     :param account:
     :param indices:
     :return:
     """
     monero.compute_subaddresses(self.creds, account, indices, self.subaddresses)
示例#2
0
    async def init(self, ctx, msg: MoneroKeyImageExportInitRequest):
        self.ctx = ctx
        await self.derive_creds(msg)

        confirmation = await self.iface.confirm_ki_sync(msg, ctx=ctx)
        if not confirmation:
            return Failure(code=FailureType.ActionCancelled, message="rejected")

        self.num = msg.num
        self.hash = msg.hash
        self.enc_key = crypto.random_bytes(32)

        # Sub address precomputation
        if msg.subs and len(msg.subs) > 0:
            for sub in msg.subs:
                monero.compute_subaddresses(
                    self.creds, sub.account, sub.minor_indices, self.subaddresses
                )
        return MoneroKeyImageExportInitAck()
示例#3
0
    async def get_tx_key_test(self, agent, con_data, creds, all_creds):
        salt1 = con_data.enc_salt1
        salt2 = con_data.enc_salt2
        res = await agent.get_tx_key(salt1, salt2, con_data.enc_keys,
                                     con_data.tx_prefix_hash,
                                     creds.view_key_private)

        extras = await monero.parse_extra_fields(list(con_data.tx.extra))
        tx_pub = monero.find_tx_extra_field_by_type(extras,
                                                    xmrtypes.TxExtraPubKey, 0)
        additional_pub_keys = monero.find_tx_extra_field_by_type(
            extras, xmrtypes.TxExtraAdditionalPubKeys)

        # For verification need to build database creds -> pubkeys
        all_creds_subs = []
        for idx, ccred in enumerate(all_creds):
            subs = {}
            for accnt in range(10):
                subs = monero.compute_subaddresses(ccred, accnt, range(20),
                                                   subs)
            all_creds_subs.append(
                [crypto.decodepoint(xx) for xx in subs.keys()])

        if not self.verify_tx_key(res[0], crypto.decodepoint(tx_pub.pub_key),
                                  all_creds_subs):
            raise ValueError("Tx pub mismatch")

        if additional_pub_keys and len(
                additional_pub_keys.data) != len(res) - 1:
            raise ValueError("Invalid additional keys count")

        if additional_pub_keys:
            for i, ad in enumerate(additional_pub_keys.data):
                if not self.verify_tx_key(res[i + 1], crypto.decodepoint(ad),
                                          all_creds_subs):
                    raise ValueError("Tx additional %s pub mismatch" % i)

        my_pub = crypto.scalarmult_base(creds.view_key_private)
        res_der = await agent.get_tx_deriv(salt1, salt2, con_data.enc_keys,
                                           con_data.tx_prefix_hash,
                                           creds.view_key_private,
                                           crypto.encodepoint(my_pub))
        if len(res) != len(res_der):
            raise ValueError("Derivation array mismatch")
        tmp = crypto.scalarmult(my_pub, res[0])
        if not crypto.point_eq(tmp, res_der[0]):
            raise ValueError("Derivation 0 mismatch")

        if additional_pub_keys:
            for i in range(len(additional_pub_keys.data)):
                tmp = crypto.scalarmult(my_pub, res[i + 1])
                if not crypto.point_eq(tmp, res_der[i + 1]):
                    raise ValueError("Tx derivation additional %s mismatch" %
                                     i)
示例#4
0
 def precompute_subaddr(self, keys, subs, major_cnt=10, minor_cnt=200):
     if keys is None:
         return None
     for i in range(major_cnt):
         monero.compute_subaddresses(keys, i, list(range(0, minor_cnt)), subs)
示例#5
0
    async def receive(self, tx, all_creds, con_data=None, exp_payment_id=None):
        """
        Test transaction receive with known view/spend keys of destinations.
        :param tx:
        :param all_creds:
        :param con_data:
        :param exp_payment_id:
        :return:
        """
        # Unserialize the transaction
        tx_obj = xmrtypes.Transaction()
        reader = xmrserialize.MemoryReaderWriter(bytearray(tx))
        ar1 = xmrserialize.Archive(reader, False)

        await ar1.message(tx_obj, msg_type=xmrtypes.Transaction)
        extras = await monero.parse_extra_fields(tx_obj.extra)
        tx_pub = monero.find_tx_extra_field_by_type(
            extras, xmrtypes.TxExtraPubKey
        ).pub_key
        additional_pub_keys = monero.find_tx_extra_field_by_type(
            extras, xmrtypes.TxExtraAdditionalPubKeys
        )
        num_outs = len(tx_obj.vout)
        num_received = 0

        # Try to receive tsx outputs with each account.
        tx_money_got_in_outs = collections.defaultdict(lambda: 0)
        outs = []

        change_idx = get_change_addr_idx(con_data.tsx_data.outputs, con_data.tsx_data.change_dts)

        for idx, creds in enumerate(all_creds):
            wallet_subs = {}
            for account in range(0, 5):
                monero.compute_subaddresses(creds, account, range(25), wallet_subs)

            derivation = crypto.generate_key_derivation(
                crypto.decodepoint(tx_pub), creds.view_key_private
            )
            additional_derivations = []
            if additional_pub_keys and additional_pub_keys.data:
                for x in additional_pub_keys.data:
                    additional_derivations.append(
                        crypto.generate_key_derivation(
                            crypto.decodepoint(x), creds.view_key_private
                        )
                    )

            for ti, to in enumerate(tx_obj.vout):
                tx_scan_info = monero.check_acc_out_precomp(
                    to, wallet_subs, derivation, additional_derivations, ti
                )
                if not tx_scan_info.received:
                    continue

                num_received += 1
                tx_scan_info = monero.scan_output(
                    creds, tx_obj, ti, tx_scan_info, tx_money_got_in_outs, outs, False
                )

                # Check spending private key correctness
                self.assertTrue(
                    crypto.point_eq(
                        crypto.decodepoint(tx_obj.rct_signatures.outPk[ti].mask),
                        crypto.gen_c(tx_scan_info.mask, tx_scan_info.amount),
                    )
                )

                self.assertTrue(
                    crypto.point_eq(
                        crypto.decodepoint(tx_obj.vout[ti].target.key),
                        crypto.scalarmult_base(tx_scan_info.in_ephemeral),
                    )
                )

                if exp_payment_id is not None:
                    payment_id = None
                    # Not checking payment id for change transaction
                    if exp_payment_id[0] == 1 and change_idx is not None and ti == change_idx:
                        continue

                    payment_id_type = None
                    extra_nonce = monero.find_tx_extra_field_by_type(extras, xmrtypes.TxExtraNonce)
                    if extra_nonce and monero.has_encrypted_payment_id(extra_nonce.nonce):
                        payment_id_type = 1
                        payment_id = monero.get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce)
                        payment_id = monero.encrypt_payment_id(payment_id, crypto.decodepoint(tx_pub), creds.view_key_private)

                    elif extra_nonce and monero.has_payment_id(extra_nonce.nonce):
                        payment_id_type = 0
                        payment_id = monero.get_payment_id_from_tx_extra_nonce(extra_nonce.nonce)

                    self.assertEqual(payment_id_type, exp_payment_id[0])
                    self.assertEqual(payment_id, exp_payment_id[1])

        # All outputs have to be successfully received
        self.assertEqual(num_outs, num_received)
示例#6
0
    async def describe(self, unsigned_txs, keys, key_subs=None):
        inp = unsigned_txs.txes[0].sources
        if key_subs is None:
            key_subs = {}
            for tx in enumerate(unsigned_txs.txes):
                monero.compute_subaddresses(keys, tx.subaddr_account,
                                            list(tx.subaddr_indices), key_subs)

        print("\nInp: %s, #txs: %s, #transfers: %s" %
              (inp, len(unsigned_txs.txes), len(unsigned_txs.transfers)))
        for txid, tx in enumerate(unsigned_txs.txes):
            srcs = tx.sources
            dsts = tx.splitted_dsts
            extra = tx.extra
            change = tx.change_dts
            account = tx.subaddr_account
            subs = tx.subaddr_indices
            mixin = len(srcs[0].outputs) - 1
            amnt_in = sum([x.amount for x in srcs])
            amnt_out = sum([x.amount for x in dsts])
            fee = amnt_in - amnt_out
            n_inp_additional = sum(
                [1 for x in srcs if len(x.real_out_additional_tx_keys) > 0])

            change_addr = (addr.build_address(change.addr.m_spend_public_key,
                                              change.addr.m_view_public_key)
                           if change else None)
            out_txs2 = await self.reformat_outs(dsts)

            num_stdaddresses, num_subaddresses, single_dest_subaddress = addr.classify_subaddresses(
                out_txs2, change_addr)

            print(
                "  tx: %s, #inp: %2d, #inp_add: %2d, #out: %2d, mixin: %2d, acc: %s, subs: %s, "
                "xmr_in: %10.6f, xmr_out: %10.6f, fee: %10.6f, change: %10.6f, out_clean: %10.6f"
                % (
                    txid,
                    len(srcs),
                    n_inp_additional,
                    len(dsts),
                    mixin,
                    account,
                    subs,
                    wallet.conv_disp_amount(amnt_in),
                    wallet.conv_disp_amount(amnt_out),
                    wallet.conv_disp_amount(fee),
                    wallet.conv_disp_amount(change.amount) if change else 0,
                    wallet.conv_disp_amount(
                        (amnt_out - change.amount) if change else amnt_out),
                ))
            print(
                "  Out: num_std: %2d, num_sub: %2d, single_dest_sub: %s, total: %s"
                % (
                    num_stdaddresses,
                    num_subaddresses,
                    1 if single_dest_subaddress else 0,
                    len(dsts),
                ))

            accounts = set()
            subs = set()
            for inp in srcs:
                res = await self.analyze_input(keys, key_subs, inp)
                accounts.add(res[0])
                if res != (0, 0):
                    subs.add(res)

            print("  Ins: accounts: %s, subs: %s" % (accounts, len(subs)))

            extras = await monero.parse_extra_fields(extra)
            extras_val = []
            for c in extras:
                if isinstance(c, TxExtraPubKey):
                    extras_val.append("TxKey")
                elif isinstance(c, TxExtraNonce):
                    extras_val.append(
                        "Nonce: %s" %
                        binascii.hexlify(c.nonce).decode("ascii"))
                elif isinstance(c, TxExtraAdditionalPubKeys):
                    extras_val.append("AdditionalTxKeys: %s" % len(c.data))
                else:
                    extras_val.append(str(c))
            print("  Extras: %s" % ", ".join(extras_val))

            # Final verification
            for idx, inp in enumerate(tx.sources):
                self.check_input(inp, keys, key_subs)
                if not crypto.point_eq(
                        crypto.decodepoint(
                            inp.outputs[inp.real_output][1].mask),
                        crypto.gen_c(crypto.decodeint(inp.mask), inp.amount),
                ):
                    raise ValueError(
                        "Real source entry's mask does not equal spend key's. Inp: %d"
                        % idx)
示例#7
0
    async def gen_input(self,
                        value=1,
                        sub_major=None,
                        sub_minor=0,
                        additionals=False):
        creds = self.src_keys
        r = self.random_scalar()
        R = crypto.scalarmult_base(r)
        additional_keys = []
        Additional = None
        sub_major = sub_major if sub_major is not None else self.account_idx
        is_sub = sub_major != 0 or sub_minor != 0

        if sub_major != self.account_idx:
            logger.warning(
                "Generating input with different major subindex, cannot be spent in the resulting "
                "transaction")

        kssec = monero.get_subaddress_secret_key(creds.view_key_private,
                                                 major=sub_major,
                                                 minor=sub_minor)
        kssub = crypto.sc_add(
            kssec,
            creds.spend_key_private) if is_sub else creds.spend_key_private
        kvsub = crypto.sc_mul(creds.view_key_private,
                              kssub) if is_sub else creds.view_key_private
        KSSUB, KVSUB = monero.generate_sub_address_keys(
            creds.view_key_private, creds.spend_key_public, sub_major,
            sub_minor)

        if not crypto.point_eq(KSSUB, crypto.scalarmult_base(kssub)):
            raise ValueError("Invariant error")

        oidx = self.prng.randint(0, 12)
        additionals_cnt = self.prng.randint(oidx + 1, 2 * oidx + 1)
        deriv = crypto.generate_key_derivation(KVSUB, r)
        kout = crypto.derive_secret_key(deriv, oidx, kssub)
        KOUT = crypto.derive_public_key(deriv, oidx, KSSUB)
        if not crypto.point_eq(KOUT, crypto.scalarmult_base(kout)):
            raise ValueError("Invariant error")

        if additionals:
            if is_sub:
                Additional = crypto.scalarmult(KSSUB, r)
            else:
                Additional = crypto.scalarmult_base(r)
        else:
            if is_sub:
                R = crypto.scalarmult(KSSUB, r)

        amnt = crypto.sc_init(value)
        msk = self.random_scalar()  # commitment mask
        C = crypto.add_keys2(msk, amnt, crypto.xmr_H())

        ring = []
        for i in range(self.ring_size - 1):
            tk = CtKey(
                dest=crypto.encodepoint(self.random_pub()),
                mask=crypto.encodepoint(self.random_pub()),
            )
            ring.append(tk)

        index = self.prng.randint(0, len(ring))
        ring.insert(
            index,
            CtKey(dest=crypto.encodepoint(KOUT), mask=crypto.encodepoint(C)))
        if additionals:
            additional_keys = [
                self.random_pub() for _ in range(additionals_cnt)
            ]
            additional_keys[oidx] = Additional

        src = TxSourceEntry()
        src.outputs = [(self.random_glob_idx(), x) for x in ring]
        src.real_output = index
        src.real_out_tx_key = crypto.encodepoint(R)
        src.real_out_additional_tx_keys = [
            crypto.encodepoint(x) for x in additional_keys
        ]
        src.real_output_in_tx_index = oidx
        src.amount = value
        src.rct = True
        src.mask = crypto.encodeint(msk)
        src.multisig_kLRki = MultisigKLRki(K=crypto.ZERO,
                                           L=crypto.ZERO,
                                           R=crypto.ZERO,
                                           ki=crypto.ZERO)

        td = TransferDetails()
        td.m_internal_output_index = oidx
        td.m_global_output_index = src.outputs[index][0]
        td.m_mask = src.mask
        td.m_amount = value
        td.m_subaddr_index = SubaddressIndex(major=sub_major, minor=sub_minor)
        td.m_rct = True
        td.m_txid = self.random_bytes(32)
        td.m_block_height = self.prng.randint(0, 0xFFFF)
        td.m_spent = False
        td.m_spent_height = 0
        td.m_key_image_known = True
        td.m_key_image_requested = False
        td.m_key_image_partial = False
        td.m_multisig_k = []
        td.m_multisig_info = []
        td.m_uses = []
        td.m_pk_index = 0
        td.m_tx = self.gen_tx_prefix(self.prng.randint(1, 10), additionals_cnt)
        td.m_tx.vout[oidx].target.key = crypto.encodepoint(KOUT)

        extras = []
        extras.append(TxExtraNonce(nonce=self.random_bytes(8)))
        extras.append(TxExtraPubKey(pub_key=src.real_out_tx_key))
        if src.real_out_additional_tx_keys:
            extras.append(
                TxExtraAdditionalPubKeys(data=src.real_out_additional_tx_keys))
        td.m_tx.extra = await self.dump_extra_fields(extras)

        tmpsubs = {}
        monero.compute_subaddresses(creds, sub_major, [sub_minor], tmpsubs)
        xi, ki, rderiv = self.check_input(src, creds, tmpsubs)
        if not crypto.sc_eq(xi, kout):
            raise ValueError("Invariant error")

        td.m_key_image = crypto.encodepoint(ki)

        self.sources.append(src)
        self.selected_transfers.append(len(self.transfers))
        self.transfers.append(td)
        self.sources_creds.append(creds)

        if not crypto.point_eq(
                crypto.decodepoint(src.outputs[src.real_output][1].dest),
                crypto.scalarmult_base(kout)):
            raise ValueError("Invariant error")

        return self