@ocap/state 1.18.137 → 1.18.138

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.
Files changed (2) hide show
  1. package/lib/states/tx.js +109 -0
  2. package/package.json +14 -10
package/lib/states/tx.js CHANGED
@@ -7,8 +7,12 @@ const camelCase = require('lodash/camelCase');
7
7
  const { fromTypeUrl, formatMessage, decodeAny } = require('@ocap/message');
8
8
  const { toBase64, BN, isBN } = require('@ocap/util');
9
9
  const { getListField } = require('@ocap/util/lib/get-list-field');
10
+ const { CustomError: Error } = require('@ocap/util/lib/error');
11
+ const { getRelatedAddresses } = require('@ocap/util/lib/get-related-addr');
12
+ const { toTypeInfo, types } = require('@arcblock/did');
10
13
 
11
14
  const ZERO = new BN(0);
15
+ const { RoleType } = types;
12
16
 
13
17
  // Receiver = Whose asset have net gain
14
18
  const getTxReceiver = ({ tx, itx, typeUrl }) => {
@@ -323,6 +327,106 @@ const mergeTxReceipts = (receipts) => {
323
327
  return Object.values(merged);
324
328
  };
325
329
 
330
+ /**
331
+ * Get list of accounts with migration history
332
+ * @param {Object} ctx - transaction execution context
333
+ * @returns {Promise<string[][]>} adress[][]
334
+ */
335
+ const getAccountMigrationsFromContext = (ctx) => {
336
+ const migrationAddresses = [];
337
+ for (const [key, state] of Object.entries(ctx)) {
338
+ if (key.endsWith('State') && state?.address) {
339
+ migrationAddresses.push(getRelatedAddresses(state));
340
+ }
341
+ if (key.endsWith('States') && Array.isArray(state)) {
342
+ state.forEach((x) => {
343
+ if (x?.address) {
344
+ migrationAddresses.push(getRelatedAddresses(x));
345
+ }
346
+ });
347
+ }
348
+ }
349
+ return migrationAddresses;
350
+ };
351
+
352
+ /**
353
+ * Group receipts by target
354
+ * @param {Object[]} receipts - transaction receipts
355
+ * @returns {Record<string, { address: string, value: BN, action: string }[]>}
356
+ */
357
+ const groupReceiptsByTarget = (receipts) => {
358
+ const targets = {};
359
+
360
+ for (const { address, changes } of receipts) {
361
+ for (const { target, value, action } of changes) {
362
+ if (!targets[target]) {
363
+ targets[target] = [];
364
+ }
365
+ targets[target].push({ address, value: new BN(value), action });
366
+ }
367
+ }
368
+ return targets;
369
+ };
370
+
371
+ /**
372
+ * Verify transaction receipts
373
+ * @param {Object[]} receipts - transaction receipts
374
+ * @param {String} typeUrl - transaction type
375
+ * @param {Object} ctx - transaction execution context
376
+ * @returns {Promise<boolean>}
377
+ */
378
+ const verifyTxReceipts = (receipts, typeUrl, ctx = {}) => {
379
+ const targets = groupReceiptsByTarget(receipts);
380
+ const accountsWithMigration = getAccountMigrationsFromContext(ctx);
381
+ const skipActions =
382
+ {
383
+ MintAssetTx: ['mint', 'consume'],
384
+ AcquireAssetV2Tx: ['mint', 'consume'],
385
+ AcquireAssetV3Tx: ['mint', 'consume'],
386
+ CreateAssetTx: ['create'],
387
+ CreateTokenTx: ['mint'],
388
+ CreateRollupBlockTx: ['burn', 'mint'],
389
+ }[typeUrl] || [];
390
+
391
+ for (const [target, changes] of Object.entries(targets)) {
392
+ // These actions will skip zero-sum calculation
393
+ const filteredChanges = changes.filter(({ action }) => {
394
+ if (!skipActions.includes(action)) return true;
395
+ if (action === 'consume' && toTypeInfo(target).role !== RoleType.ROLE_ASSET) return true;
396
+ return false;
397
+ });
398
+
399
+ // Ensure token/asset amounts are zero-sum
400
+ const sum = filteredChanges.reduce((prev, current) => prev.add(current.value), new BN(0));
401
+ if (!sum.eq(ZERO)) {
402
+ throw new Error('INVALID_RECEIPTS_VALUE', `Receipts are not zero-sum, target: ${target}, sum: ${sum}`);
403
+ }
404
+
405
+ // Ensure address is unique
406
+ const accountValue = {};
407
+ for (const { address, value, action } of filteredChanges) {
408
+ const accounts = accountsWithMigration.find((x) => x.includes(address)) || [address];
409
+ for (const account of accounts) {
410
+ const key = `${action}-${account}`;
411
+ const existingValue = accountValue[key];
412
+ if (
413
+ existingValue &&
414
+ ((existingValue.gt(ZERO) && value.lt(ZERO)) || (existingValue.lt(ZERO) && value.gt(ZERO)))
415
+ ) {
416
+ throw new Error(
417
+ 'INVALID_RECEIPTS_ADDRESS',
418
+ `Duplicate accounts in receipts, target: ${target}, address: ${address}, action: ${action}`
419
+ );
420
+ }
421
+
422
+ accountValue[key] = value;
423
+ }
424
+ }
425
+ }
426
+
427
+ return true;
428
+ };
429
+
326
430
  /**
327
431
  * Create transaction receipts, each receipt has following properties:
328
432
  *
@@ -440,6 +544,10 @@ const create = (context, code = 'OK') => {
440
544
 
441
545
  result.receipts = getTxReceipts(result, context);
442
546
 
547
+ if (code === 'OK') {
548
+ verifyTxReceipts(result.receipts, typeUrl, context);
549
+ }
550
+
443
551
  // determine gasPaid
444
552
  attachPaidTxGas(result);
445
553
 
@@ -475,4 +583,5 @@ module.exports = {
475
583
  getTxReceipts,
476
584
  mergeTxReceipts,
477
585
  attachPaidTxGas,
586
+ verifyTxReceipts,
478
587
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.18.137",
6
+ "version": "1.18.138",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -12,24 +12,28 @@
12
12
  "scripts": {
13
13
  "lint": "eslint tests lib",
14
14
  "lint:fix": "eslint --fix tests lib",
15
+ "start": "node tools/start-chain.js",
15
16
  "test": "jest --forceExit --detectOpenHandles",
16
- "coverage": "npm run test -- --coverage"
17
+ "test:ci": "jest --forceExit --detectOpenHandles --coverage",
18
+ "coverage": "start-server-and-test start http://127.0.0.1:4001 test:ci"
17
19
  },
18
20
  "dependencies": {
19
- "@arcblock/did": "1.18.137",
20
- "@arcblock/validator": "1.18.137",
21
- "@ocap/contract": "1.18.137",
22
- "@ocap/mcrypto": "1.18.137",
23
- "@ocap/message": "1.18.137",
24
- "@ocap/util": "1.18.137",
21
+ "@arcblock/did": "1.18.138",
22
+ "@arcblock/validator": "1.18.138",
23
+ "@ocap/contract": "1.18.138",
24
+ "@ocap/mcrypto": "1.18.138",
25
+ "@ocap/message": "1.18.138",
26
+ "@ocap/util": "1.18.138",
27
+ "@ocap/wallet": "1.18.138",
25
28
  "bloom-filters": "^1.3.9",
26
29
  "lodash": "^4.17.21"
27
30
  },
28
31
  "devDependencies": {
29
- "jest": "^29.7.0"
32
+ "jest": "^29.7.0",
33
+ "start-server-and-test": "^1.14.0"
30
34
  },
31
35
  "keywords": [],
32
36
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
33
37
  "license": "MIT",
34
- "gitHead": "e9ebb514f4dfde8347992f0ab275b9d269d443c5"
38
+ "gitHead": "61ab154a7f575a2e3eb1c9ce86e1cdd2ce18e928"
35
39
  }