Source: history.js

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

import { getAllNotes, insertHistory, getHistory } from "./db.js";
import { call } from "./wasm.js";
import { parseEncodedJSON } from "./encoding.js";
import { txFromBlock } from "./graphql.js";
import { getPsks } from "./keys.js";
import { duskToLux } from "./crypto.js";

/**
 * Returns the maximum block height within the list of items given.
 * The items are expected to have a `block_height` property.
 *
 * @param {Array<{block_height: number}>} items
 * @returns {number} the maximum block height found, or 0.
 *
 * It's not possible to use `Math.max` because in certain engine (WebKit)
 * it's recursive, and it will exceed the stack if the arguments are a lot.
 *
 * @see {@link https://stackoverflow.com/questions/42623071/maximum-call-stack-size-exceeded-with-math-min-and-math-max}
 */
const maxBlockHeight = (items) =>
  items.reduce(
    (max, { block_height }) => (block_height > max ? block_height : max),
    0,
  );

/**
 * @class TxData
 * @type {Object}
 * @property {number} amount The amount of the transaction
 * @property {number} block_height The block_height where the tx is
 * @property {string} direction The direction of the tx "In" or "Out"
 * @property {number} fee The fee of the tx
 * @property {string} id The hash of the tx
 * @property {string} tx_type The type of the tx
 */
export function TxData(amount, block_height, direction, fee, id, tx_type) {
  this.amount = amount;
  this.block_height = block_height;
  this.direction = direction;
  this.fee = fee;
  this.id = id;
  this.tx_type = tx_type;
}

/**
 * Fetch the history of the user
 *
 * @param {WebAssembly.Exports} wasm
 * @param {Uint8Array} seed
 * @param {string} psk The psk to get the history for
 * @returns {Array<TxData>} The history of the transactions
 * @ignore Only called by the Wallet object prototype
 */
export async function history(wasm, seed, psk) {
  let histData = await getHistory(psk);

  const lastInsertedBlockHeight = histData.lastBlockHeight;

  histData = histData.history;

  const notes = await getAllNotes(psk);

  const noteBlockHeights = maxBlockHeight(notes);

  if (lastInsertedBlockHeight >= noteBlockHeights) {
    return histData;
  }

  const txData = [];
  const noteData = [];
  const index = (await getPsks(wasm, seed)).indexOf(psk);

  for (const note of notes) {
    const blockHeight = note.block_height;
    const txs = await txFromBlock(blockHeight);

    txData.push({
      txs: txs,
      block_height: blockHeight,
    });

    noteData.push({
      pos: note.pos,
      psk: note.psk,
      note: note.note,
      nullifier: note.nullifier,
      block_height: note.block_height,
    });
  }

  const args = {
    seed: Array.from(seed),
    index: index,
    notes: noteData,
    tx_data: txData,
  };

  const result = await call(wasm, args, "get_history").then(parseEncodedJSON);

  const history = await Promise.all(
    result.history.map(async (tx) => {
      tx.fee = await duskToLux(wasm, parseInt(tx.fee));
      return tx;
    }),
  );

  const lastBlockHeight = maxBlockHeight(histData);

  const historyData = {
    psk: psk,
    history: history,
    lastBlockHeight: lastBlockHeight,
  };

  await insertHistory(historyData);

  return history;
}