facade/NemFacade.js

/* eslint-disable no-unused-vars */
import { Bip32Node } from "../Bip32.js";
/* eslint-enable no-unused-vars */
import {
  Hash256,
  PrivateKey,
  /* eslint-disable no-unused-vars */
  PublicKey,
  SharedKey256,
  Signature,
  /* eslint-enable no-unused-vars */
} from "../CryptoTypes.js";
import { NetworkLocator } from "../Network.js";
import { KeyPair, Verifier } from "../nem/KeyPair.js";
import { Address, Network as NemNetwork } from "../nem/Network.js";
import { deriveSharedKey } from "../nem/SharedKey.js";
import TransactionFactory from "../nem/TransactionFactory.js";
/* eslint-disable no-unused-vars */
import * as nc from "../nem/models.js";
/* eslint-enable no-unused-vars */
import { keccak_256 } from "@noble/hashes/sha3";

/**
 * Facade used to interact with NEM blockchain.
 */
export default class NemFacade {
  /**
   * BIP32 curve name.
   * @type {string}
   */
  static BIP32_CURVE_NAME = "ed25519-keccak";

  /**
   * Network address class type.
   * @type {Address}
   */
  static Address = Address;

  /**
   * Network key pair class type.
   * @type {KeyPair}
   */
  static KeyPair = KeyPair;

  /**
   * Network verifier class type.
   * @type {Verifier}
   */
  static Verifier = Verifier;

  /**
   * Derives shared key from key pair and other party's public key.
   * @param {KeyPair} keyPair Key pair.
   * @param {PublicKey} otherPublicKey Other party's public key.
   * @returns {SharedKey256} Shared encryption key.
   */
  static deriveSharedKey = deriveSharedKey;

  /**
   * Creates a NEM facade.
   * @param {string|NemNetwork} network NEM network or network name.
   */
  constructor(network) {
    /**
     * Underlying network.
     * @type NemNetwork
     */
    this.network =
      "string" === typeof network
        ? NetworkLocator.findByName(NemNetwork.NETWORKS, network)
        : network;

    /**
     * Underlying transaction factory.
     * @type TransactionFactory
     */
    this.transactionFactory = new TransactionFactory(this.network);
  }

  // the following three functions are NOT static in order for NemFacade and SymbolFacade to conform to the same interface

  /**
   * Hashes a NEM transaction.
   * @param {nc.Transaction} transaction Transaction object.
   * @returns {Hash256} Transaction hash.
   */
  hashTransaction(transaction) {
    // eslint-disable-line class-methods-use-this
    const nonVerifiableTransaction =
      TransactionFactory.toNonVerifiableTransaction(transaction);
    return new Hash256(keccak_256(nonVerifiableTransaction.serialize()));
  }

  /**
   * Signs a NEM transaction.
   * @param {KeyPair} keyPair Key pair.
   * @param {nc.Transaction} transaction Transaction object.
   * @returns {Signature} Transaction signature.
   */
  signTransaction(keyPair, transaction) {
    // eslint-disable-line class-methods-use-this
    const nonVerifiableTransaction =
      TransactionFactory.toNonVerifiableTransaction(transaction);
    return keyPair.sign(nonVerifiableTransaction.serialize());
  }

  /**
   * Verifies a NEM transaction.
   * @param {nc.Transaction} transaction Transaction object.
   * @param {Signature} signature Signature to verify.
   * @returns {boolean} \c true if transaction signature is verified.
   */
  verifyTransaction(transaction, signature) {
    // eslint-disable-line class-methods-use-this
    const nonVerifiableTransaction =
      TransactionFactory.toNonVerifiableTransaction(transaction);
    return new Verifier(transaction.signerPublicKey).verify(
      nonVerifiableTransaction.serialize(),
      signature
    );
  }

  /**
   * Creates a network compatible BIP32 path for the specified account.
   *
   * @param {number} accountId Id of the account for which to generate a BIP32 path.
   * @returns {Array<number>} BIP32 path for the specified account.
   */
  bip32Path(accountId) {
    return [44, "mainnet" === this.network.name ? 43 : 1, accountId, 0, 0];
  }

  /**
   * Derives a NEM KeyPair from a BIP32 node.
   * @param {Bip32Node} bip32Node BIP32 node.
   * @returns {KeyPair} Derived key pair.
   */
  static bip32NodeToKeyPair(bip32Node) {
    // BIP32 private keys should be used as is, so reverse here to counteract reverse in KeyPair
    const reversedPrivateKeyBytes = new Uint8Array([
      ...bip32Node.privateKey.bytes,
    ]);
    reversedPrivateKeyBytes.reverse();

    return new KeyPair(new PrivateKey(reversedPrivateKeyBytes));
  }
}