@ocap/resolver 1.18.161 → 1.18.163

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
@@ -688,6 +688,8 @@ module.exports = class OCAPResolver {
688
688
  return tokenFlow.verifyAccountRisk(
689
689
  {
690
690
  ...args,
691
+ tokenAddress: toAddress(args.tokenAddress || this.config.token.address),
692
+ accountAddress: toAddress(args.accountAddress || ''),
691
693
  accountLimit: process.env.VERIFY_RISK_ACCOUNT_LIMIT,
692
694
  txLimit: process.env.VERIFY_RISK_TX_LIMIT,
693
695
  tolerance: process.env.VERIFY_RISK_TOLERANCE,
@@ -999,11 +1001,7 @@ module.exports = class OCAPResolver {
999
1001
  }
1000
1002
 
1001
1003
  if (typeUrl === 'fg:t:account_migrate') {
1002
- const fromState = await this.getAccountState({
1003
- address: tx.tx.from,
1004
- traceMigration: false,
1005
- expandContext: false,
1006
- });
1004
+ const fromState = await this.statedb.account.get(tx.tx.from, { traceMigration: false });
1007
1005
  if (fromState) {
1008
1006
  tx.receipts = getTxReceipts(tx, { config: this.config, fromState });
1009
1007
  }
@@ -1189,7 +1187,7 @@ module.exports = class OCAPResolver {
1189
1187
  this.listTransactions({
1190
1188
  paging,
1191
1189
  typeFilter: { types: ['account_migrate'] },
1192
- validityFilter: { validity: ['VALID'] },
1190
+ validityFilter: { validity: 'VALID' },
1193
1191
  })
1194
1192
  );
1195
1193
  const migrationChain = new MigrationChainManager();
package/lib/token-flow.js CHANGED
@@ -6,7 +6,6 @@ const { schemas, Joi } = require('@arcblock/validator');
6
6
  const { CustomError } = require('@ocap/util/lib/error');
7
7
  const uniq = require('lodash/uniq');
8
8
  const { FORGE_TOKEN_HOLDER } = require('@ocap/state/lib/states/tx');
9
- const debug = require('debug')(require('../package.json').name);
10
9
 
11
10
  const ZERO = new BN(0);
12
11
  const paramsSchema = Joi.object({
@@ -161,22 +160,31 @@ const verifyAccountRisk = async (
161
160
  throw new CustomError('INVALID_PARAMS', validation.error.message);
162
161
  }
163
162
 
163
+ const { logger } = resolver;
164
164
  const checkedAccounts = new Map();
165
165
  const checkedTx = new Map();
166
- const accountQueue = [accountAddress];
167
166
  const tokenState = await resolver.tokenCache.get(tokenAddress);
168
167
  const toleranceUnit = fromTokenToUnit(tolerance, tokenState?.decimal);
169
168
  const vaultAccounts = getVaultAccounts(resolver.config);
170
169
 
171
- let executeCount = 0;
170
+ const accountQueue = [[{ address: accountAddress, chain: [] }]];
172
171
 
173
- const execute = async (txn) => {
174
- if (executeCount > 1) {
175
- throw new CustomError('INVALID_REQUEST', 'verifyAccountRisk should not retry');
176
- }
177
- while (accountQueue.length) {
172
+ const execute = async (depth, txn) => {
173
+ const queue = accountQueue[depth];
174
+
175
+ for (let i = 0; i < queue.length; i++) {
178
176
  // limit
179
177
  if (checkedAccounts.size >= accountLimit) {
178
+ logger.warn('Account risk check reached max account size limit', {
179
+ address: accountAddress,
180
+ tokenAddress,
181
+ accountCount: checkedAccounts.size,
182
+ txCount: checkedTx.size,
183
+ depth,
184
+ accountLimit,
185
+ txLimit,
186
+ tolerance,
187
+ });
180
188
  return {
181
189
  isRisky: false,
182
190
  reason: 'MAX_ACCOUNT_SIZE_LIMIT',
@@ -187,7 +195,8 @@ const verifyAccountRisk = async (
187
195
  };
188
196
  }
189
197
 
190
- const address = accountQueue.pop();
198
+ const { address, chain } = queue[i];
199
+ chain.push(address);
191
200
 
192
201
  // Avoid circular query
193
202
  if (checkedAccounts.has(address)) continue;
@@ -199,18 +208,22 @@ const verifyAccountRisk = async (
199
208
  continue;
200
209
  }
201
210
 
202
- const balance = await getBalance(address, tokenAddress, { resolver, txn });
203
-
211
+ let balance = 0;
204
212
  let transactions = [];
213
+
205
214
  try {
206
- transactions = await resolver._getAllResults(
207
- 'transactions',
208
- (paging) => resolver.listTransactions({ paging, accountFilter: { accounts: [address] } }, ctx),
209
- txLimit
210
- );
215
+ [balance, transactions] = await Promise.all([
216
+ getBalance(address, tokenAddress, { resolver, txn }),
217
+ resolver._getAllResults(
218
+ 'transactions',
219
+ (paging) => resolver.listTransactions({ paging, accountFilter: { accounts: [address] } }, ctx),
220
+ txLimit
221
+ ),
222
+ ]);
211
223
  } catch (e) {
212
224
  // skip if tx limit exceeded
213
- if (e.code === 'EXCEED_LIMIT') {
225
+ if (e?.code === 'EXCEED_LIMIT') {
226
+ logger.warn('Skip checking account cause tx count exceeding limit', { address, txLimit });
214
227
  checkedAccounts.set(address, true);
215
228
  continue;
216
229
  }
@@ -230,90 +243,113 @@ const verifyAccountRisk = async (
230
243
  const { transferInList, transferOutList } = checkedTx.get(tx.hash);
231
244
 
232
245
  // 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
- );
246
+ const transferInAmount = transferInList
247
+ .filter((item) => isSameDid(item.address, address))
248
+ .map((item) => item.value)
249
+ .reduce((prev, cur) => prev.add(cur), ZERO);
239
250
 
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
- );
251
+ const transferOutAmount = transferOutList
252
+ .filter((item) => isSameDid(item.address, address))
253
+ .map((item) => item.value)
254
+ .reduce((prev, cur) => prev.add(cur), ZERO);
255
+
256
+ transferIn = transferIn.add(transferInAmount);
257
+ transferOut = transferOut.add(transferOutAmount);
246
258
 
247
259
  // push transferIn accounts to queue for next time check
248
- if (transferInList.some((item) => isSameDid(item.address, address))) {
260
+ if (transferInAmount.gt(ZERO)) {
261
+ if (!accountQueue[depth + 1]) {
262
+ accountQueue[depth + 1] = [];
263
+ }
249
264
  const accountsToQueue = transferOutList
250
265
  .filter((item) => {
251
- if (accountQueue.includes(item.address)) return false;
266
+ if (checkedAccounts.has(item.address)) return false;
267
+ // Skip not token holders
268
+ if (schemas.tokenHolder.validate(item.address).error) return false;
252
269
  // Skip vault accounts
253
270
  if (vaultAccounts.includes(item.address)) return false;
254
271
  // skip gas、fee
255
272
  if (['gas', 'fee'].includes(item.action)) return false;
256
- // Skip not token holders
257
- if (schemas.tokenHolder.validate(item.address).error) return false;
258
273
 
259
274
  return true;
260
275
  })
261
276
  .map((item) => item.address);
262
277
 
263
- accountQueue.push(...uniq(accountsToQueue));
278
+ accountQueue[depth + 1].push(...uniq(accountsToQueue).map((x) => ({ address: x, chain: chain.concat() })));
264
279
  }
265
280
  }
266
281
 
267
282
  checkedAccounts.set(address, true);
268
283
 
269
284
  // 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', {
278
- address,
285
+ const diff = transferIn
286
+ .sub(transferOut)
287
+ .sub(new BN(balance))
288
+ .add(fromTokenToUnit(trustedConfig?.tolerance || 0));
289
+
290
+ if (diff.abs().gt(toleranceUnit)) {
291
+ const data = {
292
+ address: chain.join('->'),
293
+ balance,
279
294
  transferIn: transferIn.toString(),
280
295
  transferOut: transferOut.toString(),
281
- balance,
296
+ accountCount: checkedAccounts.size,
297
+ txCount: checkedTx.size,
298
+ };
299
+ logger.warn('Account balance does not match transfer records', {
300
+ ...data,
282
301
  sourceAccount: accountAddress,
302
+ tokenAddress,
303
+ diff: diff.toString(),
304
+ depth,
305
+ accountLimit,
306
+ txLimit,
307
+ tolerance,
283
308
  });
284
309
  return {
285
310
  isRisky: true,
286
311
  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
- },
312
+ data,
295
313
  };
296
314
  }
297
315
  }
298
316
  };
299
317
 
300
- const result = await resolver.runAsLambda(
301
- (txn) => {
302
- executeCount++;
303
- return execute(txn);
304
- },
305
- { retryLimit: 0 }
306
- );
307
-
308
- return (
309
- result || {
310
- isRisky: false,
311
- data: {
312
- accountCount: checkedAccounts.size,
313
- txCount: checkedTx.size,
318
+ for (let depth = 0; depth < accountQueue.length; depth++) {
319
+ let isExecuted = false;
320
+ const result = await resolver.runAsLambda(
321
+ (txn) => {
322
+ if (isExecuted) {
323
+ throw new CustomError('INVALID_REQUEST', 'verifyAccountRisk should not retry');
324
+ }
325
+ isExecuted = true;
326
+ return execute(depth, txn);
314
327
  },
328
+ { retryLimit: 0 }
329
+ );
330
+ if (result) {
331
+ return result;
315
332
  }
316
- );
333
+ }
334
+
335
+ logger.info('Account risk check completed', {
336
+ address: accountAddress,
337
+ tokenAddress,
338
+ accountCount: checkedAccounts.size,
339
+ txCount: checkedTx.size,
340
+ depth: accountQueue.length,
341
+ accountLimit,
342
+ txLimit,
343
+ tolerance,
344
+ });
345
+
346
+ return {
347
+ isRisky: false,
348
+ data: {
349
+ accountCount: checkedAccounts.size,
350
+ txCount: checkedTx.size,
351
+ },
352
+ };
317
353
  };
318
354
 
319
355
  const listTokenFlows = async (
@@ -349,8 +385,6 @@ const listTokenFlows = async (
349
385
  if (checkedAccounts.has(address)) continue;
350
386
  // Skip not token holders
351
387
  if (schemas.tokenHolder.validate(address).error) continue;
352
- // Skip vault accounts
353
- if (vaultAccounts.includes(address)) continue;
354
388
 
355
389
  const transactions = await resolver._getAllResults('transactions', (page) =>
356
390
  resolver.listTransactions(
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.18.161",
6
+ "version": "1.18.163",
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.161",
26
- "@arcblock/did-util": "1.18.161",
27
- "@arcblock/validator": "1.18.161",
28
- "@ocap/config": "1.18.161",
29
- "@ocap/indexdb": "1.18.161",
30
- "@ocap/mcrypto": "1.18.161",
31
- "@ocap/message": "1.18.161",
32
- "@ocap/state": "1.18.161",
33
- "@ocap/tx-protocols": "1.18.161",
34
- "@ocap/util": "1.18.161",
25
+ "@arcblock/did": "1.18.163",
26
+ "@arcblock/did-util": "1.18.163",
27
+ "@arcblock/validator": "1.18.163",
28
+ "@ocap/config": "1.18.163",
29
+ "@ocap/indexdb": "1.18.163",
30
+ "@ocap/mcrypto": "1.18.163",
31
+ "@ocap/message": "1.18.163",
32
+ "@ocap/state": "1.18.163",
33
+ "@ocap/tx-protocols": "1.18.163",
34
+ "@ocap/util": "1.18.163",
35
35
  "debug": "^4.3.6",
36
36
  "lodash": "^4.17.21"
37
37
  },
38
- "gitHead": "5d8436fdf5218fcbf2218b77ae370d50617ba3d0"
38
+ "gitHead": "c4bb7fae24dbac7a56ffbb84d19f64b90ea770b7"
39
39
  }