|
@@ -8,7 +8,7 @@
|
|
|
* (at your option) any later version.
|
|
|
*
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
* GNU General Public License for more details.
|
|
|
*
|
|
@@ -16,70 +16,66 @@
|
|
|
* along with this program. If not, see <https:
|
|
|
*/
|
|
|
|
|
|
-'use strict';
|
|
|
+'use strict'
|
|
|
|
|
|
-const debug = require('debug')('joystream:runtime:base');
|
|
|
+const debug = require('debug')('joystream:runtime:base')
|
|
|
|
|
|
-const { registerJoystreamTypes } = require('@joystream/types');
|
|
|
-const { ApiPromise, WsProvider } = require('@polkadot/api');
|
|
|
+const { registerJoystreamTypes } = require('@joystream/types')
|
|
|
+const { ApiPromise, WsProvider } = require('@polkadot/api')
|
|
|
|
|
|
-const { IdentitiesApi } = require('@joystream/runtime-api/identities');
|
|
|
-const { BalancesApi } = require('@joystream/runtime-api/balances');
|
|
|
-const { WorkersApi } = require('@joystream/runtime-api/workers');
|
|
|
-const { AssetsApi } = require('@joystream/runtime-api/assets');
|
|
|
-const { DiscoveryApi } = require('@joystream/runtime-api/discovery');
|
|
|
-const AsyncLock = require('async-lock');
|
|
|
+const { IdentitiesApi } = require('@joystream/runtime-api/identities')
|
|
|
+const { BalancesApi } = require('@joystream/runtime-api/balances')
|
|
|
+const { WorkersApi } = require('@joystream/runtime-api/workers')
|
|
|
+const { AssetsApi } = require('@joystream/runtime-api/assets')
|
|
|
+const { DiscoveryApi } = require('@joystream/runtime-api/discovery')
|
|
|
+const AsyncLock = require('async-lock')
|
|
|
|
|
|
|
|
|
* Initialize runtime (substrate) API and keyring.
|
|
|
*/
|
|
|
-class RuntimeApi
|
|
|
-{
|
|
|
- static async create(options)
|
|
|
- {
|
|
|
- const runtime_api = new RuntimeApi();
|
|
|
- await runtime_api.init(options || {});
|
|
|
- return runtime_api;
|
|
|
+class RuntimeApi {
|
|
|
+ static async create (options) {
|
|
|
+ const runtime_api = new RuntimeApi()
|
|
|
+ await runtime_api.init(options || {})
|
|
|
+ return runtime_api
|
|
|
}
|
|
|
|
|
|
- async init(options)
|
|
|
- {
|
|
|
- debug('Init');
|
|
|
+ async init (options) {
|
|
|
+ debug('Init')
|
|
|
|
|
|
- options = options || {};
|
|
|
+ options = options || {}
|
|
|
|
|
|
|
|
|
- registerJoystreamTypes();
|
|
|
+ registerJoystreamTypes()
|
|
|
|
|
|
- const provider = new WsProvider(options.provider_url || 'ws://localhost:9944');
|
|
|
+ const provider = new WsProvider(options.provider_url || 'ws://localhost:9944')
|
|
|
|
|
|
|
|
|
- this.api = await ApiPromise.create({ provider });
|
|
|
+ this.api = await ApiPromise.create({ provider })
|
|
|
|
|
|
- this.asyncLock = new AsyncLock();
|
|
|
+ this.asyncLock = new AsyncLock()
|
|
|
|
|
|
|
|
|
- this.nonces = {};
|
|
|
+ this.nonces = {}
|
|
|
|
|
|
|
|
|
this.identities = await IdentitiesApi.create(this, {
|
|
|
account_file: options.account_file,
|
|
|
passphrase: options.passphrase,
|
|
|
canPromptForPassphrase: options.canPromptForPassphrase
|
|
|
- });
|
|
|
- this.balances = await BalancesApi.create(this);
|
|
|
- this.workers = await WorkersApi.create(this);
|
|
|
- this.assets = await AssetsApi.create(this);
|
|
|
- this.discovery = await DiscoveryApi.create(this);
|
|
|
+ })
|
|
|
+ this.balances = await BalancesApi.create(this)
|
|
|
+ this.workers = await WorkersApi.create(this)
|
|
|
+ this.assets = await AssetsApi.create(this)
|
|
|
+ this.discovery = await DiscoveryApi.create(this)
|
|
|
}
|
|
|
|
|
|
- disconnect()
|
|
|
- {
|
|
|
- this.api.disconnect();
|
|
|
+ disconnect () {
|
|
|
+ this.api.disconnect()
|
|
|
}
|
|
|
|
|
|
- executeWithAccountLock(account_id, func) {
|
|
|
- return this.asyncLock.acquire(`${account_id}`, func);
|
|
|
+ executeWithAccountLock (account_id, func) {
|
|
|
+ return this.asyncLock.acquire(`${account_id}`, func)
|
|
|
}
|
|
|
|
|
|
|
|
@@ -89,47 +85,45 @@ class RuntimeApi
|
|
|
* The result of the Promise is an array containing first the full event
|
|
|
* name, and then the event fields as an object.
|
|
|
*/
|
|
|
- async waitForEvent(module, name)
|
|
|
- {
|
|
|
- return this.waitForEvents([[module, name]]);
|
|
|
+ async waitForEvent (module, name) {
|
|
|
+ return this.waitForEvents([[module, name]])
|
|
|
}
|
|
|
|
|
|
- _matchingEvents(subscribed, events)
|
|
|
- {
|
|
|
- debug(`Number of events: ${events.length}; subscribed to ${subscribed}`);
|
|
|
+ _matchingEvents(subscribed, events) {
|
|
|
+ debug(`Number of events: ${events.length} subscribed to ${subscribed}`)
|
|
|
|
|
|
const filtered = events.filter((record) => {
|
|
|
- const { event, phase } = record;
|
|
|
+ const { event, phase } = record
|
|
|
|
|
|
|
|
|
- debug(`\t${event.section}:${event.method}:: (phase=${phase.toString()})`);
|
|
|
- debug(`\t\t${event.meta.documentation.toString()}`);
|
|
|
+ debug(`\t${event.section}:${event.method}:: (phase=${phase.toString()})`)
|
|
|
+ debug(`\t\t${event.meta.documentation.toString()}`)
|
|
|
|
|
|
|
|
|
const matching = subscribed.filter((value) => {
|
|
|
- return event.section == value[0] && event.method == value[1];
|
|
|
- });
|
|
|
- return matching.length > 0;
|
|
|
- });
|
|
|
- debug(`Filtered: ${filtered.length}`);
|
|
|
+ return event.section === value[0] && event.method === value[1]
|
|
|
+ })
|
|
|
+ return matching.length > 0
|
|
|
+ })
|
|
|
+ debug(`Filtered: ${filtered.length}`)
|
|
|
|
|
|
const mapped = filtered.map((record) => {
|
|
|
- const { event } = record;
|
|
|
- const types = event.typeDef;
|
|
|
+ const { event } = record
|
|
|
+ const types = event.typeDef
|
|
|
|
|
|
|
|
|
- const payload = {};
|
|
|
+ const payload = {}
|
|
|
event.data.forEach((data, index) => {
|
|
|
- debug(`\t\t\t${types[index].type}: ${data.toString()}`);
|
|
|
- payload[types[index].type] = data;
|
|
|
- });
|
|
|
+ debug(`\t\t\t${types[index].type}: ${data.toString()}`)
|
|
|
+ payload[types[index].type] = data
|
|
|
+ })
|
|
|
|
|
|
- const full_name = `${event.section}.${event.method}`;
|
|
|
- return [full_name, payload];
|
|
|
- });
|
|
|
- debug('Mapped', mapped);
|
|
|
+ const full_name = `${event.section}.${event.method}`
|
|
|
+ return [full_name, payload]
|
|
|
+ })
|
|
|
+ debug('Mapped', mapped)
|
|
|
|
|
|
- return mapped;
|
|
|
+ return mapped
|
|
|
}
|
|
|
|
|
|
|
|
@@ -139,16 +133,15 @@ class RuntimeApi
|
|
|
*
|
|
|
* Returns the first matched event *only*.
|
|
|
*/
|
|
|
- async waitForEvents(subscribed)
|
|
|
- {
|
|
|
+ async waitForEvents (subscribed) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
this.api.query.system.events((events) => {
|
|
|
- const matches = this._matchingEvents(subscribed, events);
|
|
|
+ const matches = this._matchingEvents(subscribed, events)
|
|
|
if (matches && matches.length) {
|
|
|
- resolve(matches);
|
|
|
+ resolve(matches)
|
|
|
}
|
|
|
- });
|
|
|
- });
|
|
|
+ })
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
|
|
@@ -159,68 +152,67 @@ class RuntimeApi
|
|
|
* If the subscribed events are given, and a callback as well, then the
|
|
|
* callback is invoked with matching events.
|
|
|
*/
|
|
|
- async signAndSend(accountId, tx, attempts, subscribed, callback)
|
|
|
- {
|
|
|
+ async signAndSend (accountId, tx, attempts, subscribed, callback) {
|
|
|
|
|
|
- const from_key = this.identities.keyring.getPair(accountId);
|
|
|
+ const from_key = this.identities.keyring.getPair(accountId)
|
|
|
|
|
|
if (from_key.isLocked) {
|
|
|
- throw new Error('Must unlock key before using it to sign!');
|
|
|
+ throw new Error('Must unlock key before using it to sign!')
|
|
|
}
|
|
|
|
|
|
- const finalizedPromise = newExternallyControlledPromise();
|
|
|
+ const finalizedPromise = newExternallyControlledPromise()
|
|
|
|
|
|
- let unsubscribe = await this.executeWithAccountLock(accountId, async () => {
|
|
|
+ let unsubscribe = await this.executeWithAccountLock(accountId, async () => {
|
|
|
|
|
|
- let nonce = this.nonces[accountId];
|
|
|
+ let nonce = this.nonces[accountId]
|
|
|
|
|
|
let incrementNonce = () => {
|
|
|
|
|
|
- incrementNonce = () => {};
|
|
|
- nonce = nonce.addn(1);
|
|
|
- this.nonces[accountId] = nonce;
|
|
|
+ incrementNonce = () => {}
|
|
|
+ nonce = nonce.addn(1)
|
|
|
+ this.nonces[accountId] = nonce
|
|
|
}
|
|
|
|
|
|
|
|
|
if (!nonce) {
|
|
|
|
|
|
- nonce = await this.api.query.system.accountNonce(accountId);
|
|
|
- debug(`Got nonce for ${accountId} from chain: ${nonce}`);
|
|
|
+ nonce = await this.api.query.system.accountNonce(accountId)
|
|
|
+ debug(`Got nonce for ${accountId} from chain: ${nonce}`)
|
|
|
}
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
- debug('Signing and sending tx');
|
|
|
+ debug('Signing and sending tx')
|
|
|
|
|
|
let unsubscribe = tx.sign(from_key, { nonce })
|
|
|
.send(({events = [], status}) => {
|
|
|
- debug(`TX status: ${status.type}`);
|
|
|
+ debug(`TX status: ${status.type}`)
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
if (subscribed && callback) {
|
|
|
- const matched = this._matchingEvents(subscribed, events);
|
|
|
- debug('Matching events:', matched);
|
|
|
+ const matched = this._matchingEvents(subscribed, events)
|
|
|
+ debug('Matching events:', matched)
|
|
|
if (matched.length) {
|
|
|
- callback(matched);
|
|
|
+ callback(matched)
|
|
|
}
|
|
|
}
|
|
|
- } catch(err) {
|
|
|
+ } catch (err) {
|
|
|
debug(`Error handling events ${err.stack}`)
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (status.isReady) {
|
|
|
- debug('TX Ready.');
|
|
|
- incrementNonce();
|
|
|
- resolve(unsubscribe);
|
|
|
+ debug('TX Ready.')
|
|
|
+ incrementNonce()
|
|
|
+ resolve(unsubscribe)
|
|
|
} else if (status.isBroadcast) {
|
|
|
- debug('TX Broadcast.');
|
|
|
- incrementNonce();
|
|
|
- resolve(unsubscribe);
|
|
|
+ debug('TX Broadcast.')
|
|
|
+ incrementNonce()
|
|
|
+ resolve(unsubscribe)
|
|
|
} else if (status.isFinalized) {
|
|
|
- debug('TX Finalized.');
|
|
|
+ debug('TX Finalized.')
|
|
|
finalizedPromise.resolve(status)
|
|
|
} else if (status.isFuture) {
|
|
|
|
|
@@ -228,10 +220,10 @@ class RuntimeApi
|
|
|
|
|
|
debug('TX Future!')
|
|
|
|
|
|
- delete this.nonces[accountId];
|
|
|
- const err = new Error('transaction nonce set in future');
|
|
|
- finalizedPromise.reject(err);
|
|
|
- reject(err);
|
|
|
+ delete this.nonces[accountId]
|
|
|
+ const err = new Error('transaction nonce set in future')
|
|
|
+ finalizedPromise.reject(err)
|
|
|
+ reject(err)
|
|
|
}
|
|
|
|
|
|
|
|
@@ -247,45 +239,45 @@ class RuntimeApi
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
- const errstr = err.toString();
|
|
|
+ const errstr = err.toString()
|
|
|
|
|
|
|
|
|
if (errstr.indexOf('Error: 1014:') < 0 &&
|
|
|
errstr.indexOf('Error: 1010:') < 0)
|
|
|
{
|
|
|
|
|
|
- debug('TX error', err);
|
|
|
+ debug('TX error', err)
|
|
|
} else {
|
|
|
|
|
|
- delete this.nonces[accountId];
|
|
|
+ delete this.nonces[accountId]
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- finalizedPromise.reject(err);
|
|
|
+ finalizedPromise.reject(err)
|
|
|
|
|
|
- reject(err);
|
|
|
- });
|
|
|
- });
|
|
|
+ reject(err)
|
|
|
+ })
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
- return finalizedPromise.promise;
|
|
|
+ return finalizedPromise.promise
|
|
|
}
|
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
|
- RuntimeApi: RuntimeApi,
|
|
|
+ RuntimeApi
|
|
|
}
|
|
|
|
|
|
function newExternallyControlledPromise () {
|
|
|
|
|
|
- let resolve, reject;
|
|
|
+ let resolve, reject
|
|
|
const promise = new Promise((res, rej) => {
|
|
|
- resolve = res;
|
|
|
- reject = rej;
|
|
|
- });
|
|
|
- return ({resolve, reject, promise});
|
|
|
+ resolve = res
|
|
|
+ reject = rej
|
|
|
+ })
|
|
|
+ return ({resolve, reject, promise})
|
|
|
}
|