@ocap/resolver 1.18.159 → 1.18.160

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/lib/index.js CHANGED
@@ -756,9 +756,9 @@ module.exports = class OCAPResolver {
756
756
  }
757
757
  }
758
758
 
759
- runAsLambda(fn) {
759
+ runAsLambda(fn, args) {
760
760
  if (typeof this.statedb.runAsLambda === 'function') {
761
- return this.statedb.runAsLambda((txn) => fn(txn));
761
+ return this.statedb.runAsLambda((txn) => fn(txn), args);
762
762
  }
763
763
 
764
764
  return fn();
@@ -1036,7 +1036,7 @@ module.exports = class OCAPResolver {
1036
1036
 
1037
1037
  const totalPage = Math.floor(paging.total / pageSize);
1038
1038
  const cursors = new Array(totalPage).fill(true).map((_, i) => (i + 1) * pageSize);
1039
- const step = 3;
1039
+ const step = process.env.RESOLVER_BATCH_CONCURRENCY || 3;
1040
1040
  let results = firstPage;
1041
1041
 
1042
1042
  for (let i = 0; i < cursors.length; i += step) {
package/lib/token-flow.js CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable consistent-return */
1
2
  /* eslint-disable no-await-in-loop */
2
3
 
3
4
  const { BN, fromTokenToUnit, isSameDid } = require('@ocap/util');
@@ -131,6 +132,15 @@ const getInitialBalance = (address, config) => {
131
132
  return account ? fromTokenToUnit(account.balance) : ZERO;
132
133
  };
133
134
 
135
+ const getBalance = async (address, tokenAddress, { resolver, txn }) => {
136
+ const state = await resolver.statedb.account.get(address, { txn, traceMigration: false });
137
+ if (!state) {
138
+ throw new CustomError('INVALID_REQUEST', `Invalid address ${address}`);
139
+ }
140
+ const balance = state.tokens[tokenAddress] || 0;
141
+ return balance;
142
+ };
143
+
134
144
  const fixMigrateReceipts = async (tx, resolver) => {
135
145
  const migrationChain = await resolver.getMigrationChain();
136
146
  const txTime = new Date(tx.time);
@@ -146,9 +156,9 @@ const verifyAccountRisk = async (
146
156
  ctx = {}
147
157
  ) => {
148
158
  // validate request params
149
- const { error } = paramsSchema.validate({ accountAddress, tokenAddress, resolver });
150
- if (error) {
151
- throw new CustomError('INVALID_PARAMS', error.message);
159
+ const validation = paramsSchema.validate({ accountAddress, tokenAddress, resolver });
160
+ if (validation.error) {
161
+ throw new CustomError('INVALID_PARAMS', validation.error.message);
152
162
  }
153
163
 
154
164
  const checkedAccounts = new Map();
@@ -158,138 +168,152 @@ const verifyAccountRisk = async (
158
168
  const toleranceUnit = fromTokenToUnit(tolerance, tokenState?.decimal);
159
169
  const vaultAccounts = getVaultAccounts(resolver.config);
160
170
 
161
- while (accountQueue.length) {
162
- // limit
163
- if (checkedAccounts.size >= accountLimit) {
164
- return {
165
- isRisky: false,
166
- reason: 'MAX_ACCOUNT_SIZE_LIMIT',
167
- data: {
168
- accountCount: checkedAccounts.size,
169
- txCount: checkedTx.size,
170
- },
171
- };
171
+ let executeCount = 0;
172
+
173
+ const execute = async (txn) => {
174
+ if (executeCount > 1) {
175
+ throw new CustomError('INVALID_REQUEST', 'verifyAccountRisk should not retry');
172
176
  }
177
+ while (accountQueue.length) {
178
+ // limit
179
+ if (checkedAccounts.size >= accountLimit) {
180
+ return {
181
+ isRisky: false,
182
+ reason: 'MAX_ACCOUNT_SIZE_LIMIT',
183
+ data: {
184
+ accountCount: checkedAccounts.size,
185
+ txCount: checkedTx.size,
186
+ },
187
+ };
188
+ }
173
189
 
174
- const address = accountQueue.pop();
175
- // Avoid circular query
176
- if (checkedAccounts.has(address)) continue;
190
+ const address = accountQueue.pop();
177
191
 
178
- const trustedConfig = await resolver.filter?.getTrustedAccountConfig(address);
179
- // Skip trusted accounts that do not have tolerance configured
180
- if (trustedConfig && !trustedConfig.tolerance) {
181
- checkedAccounts.set(address, true);
182
- continue;
183
- }
192
+ // Avoid circular query
193
+ if (checkedAccounts.has(address)) continue;
184
194
 
185
- let transactions = [];
186
- try {
187
- transactions = await resolver._getAllResults(
188
- 'transactions',
189
- (paging) => resolver.listTransactions({ paging, accountFilter: { accounts: [address] } }, ctx),
190
- txLimit
191
- );
192
- } catch (e) {
193
- // skip if tx limit exceeded
194
- if (e.code === 'EXCEED_LIMIT') {
195
+ const trustedConfig = await resolver.filter?.getTrustedAccountConfig(address);
196
+ // Skip trusted accounts that do not have tolerance configured
197
+ if (trustedConfig && !trustedConfig.tolerance) {
195
198
  checkedAccounts.set(address, true);
196
199
  continue;
197
200
  }
198
- throw e;
199
- }
200
201
 
201
- const accountState = await resolver.getAccountState({ address, traceMigration: false, expandContext: false }, ctx);
202
- if (!accountState) {
203
- throw new CustomError('INVALID_REQUEST', `Invalid address ${address}`);
204
- }
205
- const balance = accountState.tokens.find((item) => isSameDid(item.address, tokenAddress))?.value || 0;
202
+ const balance = await getBalance(address, tokenAddress, { resolver, txn });
206
203
 
207
- let transferIn = getInitialBalance(address, resolver.config);
208
- let transferOut = ZERO;
209
-
210
- // Parse txs to get transfer amounts
211
- for (const tx of transactions) {
212
- // cache tx
213
- if (!checkedTx.has(tx.hash)) {
214
- await fixMigrateReceipts(tx, resolver, ctx);
215
- checkedTx.set(tx.hash, getTransferList(tx, tokenAddress));
204
+ let transactions = [];
205
+ try {
206
+ transactions = await resolver._getAllResults(
207
+ 'transactions',
208
+ (paging) => resolver.listTransactions({ paging, accountFilter: { accounts: [address] } }, ctx),
209
+ txLimit
210
+ );
211
+ } catch (e) {
212
+ // skip if tx limit exceeded
213
+ if (e.code === 'EXCEED_LIMIT') {
214
+ checkedAccounts.set(address, true);
215
+ continue;
216
+ }
217
+ throw e;
216
218
  }
217
- const { transferInList, transferOutList } = checkedTx.get(tx.hash);
218
-
219
- // Calculate the total amount of transfer for this address
220
- transferIn = transferIn.add(
221
- transferInList
222
- .filter((item) => isSameDid(item.address, address))
223
- .map((item) => item.value)
224
- .reduce((prev, cur) => prev.add(cur), ZERO)
225
- );
226
219
 
227
- transferOut = transferOut.add(
228
- transferOutList
229
- .filter((item) => isSameDid(item.address, address))
230
- .map((item) => item.value)
231
- .reduce((prev, cur) => prev.add(cur), ZERO)
232
- );
220
+ let transferIn = getInitialBalance(address, resolver.config);
221
+ let transferOut = ZERO;
233
222
 
234
- // push transferIn accounts to queue for next time check
235
- if (transferInList.some((item) => isSameDid(item.address, address))) {
236
- const accountsToQueue = transferOutList
237
- .filter((item) => {
238
- if (accountQueue.includes(item.address)) return false;
239
- // Skip vault accounts
240
- if (vaultAccounts.includes(item.address)) return false;
241
- // skip gas、fee
242
- if (['gas', 'fee'].includes(item.action)) return false;
243
- // Skip not token holders
244
- if (schemas.tokenHolder.validate(item.address).error) return false;
223
+ // Parse txs to get transfer amounts
224
+ for (const tx of transactions) {
225
+ // cache tx
226
+ if (!checkedTx.has(tx.hash)) {
227
+ await fixMigrateReceipts(tx, resolver, ctx);
228
+ checkedTx.set(tx.hash, getTransferList(tx, tokenAddress));
229
+ }
230
+ const { transferInList, transferOutList } = checkedTx.get(tx.hash);
231
+
232
+ // Calculate the total amount of transfer for this address
233
+ transferIn = transferIn.add(
234
+ transferInList
235
+ .filter((item) => isSameDid(item.address, address))
236
+ .map((item) => item.value)
237
+ .reduce((prev, cur) => prev.add(cur), ZERO)
238
+ );
245
239
 
246
- return true;
247
- })
248
- .map((item) => item.address);
240
+ transferOut = transferOut.add(
241
+ transferOutList
242
+ .filter((item) => isSameDid(item.address, address))
243
+ .map((item) => item.value)
244
+ .reduce((prev, cur) => prev.add(cur), ZERO)
245
+ );
249
246
 
250
- accountQueue.push(...uniq(accountsToQueue));
247
+ // push transferIn accounts to queue for next time check
248
+ if (transferInList.some((item) => isSameDid(item.address, address))) {
249
+ const accountsToQueue = transferOutList
250
+ .filter((item) => {
251
+ if (accountQueue.includes(item.address)) return false;
252
+ // Skip vault accounts
253
+ if (vaultAccounts.includes(item.address)) return false;
254
+ // skip gas、fee
255
+ if (['gas', 'fee'].includes(item.action)) return false;
256
+ // Skip not token holders
257
+ if (schemas.tokenHolder.validate(item.address).error) return false;
258
+
259
+ return true;
260
+ })
261
+ .map((item) => item.address);
262
+
263
+ accountQueue.push(...uniq(accountsToQueue));
264
+ }
251
265
  }
252
- }
253
266
 
254
- checkedAccounts.set(address, true);
255
-
256
- // Check if the balance not matches the transfer records
257
- if (
258
- transferIn
259
- .sub(transferOut.add(new BN(balance)))
260
- .add(fromTokenToUnit(trustedConfig?.tolerance || 0))
261
- .abs()
262
- .gt(toleranceUnit)
263
- ) {
264
- debug('Account balance does not match transfer records', {
265
- address,
266
- transferIn: transferIn.toString(),
267
- transferOut: transferOut.toString(),
268
- balance,
269
- sourceAccount: accountAddress,
270
- });
271
- return {
272
- isRisky: true,
273
- reason: 'INVALID_BALANCE',
274
- data: {
267
+ checkedAccounts.set(address, true);
268
+
269
+ // Check if the balance not matches the transfer records
270
+ if (
271
+ transferIn
272
+ .sub(transferOut.add(new BN(balance)))
273
+ .add(fromTokenToUnit(trustedConfig?.tolerance || 0))
274
+ .abs()
275
+ .gt(toleranceUnit)
276
+ ) {
277
+ debug('Account balance does not match transfer records', {
275
278
  address,
276
- balance,
277
279
  transferIn: transferIn.toString(),
278
280
  transferOut: transferOut.toString(),
279
- accountCount: checkedAccounts.size,
280
- txCount: checkedTx.size,
281
- },
282
- };
281
+ balance,
282
+ sourceAccount: accountAddress,
283
+ });
284
+ return {
285
+ isRisky: true,
286
+ reason: 'INVALID_BALANCE',
287
+ data: {
288
+ address,
289
+ balance,
290
+ transferIn: transferIn.toString(),
291
+ transferOut: transferOut.toString(),
292
+ accountCount: checkedAccounts.size,
293
+ txCount: checkedTx.size,
294
+ },
295
+ };
296
+ }
283
297
  }
284
- }
298
+ };
285
299
 
286
- return {
287
- isRisky: false,
288
- data: {
289
- accountCount: checkedAccounts.size,
290
- txCount: checkedTx.size,
300
+ const result = await resolver.runAsLambda(
301
+ () => {
302
+ executeCount++;
303
+ return execute();
291
304
  },
292
- };
305
+ { retryLimit: 0 }
306
+ );
307
+
308
+ return (
309
+ result || {
310
+ isRisky: false,
311
+ data: {
312
+ accountCount: checkedAccounts.size,
313
+ txCount: checkedTx.size,
314
+ },
315
+ }
316
+ );
293
317
  };
294
318
 
295
319
  const listTokenFlows = async (
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.18.159",
6
+ "version": "1.18.160",
7
7
  "description": "GraphQL resolver built upon ocap statedb and GQL layer",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -22,18 +22,18 @@
22
22
  "jest": "^29.7.0"
23
23
  },
24
24
  "dependencies": {
25
- "@arcblock/did": "1.18.159",
26
- "@arcblock/did-util": "1.18.159",
27
- "@arcblock/validator": "1.18.159",
28
- "@ocap/config": "1.18.159",
29
- "@ocap/indexdb": "1.18.159",
30
- "@ocap/mcrypto": "1.18.159",
31
- "@ocap/message": "1.18.159",
32
- "@ocap/state": "1.18.159",
33
- "@ocap/tx-protocols": "1.18.159",
34
- "@ocap/util": "1.18.159",
25
+ "@arcblock/did": "1.18.160",
26
+ "@arcblock/did-util": "1.18.160",
27
+ "@arcblock/validator": "1.18.160",
28
+ "@ocap/config": "1.18.160",
29
+ "@ocap/indexdb": "1.18.160",
30
+ "@ocap/mcrypto": "1.18.160",
31
+ "@ocap/message": "1.18.160",
32
+ "@ocap/state": "1.18.160",
33
+ "@ocap/tx-protocols": "1.18.160",
34
+ "@ocap/util": "1.18.160",
35
35
  "debug": "^4.3.6",
36
36
  "lodash": "^4.17.21"
37
37
  },
38
- "gitHead": "a3afce50d8c10223be711b86417b75fc59672e12"
38
+ "gitHead": "fe8f69251a9aa924e64895ac83307b2852e90def"
39
39
  }