@aeternity/aepp-sdk
Version:
SDK for the æternity blockchain
246 lines (234 loc) • 7.53 kB
JavaScript
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