@ocap/resolver 1.19.15 → 1.19.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2018-2019 ArcBlock
1
+ Copyright 2018-2025 ArcBlock
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
package/lib/index.js CHANGED
@@ -22,6 +22,7 @@ const { DEFAULT_TOKEN_DECIMAL } = require('@ocap/util/lib/constant');
22
22
  const { createExecutor } = require('@ocap/tx-protocols');
23
23
  const { decodeAnySafe } = require('@ocap/tx-protocols/lib/util');
24
24
  const { Joi } = require('@arcblock/validator');
25
+ const Queue = require('queue');
25
26
 
26
27
  const {
27
28
  createIndexedAccount,
@@ -96,6 +97,12 @@ const maxGasOps = {
96
97
  'fg:t:claim_block_reward': { create: 2, update: 38 },
97
98
  };
98
99
 
100
+ const txRequestConfig = {
101
+ maxRequest: Number(process.env.TX_MAX_REQUEST) || 200,
102
+ concurrent: Number(process.env.TX_CONCURRENCY) || 10,
103
+ timeout: Number(process.env.TX_TIMEOUT) || 1000 * 30,
104
+ };
105
+
99
106
  const formatData = (data) => {
100
107
  if (!data) {
101
108
  return data;
@@ -172,7 +179,7 @@ module.exports = class OCAPResolver {
172
179
  throw new Error('OCAP Resolver requires a valid indexdb implementation to work');
173
180
  }
174
181
 
175
- this.logger = logger || { info: debug, warn: debug, error: debug, debug };
182
+ this.logger = logger || console;
176
183
 
177
184
  this.statedb = statedb;
178
185
  this.indexdb = indexdb;
@@ -182,6 +189,11 @@ module.exports = class OCAPResolver {
182
189
  this.chainAddr = md5(config.chainId);
183
190
  this.tokenItx = Config.genTokenItx(this.config);
184
191
  this.consensus = `${statedb.name} v${statedb.version}`;
192
+ this.queue = new Queue({
193
+ autostart: true,
194
+ concurrency: txRequestConfig.concurrent,
195
+ timeout: txRequestConfig.timeout,
196
+ });
185
197
 
186
198
  if (indexdb) {
187
199
  this.tokenCache = getTokenCacheInstance(indexdb.token);
@@ -206,18 +218,21 @@ module.exports = class OCAPResolver {
206
218
  }
207
219
 
208
220
  this.connectIndexDB();
209
- this.runAsLambda((txn) => this.initializeStateDB(txn)).catch((err) => {
210
- console.error('Failed to initialize statedb:', err.message);
221
+ this.initializeStateDB().catch((err) => {
222
+ this.logger.error('Failed to initialize statedb:', err.message);
211
223
  process.exit(1);
212
224
  });
213
225
  }
214
226
 
215
- async sendTx({ tx: txBase64, extra }, ctx = {}) {
227
+ sendTx({ tx: txBase64, extra }, ctx = {}) {
216
228
  debug('sendTx', { txBase64, request: ctx.request });
217
229
 
218
230
  if (process.env.CHAIN_MODE === 'readonly') {
219
231
  throw new CustomError('FORBIDDEN', 'This chain node is running in readonly mode');
220
232
  }
233
+ if (this.queue.length >= txRequestConfig.maxRequest) {
234
+ throw new CustomError('FORBIDDEN', 'Chain is busy');
235
+ }
221
236
 
222
237
  // 0. create new context
223
238
  // NOTE: if you add more fields here, please remember to change core/tx-protocols/lib/execute.js
@@ -234,11 +249,18 @@ module.exports = class OCAPResolver {
234
249
  },
235
250
  };
236
251
 
237
- // 1. execute and persist the transaction
238
- const result = await this.executor.execute(context);
239
-
240
- // 2. Return the hash
241
- return result.txHash;
252
+ // eslint-disable-next-line no-async-promise-executor
253
+ return new Promise((resolve, reject) => {
254
+ const task = async () => {
255
+ try {
256
+ const result = await this.executor.execute(context);
257
+ resolve(result.txHash);
258
+ } catch (err) {
259
+ reject(err);
260
+ }
261
+ };
262
+ this.queue.push(task);
263
+ });
242
264
  }
243
265
 
244
266
  getTx({ hash }, ctx) {
@@ -786,56 +808,62 @@ module.exports = class OCAPResolver {
786
808
  return this.runAsLambda((txn) => this.statedb.token.get(id, { txn }));
787
809
  }
788
810
 
789
- async initializeStateDB(txn) {
811
+ async initializeStateDB() {
790
812
  const { accounts, token } = this.config;
791
813
  const { account: accountDB, chain: chainDB, token: tokenDB } = this.statedb;
792
814
  const { account: accountState, chain: chainState, token: tokenState } = states;
793
815
 
794
- const ctx = { txn };
795
816
  const context = { txTime: new Date().toISOString(), txHash: '' };
796
817
 
797
818
  // Auto persist config to chain state
798
819
  // Will throw error if immutable chain config are updated
799
820
  const info = await this.getChain();
800
- if (!info) {
801
- const state = chainState.create({ ...this.config, address: CHAIN_ADDR }, context);
802
- const result = await chainDB.create(CHAIN_ADDR, state, ctx);
803
- debug('create chain state', result);
804
- } else if (isEqual(pick(info, Object.keys(this.config)), this.config) === false) {
805
- const state = chainState.update(info, this.config, context);
806
- const result = await chainDB.update(CHAIN_ADDR, state, ctx);
807
- debug('update chain state', result);
808
- }
821
+ await this.runAsLambda(async (txn) => {
822
+ if (!info) {
823
+ const state = chainState.create({ ...this.config, address: CHAIN_ADDR }, context);
824
+ const result = await chainDB.create(CHAIN_ADDR, state, { txn });
825
+ this.logger.info('create chain state', { result });
826
+ } else if (isEqual(pick(info, Object.keys(this.config)), this.config) === false) {
827
+ const state = chainState.update(info, this.config, context);
828
+ const result = await chainDB.update(CHAIN_ADDR, state, { txn });
829
+ this.logger.info('update chain state', { result });
830
+ }
831
+ });
809
832
 
810
833
  // Auto persist token state, just once
811
834
  // Since the token info should not be changed after restart
812
835
  if (this.tokenItx) {
813
836
  const existToken = await this.getToken(this.tokenItx.address);
814
837
  if (!existToken) {
815
- const state = tokenState.create(this.tokenItx, context);
816
- const result = await tokenDB.create(this.tokenItx.address, state, ctx);
817
- tokenDB.emit('create', result, ctx);
818
- debug('create token state', result);
838
+ await this.runAsLambda(async (txn) => {
839
+ const state = tokenState.create(this.tokenItx, context);
840
+ const result = await tokenDB.create(this.tokenItx.address, state, { txn });
841
+ tokenDB.emit('create', result, { txn });
842
+ this.logger.info('create token state', { address: result?.address });
843
+ });
819
844
  }
820
845
 
821
846
  // Auto populate token holder accounts if not exist
822
- for (let i = 0; i < accounts.length; i++) {
823
- const { address, balance, moniker } = accounts[i];
824
- try {
825
- const existAccount = await accountDB.get(address, ctx);
826
- if (!existAccount) {
827
- const balanceStr = fromTokenToUnit(balance, token.decimal).toString(10);
828
- const state = accountState.create(
829
- { address, tokens: { [this.tokenItx.address]: balanceStr }, moniker: moniker || 'token-holder' },
830
- context
831
- );
832
- const result = await accountDB.create(address, state, ctx);
833
- accountDB.emit('create', result, ctx);
847
+ await this.runAsLambda(async (txn) => {
848
+ for (let i = 0; i < accounts.length; i++) {
849
+ const { address, balance, moniker, pk } = accounts[i];
850
+ try {
851
+ const existAccount = await accountDB.get(address, { txn });
852
+ if (!existAccount) {
853
+ const balanceStr = fromTokenToUnit(balance, token.decimal).toString(10);
854
+ const state = accountState.create(
855
+ { address, tokens: { [this.tokenItx.address]: balanceStr }, moniker: moniker || 'token-holder', pk },
856
+ context
857
+ );
858
+ const result = await accountDB.create(address, state, { txn });
859
+ accountDB.emit('create', result, { txn });
860
+ this.logger.info('init account done', { address });
861
+ }
862
+ } catch (err) {
863
+ this.logger.error('Failed to initialize initial token holders', err);
834
864
  }
835
- } catch (err) {
836
- console.error('Failed to initialize initial token holders', err);
837
865
  }
838
- }
866
+ });
839
867
  }
840
868
  }
841
869
 
@@ -347,9 +347,9 @@ class TokenDistributionManager {
347
347
  }
348
348
 
349
349
  async getStakeState(address, ctx) {
350
- const stakeState =
351
- ctx?.stateSnapshot?.[address] ||
352
- (await this.resolver.runAsLambda((txn) => this.resolver.statedb.stake.get(address, { txn })));
350
+ const stakeState = await this.resolver.runAsLambda((txn) =>
351
+ this.resolver.statedb.stake.get(address, { ...ctx, txn })
352
+ );
353
353
 
354
354
  return stakeState;
355
355
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.19.15",
6
+ "version": "1.19.16",
7
7
  "description": "GraphQL resolver built upon ocap statedb and GQL layer",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -22,18 +22,19 @@
22
22
  "jest": "^29.7.0"
23
23
  },
24
24
  "dependencies": {
25
- "@arcblock/did": "1.19.15",
26
- "@arcblock/did-util": "1.19.15",
27
- "@arcblock/validator": "1.19.15",
28
- "@ocap/config": "1.19.15",
29
- "@ocap/indexdb": "1.19.15",
30
- "@ocap/mcrypto": "1.19.15",
31
- "@ocap/message": "1.19.15",
32
- "@ocap/state": "1.19.15",
33
- "@ocap/tx-protocols": "1.19.15",
34
- "@ocap/util": "1.19.15",
25
+ "@arcblock/did": "1.19.16",
26
+ "@arcblock/did-util": "1.19.16",
27
+ "@arcblock/validator": "1.19.16",
28
+ "@ocap/config": "1.19.16",
29
+ "@ocap/indexdb": "1.19.16",
30
+ "@ocap/mcrypto": "1.19.16",
31
+ "@ocap/message": "1.19.16",
32
+ "@ocap/state": "1.19.16",
33
+ "@ocap/tx-protocols": "1.19.16",
34
+ "@ocap/util": "1.19.16",
35
35
  "debug": "^4.3.6",
36
- "lodash": "^4.17.21"
36
+ "lodash": "^4.17.21",
37
+ "queue": "^6"
37
38
  },
38
- "gitHead": "ab6cd19ab8633c815ab0235b7ab5d80fc071de15"
39
+ "gitHead": "9c3c349e48fc3c5a63aba6068ba5d31cc46d81c9"
39
40
  }