UNPKG

@aeternity/aepp-sdk

Version:

SDK for the æternity blockchain

246 lines (234 loc) 7.53 kB
import _defineProperty from "@babel/runtime-corejs3/helpers/defineProperty"; function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); } function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } import EventEmitter from 'events'; import { snakeToPascal } from '../utils/string.js'; import { unpackTx } from '../tx/builder/index.js'; import { Tag } from '../tx/builder/constants.js'; import * as handlers from './handlers.js'; import { initialize, enqueueAction, notify, call, disconnect as channelDisconnect } from './internal.js'; import { ChannelError, IllegalArgumentError } from '../utils/errors.js'; import { unpackEntry } from '../tx/builder/entry/index.js'; function snakeToPascalObjKeys(obj) { return Object.entries(obj).reduce((result, [key, val]) => ({ ...result, [snakeToPascal(key)]: val }), {}); } let channelCounter = 0; /** * Channel * @example * ```js * await Channel.initialize({ * url: 'ws://localhost:3001', * role: 'initiator' * initiatorId: 'ak_Y1NRjHuoc3CGMYMvCmdHSBpJsMDR6Ra2t5zjhRcbtMeXXLpLH', * responderId: 'ak_V6an1xhec1xVaAhLuak7QoEbi6t7w5hEtYWp9bMKaJ19i6A9E', * initiatorAmount: 1e18, * responderAmount: 1e18, * pushAmount: 0, * channelReserve: 0, * ttl: 1000, * host: 'localhost', * port: 3002, * lockPeriod: 10, * async sign (tag, tx) => await account.signTransaction(tx) * }) * ``` */ var _debugId = /*#__PURE__*/new WeakMap(); export default class Channel { constructor() { _defineProperty(this, "_eventEmitter", new EventEmitter()); _defineProperty(this, "_nextRpcMessageId", 0); _defineProperty(this, "_rpcCallbacks", new Map()); _defineProperty(this, "_messageQueue", []); _defineProperty(this, "_isMessageQueueLocked", false); _defineProperty(this, "_actionQueue", []); _defineProperty(this, "_isActionQueueLocked", false); _defineProperty(this, "_status", 'disconnected'); _defineProperty(this, "_state", ''); _classPrivateFieldInitSpec(this, _debugId, void 0); channelCounter += 1; _classPrivateFieldSet(_debugId, this, channelCounter); } _debug(...args) { if (this._options.debug !== true) return; console.debug(`Channel #${_classPrivateFieldGet(_debugId, this)}`, ...args); } /** * @param options - Channel params */ static async initialize(options) { return Channel._initialize(new Channel(), options); } static async _initialize(channel, options) { var _options$existingFsmI; const reconnect = ((_options$existingFsmI = options.existingFsmId) !== null && _options$existingFsmI !== void 0 ? _options$existingFsmI : options.existingChannelId) != null; if (reconnect && (options.existingFsmId == null || options.existingChannelId == null)) { throw new IllegalArgumentError('`existingChannelId`, `existingFsmId` should be both provided or missed'); } const reconnectHandler = handlers[options.reestablish === true ? 'awaitingReestablish' : 'awaitingReconnection']; await initialize(channel, reconnect ? reconnectHandler : handlers.awaitingConnection, handlers.channelOpen, options); return channel; } /** * Register event listener function * * Possible events: * * - "error" * - "stateChanged" * - "statusChanged" * - "message" * - "peerDisconnected" * - "onChainTx" * - "ownWithdrawLocked" * - "withdrawLocked" * - "ownDepositLocked" * - "depositLocked" * - "channelReestablished" * - "newContract" * * * @param eventName - Event name * @param callback - Callback function */ on(eventName, callback) { this._eventEmitter.on(eventName, callback); } /** * Remove event listener function * @param eventName - Event name * @param callback - Callback function */ off(eventName, callback) { this._eventEmitter.removeListener(eventName, callback); } /** * Close the connection */ disconnect() { return channelDisconnect(this); } /** * Get current status */ status() { return this._status; } /** * Get current state */ async state() { const res = snakeToPascalObjKeys(await call(this, 'channels.get.offchain_state', {})); return { calls: unpackEntry(res.calls), ...(res.halfSignedTx !== '' && { halfSignedTx: unpackTx(res.halfSignedTx, Tag.SignedTx) }), ...(res.signedTx !== '' && { signedTx: unpackTx(res.signedTx, Tag.SignedTx) }), trees: unpackEntry(res.trees) }; } /** * Get current round * * If round cannot be determined (for example when channel has not been opened) * it will return `null`. */ round() { if (this._state === '') { return null; } const params = unpackTx(this._state, Tag.SignedTx).encodedTx; switch (params.tag) { case Tag.ChannelCreateTx: return 1; case Tag.ChannelOffChainTx: case Tag.ChannelWithdrawTx: case Tag.ChannelDepositTx: return params.round; default: return null; } } /** * Get channel id * */ id() { if (this._channelId == null) throw new ChannelError('Channel is not initialized'); return this._channelId; } /** * Get channel's fsm id * */ fsmId() { if (this._fsmId == null) throw new ChannelError('Channel is not initialized'); return this._fsmId; } async enqueueAction(action) { return enqueueAction(this, (channel, state) => state?.handler === handlers.channelOpen, action); } /** * Leave channel * * It is possible to leave a channel and then later reestablish the channel * off-chain state and continue operation. When a leave method is called, * the channel fsm passes it on to the peer fsm, reports the current mutually * signed state and then terminates. * * The channel can be reestablished by instantiating another Channel instance * with two extra params: existingChannelId and existingFsmId. * * @example * ```js * channel.leave().then(({ channelId, signedTx }) => { * console.log(channelId) * console.log(signedTx) * }) * ``` */ async leave() { return this.enqueueAction(() => { notify(this, 'channels.leave'); return { handler: handlers.awaitingLeave }; }); } /** * Trigger mutual close * * At any moment after the channel is opened, a closing procedure can be triggered. * This can be done by either of the parties. The process is similar to the off-chain updates. * * @param sign - Function which verifies and signs mutual close transaction * @example * ```js * channel.shutdown( * async (tx) => await account.signTransaction(tx) * ).then(tx => console.log('on_chain_tx', tx)) * ``` */ async shutdown(sign) { return this.enqueueAction(() => { notify(this, 'channels.shutdown'); return { handler: handlers.awaitingShutdownTx, state: { sign } }; }); } } //# sourceMappingURL=Base.js.map