PHP WebShell

Текущая директория: /opt/BitGoJS/node_modules/@hashgraph/sdk/lib/transaction

Просмотр файла: Transaction.cjs

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.TRANSACTION_REGISTRY = exports.SCHEDULE_CREATE_TRANSACTION = exports.DEFAULT_RECORD_THRESHOLD = exports.DEFAULT_AUTO_RENEW_PERIOD = exports.CHUNK_SIZE = void 0;
var _Hbar = _interopRequireDefault(require("../Hbar.cjs"));
var _TransactionResponse = _interopRequireDefault(require("./TransactionResponse.cjs"));
var _TransactionId = _interopRequireDefault(require("./TransactionId.cjs"));
var _TransactionHashMap = _interopRequireDefault(require("./TransactionHashMap.cjs"));
var _SignatureMap = _interopRequireDefault(require("./SignatureMap.cjs"));
var _Executable = _interopRequireWildcard(require("../Executable.cjs"));
var _Status = _interopRequireDefault(require("../Status.cjs"));
var _long = _interopRequireDefault(require("long"));
var sha384 = _interopRequireWildcard(require("../cryptography/sha384.cjs"));
var hex = _interopRequireWildcard(require("../encoding/hex.cjs"));
var HashgraphProto = _interopRequireWildcard(require("@hashgraph/proto"));
var _PrecheckStatusError = _interopRequireDefault(require("../PrecheckStatusError.cjs"));
var _AccountId = _interopRequireDefault(require("../account/AccountId.cjs"));
var _PublicKey = _interopRequireDefault(require("../PublicKey.cjs"));
var _List = _interopRequireDefault(require("./List.cjs"));
var _Timestamp = _interopRequireDefault(require("../Timestamp.cjs"));
var util = _interopRequireWildcard(require("../util.cjs"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*-
 * ‌
 * Hedera JavaScript SDK
 * ​
 * Copyright (C) 2020 - 2022 Hedera Hashgraph, LLC
 * ​
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ‍
 */

/**
 * @typedef {import("bignumber.js").default} BigNumber
 */

/**
 * @typedef {import("../schedule/ScheduleCreateTransaction.js").default} ScheduleCreateTransaction
 * @typedef {import("../PrivateKey.js").default} PrivateKey
 * @typedef {import("../channel/Channel.js").default} Channel
 * @typedef {import("../client/Client.js").default<*, *>} Client
 * @typedef {import("../Signer.js").Signer} Signer
 */

// 90 days (in seconds)
const DEFAULT_AUTO_RENEW_PERIOD = _long.default.fromValue(7776000);

// maximum value of i64 (so there is never a record generated)
exports.DEFAULT_AUTO_RENEW_PERIOD = DEFAULT_AUTO_RENEW_PERIOD;
const DEFAULT_RECORD_THRESHOLD = _Hbar.default.fromTinybars(_long.default.fromString("9223372036854775807"));

// 120 seconds
exports.DEFAULT_RECORD_THRESHOLD = DEFAULT_RECORD_THRESHOLD;
const DEFAULT_TRANSACTION_VALID_DURATION = 120;
const CHUNK_SIZE = 1024;

/**
 * @type {Map<NonNullable<HashgraphProto.proto.TransactionBody["data"]>, (transactions: HashgraphProto.proto.ITransaction[], signedTransactions: HashgraphProto.proto.ISignedTransaction[], transactionIds: TransactionId[], nodeIds: AccountId[], bodies: HashgraphProto.proto.TransactionBody[]) => Transaction>}
 */
exports.CHUNK_SIZE = CHUNK_SIZE;
const TRANSACTION_REGISTRY = new Map();

/**
 * Base class for all transactions that may be submitted to Hedera.
 *
 * @abstract
 * @augments {Executable<HashgraphProto.proto.ITransaction, HashgraphProto.proto.ITransactionResponse, TransactionResponse>}
 */
exports.TRANSACTION_REGISTRY = TRANSACTION_REGISTRY;
class Transaction extends _Executable.default {
  // A SDK transaction is composed of multiple, raw protobuf transactions.
  // These should be functionally identical, with the exception of pointing to
  // different nodes.

  // When retrying a transaction after a network error or retry-able
  // status response, we try a different transaction and thus a different node.

  constructor() {
    super();

    /**
     * List of proto transactions that have been built from this SDK
     * transaction.
     *
     * This is a 2-D array built into one, meaning to
     * get to the next row you'd index into this array `row * rowLength + column`
     * where `rowLength` is `nodeAccountIds.length`
     *
     * @internal
     * @type {List<HashgraphProto.proto.ITransaction | null>}
     */
    this._transactions = new _List.default();

    /**
     * List of proto transactions that have been built from this SDK
     * transaction.
     *
     * This is a 2-D array built into one, meaning to
     * get to the next row you'd index into this array `row * rowLength + column`
     * where `rowLength` is `nodeAccountIds.length`
     *
     * @internal
     * @type {List<HashgraphProto.proto.ISignedTransaction>}
     */
    this._signedTransactions = new _List.default();

    /**
     * Set of public keys (as string) who have signed this transaction so
     * we do not allow them to sign it again.
     *
     * @internal
     * @type {Set<string>}
     */
    this._signerPublicKeys = new Set();

    /**
     * The transaction valid duration
     *
     * @private
     * @type {number}
     */
    this._transactionValidDuration = DEFAULT_TRANSACTION_VALID_DURATION;

    /**
     * The default max transaction fee for this particular transaction type.
     * Most transactions use the default of 2 Hbars, but some requests such
     * as `TokenCreateTransaction` need to use a different default value.
     *
     * @protected
     * @type {Hbar}
     */
    this._defaultMaxTransactionFee = new _Hbar.default(2);

    /**
     * The max transaction fee on the request. This field is what users are able
     * to set, not the `defaultMaxTransactionFee`. The purpose of this field is
     * to allow us to determine if the user set the field explicitly, or if we're
     * using the default max transation fee for the request.
     *
     * @private
     * @type {Hbar | null}
     */
    this._maxTransactionFee = null;

    /**
     * The transaction's memo
     *
     * @private
     * @type {string}
     */
    this._transactionMemo = "";

    /**
     * The list of transaction IDs. This list will almost always be of length 1.
     * The only time this list will be a different length is for chunked transactions.
     * The only two chunked transactions supported right now are `FileAppendTransaction`
     * and `TopicMessageSubmitTransaction`
     *
     * @protected
     * @type {List<TransactionId>}
     */
    this._transactionIds = new _List.default();

    /**
     * A list of public keys that will be added to the requests signatures
     *
     * @private
     * @type {PublicKey[]}
     */
    this._publicKeys = [];

    /**
     * The list of signing function 1-1 with `_publicKeys` which sign the request.
     * The reason this list allows `null` is because if we go from bytes into
     * a transaction, then we know the public key, but we don't have the signing function.
     *
     * @private
     * @type {(((message: Uint8Array) => Promise<Uint8Array>) | null)[]}
     */
    this._transactionSigners = [];

    /**
     * Determine if we should regenerate transaction IDs when we receive `TRANSACITON_EXPIRED`
     *
     * @private
     * @type {?boolean}
     */
    this._regenerateTransactionId = null;
  }

  /**
   * Deserialize a transaction from bytes. The bytes can either be a `proto.Transaction` or
   * `proto.TransactionList`.
   *
   * @param {Uint8Array} bytes
   * @returns {Transaction}
   */
  static fromBytes(bytes) {
    const signedTransactions = [];
    const transactionIds = [];
    const nodeIds = [];

    /** @type {string[]} */
    const transactionIdStrings = [];

    /** @type {string[]} */
    const nodeIdStrings = [];
    const bodies = [];
    const list = HashgraphProto.proto.TransactionList.decode(bytes).transactionList;

    // If the list is of length 0, then teh bytes provided were not a
    // `proto.TransactionList`
    //
    // FIXME: We should also check to make sure the bytes length is greater than
    // 0 otherwise this check is wrong?
    if (list.length === 0) {
      const transaction = HashgraphProto.proto.Transaction.decode(bytes);

      // We support `Transaction.signedTransactionBytes` and
      // `Transaction.bodyBytes` + `Transaction.sigMap`. If the bytes represent the
      // latter, convert them into `signedTransactionBytes`
      if (transaction.signedTransactionBytes.length !== 0) {
        list.push(transaction);
      } else {
        list.push({
          signedTransactionBytes: HashgraphProto.proto.SignedTransaction.encode({
            bodyBytes: transaction.bodyBytes,
            sigMap: transaction.sigMap
          }).finish()
        });
      }
    }

    // This loop is responsible for fill out the `signedTransactions`, `transactionIds`,
    // `nodeIds`, and `bodies` variables.
    for (const transaction of list) {
      // The `signedTransactionBytes` should not be null
      if (transaction.signedTransactionBytes == null) {
        throw new Error("Transaction.signedTransactionBytes are null");
      }

      // Decode a signed transaction
      const signedTransaction = HashgraphProto.proto.SignedTransaction.decode(transaction.signedTransactionBytes);
      signedTransactions.push(signedTransaction);

      // Decode a transaction body
      const body = HashgraphProto.proto.TransactionBody.decode(signedTransaction.bodyBytes);

      // Make sure the body is set
      if (body.data == null) {
        throw new Error("(BUG) body.data was not set in the protobuf");
      }
      bodies.push(body);

      // Make sure the transaction ID within the body is set
      if (body.transactionID != null) {
        const transactionId = _TransactionId.default._fromProtobuf( /** @type {HashgraphProto.proto.ITransactionID} */
        body.transactionID);

        // If we haven't already seen this transaction ID in the list, add it
        if (!transactionIdStrings.includes(transactionId.toString())) {
          transactionIds.push(transactionId);
          transactionIdStrings.push(transactionId.toString());
        }
      }

      // Make sure the node account ID within the body is set
      if (body.nodeAccountID != null) {
        const nodeAccountId = _AccountId.default._fromProtobuf( /** @type {HashgraphProto.proto.IAccountID} */
        body.nodeAccountID);

        // If we haven't already seen this node account ID in the list, add it
        if (!nodeIdStrings.includes(nodeAccountId.toString())) {
          nodeIds.push(nodeAccountId);
          nodeIdStrings.push(nodeAccountId.toString());
        }
      }
    }

    // FIXME: We should have a length check before we access `0` since that would error
    const body = bodies[0];

    // We should have at least more than one body
    if (body == null || body.data == null) {
      throw new Error("No transaction found in bytes or failed to decode TransactionBody");
    }

    // Use the registry to call the right transaction's `fromProtobuf` method based
    // on the `body.data` string
    const fromProtobuf = TRANSACTION_REGISTRY.get(body.data); //NOSONAR

    // If we forgot to update the registry we should error
    if (fromProtobuf == null) {
      throw new Error(`(BUG) Transaction.fromBytes() not implemented for type ${body.data}`);
    }

    // That the specific transaction type from protobuf implementation and pass in all the
    // information we've gathered.
    return fromProtobuf(list, signedTransactions, transactionIds, nodeIds, bodies);
  }

  /**
   * Convert this transaction a `ScheduleCreateTransaction`
   *
   * @returns {ScheduleCreateTransaction}
   */
  schedule() {
    this._requireNotFrozen();
    if (SCHEDULE_CREATE_TRANSACTION.length != 1) {
      throw new Error("ScheduleCreateTransaction has not been loaded yet");
    }
    return SCHEDULE_CREATE_TRANSACTION[0]()._setScheduledTransaction(this);
  }

  /**
   * This method is called by each `*Transaction._fromProtobuf()` method. It does
   * all the finalization before the user gets hold of a complete `Transaction`
   *
   * @template {Transaction} TransactionT
   * @param {TransactionT} transaction
   * @param {HashgraphProto.proto.ITransaction[]} transactions
   * @param {HashgraphProto.proto.ISignedTransaction[]} signedTransactions
   * @param {TransactionId[]} transactionIds
   * @param {AccountId[]} nodeIds
   * @param {HashgraphProto.proto.ITransactionBody[]} bodies
   * @returns {TransactionT}
   */
  static _fromProtobufTransactions(transaction, transactions, signedTransactions, transactionIds, nodeIds, bodies) {
    const body = bodies[0];

    // "row" of the 2-D `bodies` array has all the same contents except for `nodeAccountID`
    for (let i = 0; i < transactionIds.length; i++) {
      for (let j = 0; j < nodeIds.length - 1; j++) {
        if (!util.compare(bodies[i * nodeIds.length + j], bodies[i * nodeIds.length + j + 1],
        // eslint-disable-next-line ie11/no-collection-args
        new Set(["nodeAccountID"]))) {
          throw new Error("failed to validate transaction bodies");
        }
      }
    }

    // Remove node account IDs of 0
    // _IIRC_ this was initial due to some funny behavior with `ScheduleCreateTransaction`
    // We may be able to remove this.
    const zero = new _AccountId.default(0);
    for (let i = 0; i < nodeIds.length; i++) {
      if (nodeIds[i].equals(zero)) {
        nodeIds.splice(i--, 1);
      }
    }

    // Set the transactions accordingly, but don't lock the list because transactions can
    // be regenerated if more signatures are added
    transaction._transactions.setList(transactions);

    // Set the signed transactions accordingly, and lock the list since signed transaction
    // will not be regenerated. Although, they can be manipulated if for instance more
    // signatures are added
    transaction._signedTransactions.setList(signedTransactions).setLocked();

    // Set the transaction IDs accordingly, and lock the list. Transaction IDs should not
    // be regenerated if we're deserializing a request from bytes
    transaction._transactionIds.setList(transactionIds).setLocked();

    // Set the node account IDs accordingly, and lock the list. Node account IDs should
    // never be changed if we're deserializing a request from bytes
    transaction._nodeAccountIds.setList(nodeIds).setLocked();

    // Make sure to update the rest of the fields
    transaction._transactionValidDuration = body.transactionValidDuration != null && body.transactionValidDuration.seconds != null ? _long.default.fromValue(body.transactionValidDuration.seconds).toInt() : DEFAULT_TRANSACTION_VALID_DURATION;
    transaction._maxTransactionFee = body.transactionFee != null ? _Hbar.default.fromTinybars(body.transactionFee) : new _Hbar.default(0);
    transaction._transactionMemo = body.memo != null ? body.memo : "";

    // Loop over a single row of `signedTransactions` and add all the public
    // keys to the `signerPublicKeys` set, and `publicKeys` list with
    // `null` in the `transactionSigners` at the same index.
    for (let i = 0; i < nodeIds.length; i++) {
      const signedTransaction = signedTransactions[i];
      if (signedTransaction.sigMap != null && signedTransaction.sigMap.sigPair != null) {
        for (const sigPair of signedTransaction.sigMap.sigPair) {
          transaction._signerPublicKeys.add(hex.encode( /** @type {Uint8Array} */sigPair.pubKeyPrefix));
          transaction._publicKeys.push(_PublicKey.default.fromBytes( /** @type {Uint8Array} */sigPair.pubKeyPrefix));
          transaction._transactionSigners.push(null);
        }
      }
    }
    return transaction;
  }

  /**
   * Set the node account IDs
   *
   * @override
   * @param {AccountId[]} nodeIds
   * @returns {this}
   */
  setNodeAccountIds(nodeIds) {
    // The reason we overwrite this method is simply because we need to call `requireNotFrozen()`
    // Now that I think of it, we could just add an abstract method `setterPrerequiest()` which
    // by default does nothing, and `Executable` can call. Then we'd only need to overwrite that
    // method once.
    this._requireNotFrozen();
    super.setNodeAccountIds(nodeIds);
    return this;
  }

  /**
   * Get the transaction valid duration
   *
   * @returns {number}
   */
  get transactionValidDuration() {
    return this._transactionValidDuration;
  }

  /**
   * Sets the duration (in seconds) that this transaction is valid for.
   *
   * This is defaulted to 120 seconds (from the time its executed).
   *
   * @param {number} validDuration
   * @returns {this}
   */
  setTransactionValidDuration(validDuration) {
    this._requireNotFrozen();
    this._transactionValidDuration = validDuration;
    return this;
  }

  /**
   * Get the max transaction fee
   *
   * @returns {?Hbar}
   */
  get maxTransactionFee() {
    return this._maxTransactionFee;
  }

  /**
   * Set the maximum transaction fee the operator (paying account)
   * is willing to pay.
   *
   * @param {number | string | Long | BigNumber | Hbar} maxTransactionFee
   * @returns {this}
   */
  setMaxTransactionFee(maxTransactionFee) {
    this._requireNotFrozen();
    this._maxTransactionFee = maxTransactionFee instanceof _Hbar.default ? maxTransactionFee : new _Hbar.default(maxTransactionFee);
    return this;
  }

  /**
   * Is transaction ID regeneration enabled
   *
   * @returns {?boolean}
   */
  get regenerateTransactionId() {
    return this._regenerateTransactionId;
  }

  /**
   * Set the maximum transaction fee the operator (paying account)
   * is willing to pay.
   *
   * @param {boolean} regenerateTransactionId
   * @returns {this}
   */
  setRegenerateTransactionId(regenerateTransactionId) {
    this._requireNotFrozen();
    this._regenerateTransactionId = regenerateTransactionId;
    return this;
  }

  /**
   * Get the transaction memo
   *
   * @returns {string}
   */
  get transactionMemo() {
    return this._transactionMemo;
  }

  /**
   * Set a note or description to be recorded in the transaction
   * record (maximum length of 100 bytes).
   *
   * @param {string} transactionMemo
   * @returns {this}
   */
  setTransactionMemo(transactionMemo) {
    this._requireNotFrozen();
    this._transactionMemo = transactionMemo;
    return this;
  }

  /**
   * Get the curent transaction ID
   *
   * @returns {?TransactionId}
   */
  get transactionId() {
    if (this._transactionIds.isEmpty) {
      return null;
    }

    // If a user calls `.transactionId` that means we need to use that transaction ID
    // and **not** regenerate it. To do this, we simply lock the transaction ID list.
    //
    // This may be a little conffusing since a user can enable transaction ID regenration
    // explicity, but if they call `.transactionId` then we will not regenerate transaction
    // IDs.
    this._transactionIds.setLocked();
    return this._transactionIds.current;
  }

  /**
   * Set the ID for this transaction.
   *
   * The transaction ID includes the operator's account ( the account paying the transaction
   * fee). If two transactions have the same transaction ID, they won't both have an effect. One
   * will complete normally and the other will fail with a duplicate transaction status.
   *
   * Normally, you should not use this method. Just before a transaction is executed, a
   * transaction ID will be generated from the operator on the client.
   *
   * @param {TransactionId} transactionId
   * @returns {this}
   */
  setTransactionId(transactionId) {
    this._requireNotFrozen();
    this._transactionIds.setList([transactionId]).setLocked();
    return this;
  }

  /**
   * Sign the transaction with the private key
   * **NOTE**: This is a thin wrapper around `.signWith()`
   *
   * @param {PrivateKey} privateKey
   * @returns {Promise<this>}
   */
  sign(privateKey) {
    return this.signWith(privateKey.publicKey, message => Promise.resolve(privateKey.sign(message)));
  }

  /**
   * Sign the transaction with the public key and signer function
   *
   * If sign on demand is enabled no signing will be done immediately, instead
   * the private key signing function and public key are saved to be used when
   * a user calls an exit condition method (not sure what a better name for this is)
   * such as `toBytes[Async]()`, `getTransactionHash[PerNode]()` or `execute()`.
   *
   * @param {PublicKey} publicKey
   * @param {(message: Uint8Array) => Promise<Uint8Array>} transactionSigner
   * @returns {Promise<this>}
   */
  async signWith(publicKey, transactionSigner) {
    // If signing on demand is disabled, we need to make sure
    // the request is frozen
    if (!this._signOnDemand) {
      this._requireFrozen();
    }
    const publicKeyData = publicKey.toBytesRaw();

    // note: this omits the DER prefix on purpose because Hedera doesn't
    // support that in the protobuf. this means that we would fail
    // to re-inflate [this._signerPublicKeys] during [fromBytes] if we used DER
    // prefixes here
    const publicKeyHex = hex.encode(publicKeyData);
    if (this._signerPublicKeys.has(publicKeyHex)) {
      // this public key has already signed this transaction
      return this;
    }

    // If we add a new signer, then we need to re-create all transactions
    this._transactions.clear();

    // Save the current public key so we don't attempt to sign twice
    this._signerPublicKeys.add(publicKeyHex);

    // If signing on demand is enabled we will save the public key and signer and return
    if (this._signOnDemand) {
      this._publicKeys.push(publicKey);
      this._transactionSigners.push(transactionSigner);
      return this;
    }

    // If we get here, signing on demand is disabled, this means the transaction
    // is frozen and we need to sign all the transactions immediately. If we're
    // signing all the transactions immediately, we need to lock the node account IDs
    // and transaction IDs.
    // Now that I think of it, this code should likely exist in `freezeWith()`?
    this._transactionIds.setLocked();
    this._nodeAccountIds.setLocked();

    // Sign each signed transatcion
    for (const signedTransaction of this._signedTransactions.list) {
      const bodyBytes = /** @type {Uint8Array} */
      signedTransaction.bodyBytes;
      const signature = await transactionSigner(bodyBytes);
      if (signedTransaction.sigMap == null) {
        signedTransaction.sigMap = {};
      }
      if (signedTransaction.sigMap.sigPair == null) {
        signedTransaction.sigMap.sigPair = [];
      }
      signedTransaction.sigMap.sigPair.push(publicKey._toProtobufSignature(signature));
    }
    return this;
  }

  /**
   * Sign the transaction with the client operator. This is a thin wrapper
   * around `.signWith()`
   *
   * **NOTE**: If client does not have an operator set, this method will throw
   *
   * @param {import("../client/Client.js").default<Channel, *>} client
   * @returns {Promise<this>}
   */
  signWithOperator(client) {
    const operator = client._operator;
    if (operator == null) {
      throw new Error("`client` must have an operator to sign with the operator");
    }
    if (!this._isFrozen()) {
      this.freezeWith(client);
    }
    return this.signWith(operator.publicKey, operator.transactionSigner);
  }

  /**
   * Add a signature explicitly
   *
   * This method requires the transaction to have exactly 1 node account ID set
   * since different node account IDs have different byte representations and
   * hence the same signature would not work for all transactions that are the same
   * except for node account ID being different.
   *
   * @param {PublicKey} publicKey
   * @param {Uint8Array} signature
   * @returns {this}
   */
  addSignature(publicKey, signature) {
    // Require that only one node is set on this transaction
    // FIXME: This doesn't consider if we have one node account ID set, but we're
    // also a chunked transaction. We should also check transaction IDs is of length 1
    this._requireOneNodeAccountId();

    // If the transaction isn't frozen, freeze it.
    if (!this.isFrozen()) {
      this.freeze();
    }
    const publicKeyData = publicKey.toBytesRaw();
    const publicKeyHex = hex.encode(publicKeyData);
    if (this._signerPublicKeys.has(publicKeyHex)) {
      // this public key has already signed this transaction
      return this;
    }

    // Transactions will have to be regenerated
    this._transactions.clear();

    // Locking the transaction IDs and node account IDs is necessary for consistency
    // between before and after execution
    this._transactionIds.setLocked();
    this._nodeAccountIds.setLocked();
    this._signedTransactions.setLocked();

    // Add the signature to the signed transaction list. This is a copy paste
    // of `.signWith()`, but it really shouldn't be if `_signedTransactions.list`
    // must be a length of one.
    // FIXME: Remove unnecessary for loop.
    for (const transaction of this._signedTransactions.list) {
      if (transaction.sigMap == null) {
        transaction.sigMap = {};
      }
      if (transaction.sigMap.sigPair == null) {
        transaction.sigMap.sigPair = [];
      }
      transaction.sigMap.sigPair.push(publicKey._toProtobufSignature(signature));
    }
    this._signerPublicKeys.add(publicKeyHex);
    this._publicKeys.push(publicKey);
    this._transactionSigners.push(null);
    return this;
  }

  /**
   * Get the current signatures on the request
   *
   * **NOTE**: Does NOT support sign on demand
   *
   * @returns {SignatureMap}
   */
  getSignatures() {
    // If a user is attempting to get signatures for a transaction, then the
    // transaction must be frozen.
    this._requireFrozen();

    // Sign on demand must be disabled because this is the non-async version and
    // signing requires awaiting callbacks.
    this._requireNotSignOnDemand();

    // Build all the transactions
    this._buildAllTransactions();

    // Lock transaction IDs, and node account IDs
    this._transactionIds.setLocked();
    this._nodeAccountIds.setLocked();

    // Construct a signature map from this transaction
    return _SignatureMap.default._fromTransaction(this);
  }

  /**
   * Get the current signatures on the request
   *
   * **NOTE**: Supports sign on demand
   *
   * @returns {Promise<SignatureMap>}
   */
  async getSignaturesAsync() {
    // If sign on demand is enabled, we don't need to care about being frozen
    // since we can just regenerate and resign later if some field of the transaction
    // changes.

    // Locking the transaction IDs and node account IDs is necessary for consistency
    // between before and after execution
    this._transactionIds.setLocked();
    this._nodeAccountIds.setLocked();

    // Build all transactions, and sign them
    await this._buildAllTransactionsAsync();

    // Lock transaction IDs, and node account IDs
    this._transactions.setLocked();
    this._signedTransactions.setLocked();

    // Construct a signature map from this transaction
    return _SignatureMap.default._fromTransaction(this);
  }

  /**
   * Not sure why this is called `setTransactionId()` when it doesn't set anything...
   * FIXME: Remove this?
   */
  _setTransactionId() {
    if (this._operatorAccountId == null && this._transactionIds.isEmpty) {
      throw new Error("`transactionId` must be set or `client` must be provided with `freezeWith`");
    }
  }

  /**
   * Set the node account IDs using the client
   *
   * @param {?import("../client/Client.js").default<Channel, *>} client
   */
  _setNodeAccountIds(client) {
    if (!this._nodeAccountIds.isEmpty) {
      return;
    }
    if (client == null) {
      throw new Error("`nodeAccountId` must be set or `client` must be provided with `freezeWith`");
    }
    this._nodeAccountIds.setList(client._network.getNodeAccountIdsForExecute());
  }

  /**
   * Build all the signed transactions from the node account IDs
   *
   * @private
   */
  _buildSignedTransactions() {
    if (this._signedTransactions.locked) {
      return;
    }
    this._signedTransactions.setList(this._nodeAccountIds.list.map(nodeId => this._makeSignedTransaction(nodeId)));
  }

  /**
   * Freeze this transaction from future modification to prepare for
   * signing or serialization.
   *
   * @returns {this}
   */
  freeze() {
    return this.freezeWith(null);
  }

  /**
   * @param {?AccountId} accountId
   */
  _freezeWithAccountId(accountId) {
    if (this._operatorAccountId == null) {
      this._operatorAccountId = accountId;
    }
  }

  /**
   * Freeze this transaction from further modification to prepare for
   * signing or serialization.
   *
   * Will use the `Client`, if available, to generate a default Transaction ID and select 1/3
   * nodes to prepare this transaction for.
   *
   * @param {?import("../client/Client.js").default<Channel, *>} client
   * @returns {this}
   */
  freezeWith(client) {
    // Set sign on demand based on client
    this._signOnDemand = client != null ? client.signOnDemand : false;

    // Save the operator
    this._operator = client != null ? client._operator : null;
    this._freezeWithAccountId(client != null ? client.operatorAccountId : null);

    // Set max transaction fee to either `this._maxTransactionFee`,
    // `client._defaultMaxTransactionFee`, or `this._defaultMaxTransactionFee`
    // in that priority order depending on if `this._maxTransactionFee` has
    // been set or if `client._defaultMaxTransactionFee` has been set.
    this._maxTransactionFee = this._maxTransactionFee == null ? client != null && client.defaultMaxTransactionFee != null ? client.defaultMaxTransactionFee : this._defaultMaxTransactionFee : this._maxTransactionFee;

    // Determine if transaction ID generation should be enabled.
    this._regenerateTransactionId = client != null && this._regenerateTransactionId == null ? client.defaultRegenerateTransactionId : this._regenerateTransactionId;

    // Set the node account IDs via client
    this._setNodeAccountIds(client);

    // Make sure a transaction ID or operator is set.
    this._setTransactionId();

    // If a client was not provided, we need to make sure the transaction ID already set
    // validates aginst the client.
    if (client != null) {
      for (const transactionId of this._transactionIds.list) {
        if (transactionId.accountId != null) {
          transactionId.accountId.validateChecksum(client);
        }
      }
    }

    // Build a list of transaction IDs so that if a user calls `.transactionId` they'll
    // get a value, but if they dont' we'll just regenerate transaction IDs during execution
    this._buildNewTransactionIdList();

    // If sign on demand is disabled we need to build out all the signed transactions
    if (!this._signOnDemand) {
      this._buildSignedTransactions();
    }
    return this;
  }

  /**
   * Sign the transaction using a signer
   *
   * This is part of the signature provider feature
   *
   * @param {Signer} signer
   * @returns {Promise<this>}
   */
  async signWithSigner(signer) {
    await signer.signTransaction(this);
    return this;
  }

  /**
   * Freeze the transaction using a signer
   *
   * This is part of the signature provider feature.
   *
   * @param {Signer} signer
   * @returns {Promise<this>}
   */
  async freezeWithSigner(signer) {
    await signer.populateTransaction(this);
    this.freeze();
    return this;
  }

  /**
   * Serialize the request into bytes. This will encode all the transactions
   * into a `proto.TransactionList` and return the encoded protobuf.
   *
   * **NOTE**: Does not support sign on demand
   *
   * @returns {Uint8Array}
   */
  toBytes() {
    // If a user is attempting to serialize a transaction into bytes, then the
    // transaction must be frozen.
    this._requireFrozen();

    // Sign on demand must be disabled because this is the non-async version and
    // signing requires awaiting callbacks.
    this._requireNotSignOnDemand();

    // Locking the transaction IDs and node account IDs is necessary for consistency
    // between before and after execution
    this._transactionIds.setLocked();
    this._nodeAccountIds.setLocked();

    // Build all the transactions withot signing
    this._buildAllTransactions();

    // Construct and encode the transaction list
    return HashgraphProto.proto.TransactionList.encode({
      transactionList: /** @type {HashgraphProto.proto.ITransaction[]} */
      this._transactions.list
    }).finish();
  }

  /**
   * Serialize the transaction into bytes
   *
   * **NOTE**: Supports sign on demand
   *
   * @returns {Promise<Uint8Array>}
   */
  async toBytesAsync() {
    // If sign on demand is enabled, we don't need to care about being frozen
    // since we can just regenerate and resign later if some field of the transaction
    // changes.

    // Locking the transaction IDs and node account IDs is necessary for consistency
    // between before and after execution
    this._transactionIds.setLocked();
    this._nodeAccountIds.setLocked();

    // Build all transactions, and sign them
    await this._buildAllTransactionsAsync();

    // Lock transaction IDs, and node account IDs
    this._transactions.setLocked();
    this._signedTransactions.setLocked();

    // Construct and encode the transaction list
    return HashgraphProto.proto.TransactionList.encode({
      transactionList: /** @type {HashgraphProto.proto.ITransaction[]} */
      this._transactions.list
    }).finish();
  }

  /**
   * Get the transaction hash
   *
   * @returns {Promise<Uint8Array>}
   */
  async getTransactionHash() {
    this._requireFrozen();

    // Locking the transaction IDs and node account IDs is necessary for consistency
    // between before and after execution
    this._transactionIds.setLocked();
    this._nodeAccountIds.setLocked();
    await this._buildAllTransactionsAsync();
    this._transactions.setLocked();
    this._signedTransactions.setLocked();
    return sha384.digest( /** @type {Uint8Array} */
    /** @type {HashgraphProto.proto.ITransaction} */this._transactions.get(0).signedTransactionBytes);
  }

  /**
   * Get all the transaction hashes
   *
   * @returns {Promise<TransactionHashMap>}
   */
  async getTransactionHashPerNode() {
    this._requireFrozen();

    // Locking the transaction IDs and node account IDs is necessary for consistency
    // between before and after execution
    this._transactionIds.setLocked();
    this._nodeAccountIds.setLocked();
    await this._buildAllTransactionsAsync();
    return await _TransactionHashMap.default._fromTransaction(this);
  }

  /**
   * Is transaction frozen
   *
   * @returns {boolean}
   */
  isFrozen() {
    return this._signedTransactions.length > 0;
  }

  /**
   * Get the current transaction ID, and make sure it's not null
   *
   * @protected
   * @returns {TransactionId}
   */
  _getTransactionId() {
    const transactionId = this.transactionId;
    if (transactionId == null) {
      throw new Error("transaction must have been frozen before getting the transaction ID, try calling `freeze`");
    }
    return transactionId;
  }

  /**
   * @param {Client} client
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function
  _validateChecksums(client) {
    // Do nothing
  }

  /**
   * Before we proceed exeuction, we need to do a couple checks
   *
   * @override
   * @protected
   * @param {import("../client/Client.js").default<Channel, *>} client
   * @returns {Promise<void>}
   */
  async _beforeExecute(client) {
    if (this._logger) {
      this._logger.info(`Network used: ${client._network.networkName}` // eslint-disable-line @typescript-eslint/restrict-template-expressions
      );
    }

    // Make sure we're frozen
    if (!this._isFrozen()) {
      this.freezeWith(client);
    }

    // Valid checksums if the option is enabled
    if (client.isAutoValidateChecksumsEnabled()) {
      this._validateChecksums(client);
    }

    // Set the operator if the client has one and the current operator is nullish
    if (this._operator == null || this._operator == undefined) {
      this._operator = client != null ? client._operator : null;
    }
    if (this._operatorAccountId == null || this._operatorAccountId == undefined) {
      this._operatorAccountId = client != null && client._operator != null ? client._operator.accountId : null;
    }

    // If the client has an operator, sign this request with the operator
    if (this._operator != null) {
      await this.signWith(this._operator.publicKey, this._operator.transactionSigner);
    }
  }

  /**
   * Construct a protobuf transaction
   *
   * @override
   * @internal
   * @returns {Promise<HashgraphProto.proto.ITransaction>}
   */
  async _makeRequestAsync() {
    // The index for the transaction
    const index = this._transactionIds.index * this._nodeAccountIds.length + this._nodeAccountIds.index;

    // If sign on demand is disabled we need to simply build that transaction
    // and return the result, without signing
    if (!this._signOnDemand) {
      this._buildTransaction(index);
      return (/** @type {HashgraphProto.proto.ITransaction} */
        this._transactions.get(index)
      );
    }

    // Build and sign a transaction
    return await this._buildTransactionAsync();
  }

  /**
   * Sign a `proto.SignedTransaction` with all the keys
   *
   * @private
   * @returns {Promise<HashgraphProto.proto.ISignedTransaction>}
   */
  async _signTransaction() {
    const signedTransaction = this._makeSignedTransaction(this._nodeAccountIds.next);
    const bodyBytes = /** @type {Uint8Array} */
    signedTransaction.bodyBytes;
    for (let j = 0; j < this._publicKeys.length; j++) {
      const publicKey = this._publicKeys[j];
      const transactionSigner = this._transactionSigners[j];
      if (transactionSigner == null) {
        continue;
      }
      const signature = await transactionSigner(bodyBytes);
      if (signedTransaction.sigMap == null) {
        signedTransaction.sigMap = {};
      }
      if (signedTransaction.sigMap.sigPair == null) {
        signedTransaction.sigMap.sigPair = [];
      }
      signedTransaction.sigMap.sigPair.push(publicKey._toProtobufSignature(signature));
    }
    return signedTransaction;
  }

  /**
   * Construct a new transaction ID at the current index
   *
   * @private
   */
  _buildNewTransactionIdList() {
    if (this._transactionIds.locked || this._operatorAccountId == null) {
      return;
    }
    const transactionId = _TransactionId.default.withValidStart(this._operatorAccountId, _Timestamp.default.generate());
    this._transactionIds.set(this._transactionIds.index, transactionId);
  }

  /**
   * Build each transaction in a loop
   *
   * @private
   */
  _buildAllTransactions() {
    for (let i = 0; i < this._signedTransactions.length; i++) {
      this._buildTransaction(i);
    }
  }

  /**
   * Build and and sign each transaction in a loop
   *
   * This method is primary used in the exist condition methods
   * which are not `execute()`, e.g. `toBytesAsync()` and `getSignaturesAsync()`
   *
   * @private
   */
  async _buildAllTransactionsAsync() {
    if (!this._signOnDemand) {
      this._buildAllTransactions();
      return;
    }
    this._buildSignedTransactions();
    if (this._transactions.locked) {
      return;
    }
    for (let i = 0; i < this._signedTransactions.length; i++) {
      this._transactions.push(await this._buildTransactionAsync());
    }
  }

  /**
   * Build a transaction at a particular index
   *
   * @private
   * @param {number} index
   */
  _buildTransaction(index) {
    if (this._transactions.length < index) {
      for (let i = this._transactions.length; i < index; i++) {
        this._transactions.push(null);
      }
    }
    this._transactions.setIfAbsent(index, () => {
      return {
        signedTransactionBytes: HashgraphProto.proto.SignedTransaction.encode(this._signedTransactions.get(index)).finish()
      };
    });
  }

  /**
   * Build a trransaction using the current index, where the current
   * index is determined by `this._nodeAccountIds.index` and
   * `this._transactionIds.index`
   *
   * @private
   * @returns {Promise<HashgraphProto.proto.ITransaction>}
   */
  async _buildTransactionAsync() {
    return {
      signedTransactionBytes: HashgraphProto.proto.SignedTransaction.encode(await this._signTransaction()).finish()
    };
  }

  /**
   * Determine what execution state we're in.
   *
   * @override
   * @internal
   * @param {HashgraphProto.proto.ITransaction} request
   * @param {HashgraphProto.proto.ITransactionResponse} response
   * @returns {[Status, ExecutionState]}
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  _shouldRetry(request, response) {
    const {
      nodeTransactionPrecheckCode
    } = response;

    // Get the node precheck code, and convert it into an SDK `Status`
    const status = _Status.default._fromCode(nodeTransactionPrecheckCode != null ? nodeTransactionPrecheckCode : HashgraphProto.proto.ResponseCodeEnum.OK);
    if (this._logger) {
      this._logger.debug(`[${this._getLogId()}] received status ${status.toString()}`);
      this._logger.info(`SDK Transaction Status Response: ${status.toString()}`);
    }

    // Based on the status what execution state are we in
    switch (status) {
      case _Status.default.Busy:
      case _Status.default.Unknown:
      case _Status.default.PlatformTransactionNotCreated:
      case _Status.default.PlatformNotActive:
        return [status, _Executable.ExecutionState.Retry];
      case _Status.default.Ok:
        return [status, _Executable.ExecutionState.Finished];
      case _Status.default.TransactionExpired:
        if (this._regenerateTransactionId == null || this._regenerateTransactionId) {
          this._buildNewTransactionIdList();
          return [status, _Executable.ExecutionState.Retry];
        } else {
          return [status, _Executable.ExecutionState.Error];
        }
      default:
        return [status, _Executable.ExecutionState.Error];
    }
  }

  /**
   * Map the request and response into a precheck status error
   *
   * @override
   * @internal
   * @param {HashgraphProto.proto.ITransaction} request
   * @param {HashgraphProto.proto.ITransactionResponse} response
   * @returns {Error}
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  _mapStatusError(request, response) {
    const {
      nodeTransactionPrecheckCode
    } = response;
    const status = _Status.default._fromCode(nodeTransactionPrecheckCode != null ? nodeTransactionPrecheckCode : HashgraphProto.proto.ResponseCodeEnum.OK);
    if (this._logger) {
      this._logger.info(
      // @ts-ignore
      `Transaction Error Info: ${status.toString()}, ${this.transactionId.toString()}` // eslint-disable-line @typescript-eslint/restrict-template-expressions
      );
    }

    return new _PrecheckStatusError.default({
      status,
      transactionId: this._getTransactionId(),
      contractFunctionResult: null
    });
  }

  /**
   * Map the request, response, and node account ID into a `TransactionResponse`
   *
   * @override
   * @protected
   * @param {HashgraphProto.proto.ITransactionResponse} response
   * @param {AccountId} nodeId
   * @param {HashgraphProto.proto.ITransaction} request
   * @returns {Promise<TransactionResponse>}
   */
  async _mapResponse(response, nodeId, request) {
    const transactionHash = await sha384.digest( /** @type {Uint8Array} */request.signedTransactionBytes);
    const transactionId = this._getTransactionId();
    this._transactionIds.advance();
    if (this._logger) {
      this._logger.info(`Transaction Info: ${JSON.stringify(new _TransactionResponse.default({
        nodeId,
        transactionHash,
        transactionId
      }).toJSON())}`);
    }
    return new _TransactionResponse.default({
      nodeId,
      transactionHash,
      transactionId
    });
  }

  /**
   * Make a signed transaction given a node account ID
   *
   * @internal
   * @param {?AccountId} nodeId
   * @returns {HashgraphProto.proto.ISignedTransaction}
   */
  _makeSignedTransaction(nodeId) {
    const body = this._makeTransactionBody(nodeId);
    if (this._logger) {
      this._logger.info(`Transaction Body: ${JSON.stringify(body)}`);
    }
    const bodyBytes = HashgraphProto.proto.TransactionBody.encode(body).finish();
    return {
      bodyBytes,
      sigMap: {
        sigPair: []
      }
    };
  }

  /**
   * Make a protobuf transaction body
   *
   * @private
   * @param {?AccountId} nodeId
   * @returns {HashgraphProto.proto.ITransactionBody}
   */
  _makeTransactionBody(nodeId) {
    return {
      [this._getTransactionDataCase()]: this._makeTransactionData(),
      transactionFee: this._maxTransactionFee != null ? this._maxTransactionFee.toTinybars() : null,
      memo: this._transactionMemo,
      transactionID: this._transactionIds.current._toProtobuf(),
      nodeAccountID: nodeId != null ? nodeId._toProtobuf() : null,
      transactionValidDuration: {
        seconds: _long.default.fromNumber(this._transactionValidDuration)
      }
    };
  }

  /**
   * This method returns a key for the `data` field in a transaction body.
   * Each transaction overwrite this to make sure when we build the transaction body
   * we set the right data field.
   *
   * @abstract
   * @protected
   * @returns {NonNullable<HashgraphProto.proto.TransactionBody["data"]>}
   */
  _getTransactionDataCase() {
    throw new Error("not implemented");
  }

  /**
   * Make a scheduled transaction body
   * FIXME: Should really call this `makeScheduledTransactionBody` to be consistent
   *
   * @internal
   * @returns {HashgraphProto.proto.ISchedulableTransactionBody}
   */
  _getScheduledTransactionBody() {
    return {
      memo: this.transactionMemo,
      transactionFee: this._maxTransactionFee == null ? this._defaultMaxTransactionFee.toTinybars() : this._maxTransactionFee.toTinybars(),
      [this._getTransactionDataCase()]: this._makeTransactionData()
    };
  }

  /**
   * Make the transaction body data.
   *
   * @abstract
   * @protected
   * @returns {object}
   */
  _makeTransactionData() {
    throw new Error("not implemented");
  }

  /**
   * FIXME: Why do we have `isFrozen` and `_isFrozen()`?
   *
   * @protected
   * @returns {boolean}
   */
  _isFrozen() {
    return this._signOnDemand || this._signedTransactions.length > 0 || this._transactions.length > 0;
  }

  /**
   * Require the transaction to NOT be frozen
   *
   * @internal
   */
  _requireNotFrozen() {
    if (this._isFrozen()) {
      throw new Error("transaction is immutable; it has at least one signature or has been explicitly frozen");
    }
  }

  /**
   * Require the transaction to have sign on demand disabled
   *
   * @internal
   */
  _requireNotSignOnDemand() {
    if (this._signOnDemand) {
      throw new Error("Please use `toBytesAsync()` if `signOnDemand` is enabled");
    }
  }

  /**
   * Require the transaction to be frozen
   *
   * @internal
   */
  _requireFrozen() {
    if (!this._isFrozen()) {
      throw new Error("transaction must have been frozen before calculating the hash will be stable, try calling `freeze`");
    }
  }

  /**
   * Require the transaction to have a single node account ID set
   *
   * @internal
   * @protected
   */
  _requireOneNodeAccountId() {
    if (this._nodeAccountIds.length != 1) {
      throw "transaction did not have exactly one node ID set";
    }
  }

  /**
   * @param {HashgraphProto.proto.Transaction} request
   * @returns {Uint8Array}
   */
  _requestToBytes(request) {
    return HashgraphProto.proto.Transaction.encode(request).finish();
  }

  /**
   * @param {HashgraphProto.proto.TransactionResponse} response
   * @returns {Uint8Array}
   */
  _responseToBytes(response) {
    return HashgraphProto.proto.TransactionResponse.encode(response).finish();
  }
}

/**
 * This is essentially a registry/cache for a callback that creates a `ScheduleCreateTransaction`
 *
 * @type {(() => ScheduleCreateTransaction)[]}
 */
exports.default = Transaction;
const SCHEDULE_CREATE_TRANSACTION = [];
exports.SCHEDULE_CREATE_TRANSACTION = SCHEDULE_CREATE_TRANSACTION;

Выполнить команду


Для локальной разработки. Не используйте в интернете!