@ocap/tx-pipeline 1.18.164 → 1.18.165

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.
@@ -5,24 +5,27 @@ module.exports = function CreateTakeStateSnapshotPipe({ key = 'stateSnapshot' }
5
5
  return async function TakeStateSnapshot(context, next) {
6
6
  const { statedb, config } = context;
7
7
 
8
- const statesMap = groupStatesByAddress(context);
9
-
10
- // feeVaultState is usually add to context at the end, so we need to query it here
11
- if (!statesMap[config.vaults.txFee]) {
12
- statesMap[config.vaults.txFee] = [await statedb.account.get(config.vaults.txFee, context)];
8
+ // feeVaultState and gasVaultState is usually add to context at the end, so we need to query it here
9
+ if (!context.feeVaultState) {
10
+ context.feeVaultState = await statedb.account.get(config.vaults.txFee, context);
11
+ }
12
+ if (context.gasVault && !context.gasVaultState) {
13
+ context.gasVaultState = await statedb.account.get(context.gasVault, context);
13
14
  }
14
15
 
16
+ const statesMap = groupStatesByAddress(context);
17
+
15
18
  // for acquire transactions, we should pre-fetch the account state in the mint hooks to verify their balance changes later
16
19
  if (context.factoryState && ['fg:t:acquire_asset_v3', 'fg:t:acquire_asset_v2'].includes(context.txType)) {
17
20
  const mintHook = context.factoryState.hooks?.find((x) => x.name === 'mint');
18
21
  const states = await Promise.all(
19
22
  (mintHook?.compiled || []).map((x) => {
20
23
  const address = x.args.to;
21
- return statesMap[address]?.[0] || statedb.account.get(address, context);
24
+ return statesMap[address] || statedb.account.get(address, context);
22
25
  })
23
26
  );
24
27
  states.forEach((x) => {
25
- statesMap[x.address] = [x];
28
+ statesMap[x.address] = x;
26
29
  });
27
30
  }
28
31
 
@@ -15,24 +15,25 @@ const { pickBy } = require('lodash');
15
15
 
16
16
  function groupStatesByAddress(context) {
17
17
  /**
18
- * a map of address -> state list
19
- * @type {Record<string, []>}
18
+ * a map of address -> state
19
+ * @type {Record<string, any>}
20
20
  */
21
21
  const statesMap = {};
22
22
 
23
- for (const [stateKey, stateOrStates] of Object.entries(context)) {
23
+ for (const stateOrStates of Object.values(context)) {
24
24
  const states = [].concat(stateOrStates).filter((x) => x && x.address && x.tokens && x.context);
25
25
 
26
26
  for (const state of states) {
27
- // mintHookStates has the highest priority
28
- if (stateKey === 'mintHookStates') {
29
- statesMap[state.address] = [state];
27
+ const curStates = statesMap[state.address];
28
+
29
+ if (!curStates) {
30
+ statesMap[state.address] = state;
30
31
  continue;
31
32
  }
32
- // skip state with the same tokens as existing states
33
- const curStates = statesMap[state.address] || [];
34
- if (curStates.every((x) => !isEqual(x.tokens, state.tokens))) {
35
- statesMap[state.address] = curStates.concat(state);
33
+ // Use the state that recently retrieved from database
34
+ if ((state._retrievedAt || 0) > (curStates._retrievedAt || 0)) {
35
+ statesMap[state.address] = state;
36
+ continue;
36
37
  }
37
38
  }
38
39
  }
@@ -46,11 +47,11 @@ function findStates(address, statesMap) {
46
47
  }
47
48
  // Try to find state from migrated addresses
48
49
  const matchedAddress = Object.keys(statesMap).find((x) => {
49
- const state = statesMap[x][0];
50
+ const state = statesMap[x];
50
51
  const relatedAddresses = getRelatedAddresses(state);
51
52
  return !state.migratedTo?.length && relatedAddresses.some((y) => isSameDid(y, address));
52
53
  });
53
- return statesMap[matchedAddress] || [];
54
+ return statesMap[matchedAddress];
54
55
  }
55
56
 
56
57
  function getStateTokens(state, txType) {
@@ -74,7 +75,7 @@ function mergeTokenChange(token1, token2, operator = 'add') {
74
75
  * Update migrated addresses in receipts to their latest addresses
75
76
  */
76
77
  function fixMigratedReceipts(receipts, statesMap) {
77
- const states = Object.values(statesMap).flat();
78
+ const states = Object.values(statesMap);
78
79
  const fixedReceipts = receipts.map((receipt) => {
79
80
  const state = states.find((x) => {
80
81
  const relatedAddresses = getRelatedAddresses(x);
@@ -109,10 +110,10 @@ module.exports = function CreateVerifyStateDiffPipe({ snapshotKey = 'stateSnapsh
109
110
  // verify: receipts -> state diff
110
111
  // before tokens + receipt changes = after tokens
111
112
  for (const [address, tokenChanges] of Object.entries(receiptChanges)) {
112
- const beforeState = findStates(address, snapshotStates)[0];
113
- const afterStates = findStates(address, contextStates);
113
+ const beforeState = findStates(address, snapshotStates);
114
+ const afterState = findStates(address, contextStates);
114
115
 
115
- if (!afterStates.length) {
116
+ if (!afterState) {
116
117
  logger?.error('INVALID_RECEIPT', 'Changed state not found for receipt', {
117
118
  address,
118
119
  context,
@@ -126,14 +127,14 @@ module.exports = function CreateVerifyStateDiffPipe({ snapshotKey = 'stateSnapsh
126
127
  const expectedTokens = beforeState
127
128
  ? mergeTokenChange(getStateTokens(beforeState, txState.type), tokenChanges)
128
129
  : tokenChanges;
129
- const matchAfterState = afterStates.find((x) => isEqual(expectedTokens, getStateTokens(x, txState.type)));
130
+ const isMatchExpected = isEqual(expectedTokens, getStateTokens(afterState, txState.type));
130
131
 
131
- if (!matchAfterState) {
132
+ if (!isMatchExpected) {
132
133
  logger?.error('INVALID_RECEIPT', 'Tx receipts does not match with state diff', {
133
134
  txState,
134
135
  expectedTokens,
135
136
  beforeState,
136
- afterStates,
137
+ afterState,
137
138
  tokenChanges,
138
139
  });
139
140
  return next(new Error('INVALID_RECEIPT', 'Tx receipts does not match with state diff'));
@@ -142,45 +143,37 @@ module.exports = function CreateVerifyStateDiffPipe({ snapshotKey = 'stateSnapsh
142
143
 
143
144
  // verify: state diff -> receipts
144
145
  // after tokens - before tokens = receipt changes
145
- for (const [address, afterStates] of Object.entries(contextStates)) {
146
- const beforeState = findStates(address, snapshotStates)[0];
147
- const beforeTokens = beforeState ? getStateTokens(beforeState, txState.type) : {};
146
+ for (const [address, afterState] of Object.entries(contextStates)) {
148
147
  const roleType = toTypeInfo(address).role;
149
-
150
- // there might be multiple afterStates found, e.g. when signerState and senderState have the same address,
151
- // and it's possible that senderState has updated tokens but signerState hasn't.
152
- // so we need to verify each afterState. there are two cases:
153
- // 1. state with updated tokens: their tokens differ from beforeState.tokens, so they will enter verification logic
154
- // 2. state with no updated tokens: their tokens match beforeState.tokens, so they will skip verification logic
155
- for (const afterState of afterStates) {
156
- const afterTokens = getStateTokens(afterState, txState.type);
157
- if (!beforeState || !isEqual(afterTokens, beforeTokens)) {
158
- const tokenChange = pickBy(
159
- beforeState ? mergeTokenChange(afterTokens, beforeTokens, 'sub') : afterTokens,
148
+ const beforeState = findStates(address, snapshotStates);
149
+ const beforeTokens = beforeState ? getStateTokens(beforeState, txState.type) : {};
150
+ const afterTokens = getStateTokens(afterState, txState.type);
151
+
152
+ if (!beforeState || !isEqual(afterTokens, beforeTokens)) {
153
+ const tokenChange = pickBy(
154
+ beforeState ? mergeTokenChange(afterTokens, beforeTokens, 'sub') : afterTokens,
155
+ (value) => value !== '0'
156
+ );
157
+ let expectedChange = pickBy(receiptChanges[address] || {}, (value) => value !== '0');
158
+
159
+ // revoke_stake only moves tokens from staked to revoked tokens and without generating any receipts
160
+ // after tokens - before tokens = before revoke tokens - after revoke tokens
161
+ if (txState.type === 'revoke_stake' && roleType === RoleType.ROLE_STAKE) {
162
+ expectedChange = pickBy(
163
+ mergeTokenChange(beforeState.revokedTokens, afterState.revokedTokens, 'sub'),
160
164
  (value) => value !== '0'
161
165
  );
162
- let expectedChange = receiptChanges[address] || {};
163
-
164
- // revoke_stake only moves tokens from staked to revoked tokens and without generating any receipts
165
- // after tokens - before tokens = before revoke tokens - after revoke tokens
166
- if (txState.type === 'revoke_stake' && roleType === RoleType.ROLE_STAKE) {
167
- expectedChange = pickBy(
168
- mergeTokenChange(beforeState.revokedTokens, afterState.revokedTokens, 'sub'),
169
- (value) => value !== '0'
170
- );
171
- }
172
-
173
- if (!isEqual(tokenChange, expectedChange)) {
174
- logger?.error('INVALID_RECEIPT', 'State diff does not match with tx receipts', {
175
- txState,
176
- beforeState,
177
- afterState,
178
- afterStates,
179
- tokenChange,
180
- expectedChange,
181
- });
182
- return next(new Error('INVALID_RECEIPT', 'State diff does not match with tx receipts'));
183
- }
166
+ }
167
+
168
+ if (!isEqual(tokenChange, expectedChange)) {
169
+ logger?.error('INVALID_RECEIPT', 'State diff does not match with tx receipts', {
170
+ txState,
171
+ beforeState,
172
+ afterState,
173
+ tokenChange,
174
+ expectedChange,
175
+ });
176
+ return next(new Error('INVALID_RECEIPT', 'State diff does not match with tx receipts'));
184
177
  }
185
178
  }
186
179
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.18.164",
6
+ "version": "1.18.165",
7
7
  "description": "Pipeline runner and common pipelines to process transactions",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -29,16 +29,16 @@
29
29
  "elliptic": "6.5.3"
30
30
  },
31
31
  "dependencies": {
32
- "@arcblock/did": "1.18.164",
33
- "@arcblock/did-util": "1.18.164",
34
- "@ocap/mcrypto": "1.18.164",
35
- "@ocap/message": "1.18.164",
36
- "@ocap/state": "1.18.164",
37
- "@ocap/util": "1.18.164",
38
- "@ocap/wallet": "1.18.164",
32
+ "@arcblock/did": "1.18.165",
33
+ "@arcblock/did-util": "1.18.165",
34
+ "@ocap/mcrypto": "1.18.165",
35
+ "@ocap/message": "1.18.165",
36
+ "@ocap/state": "1.18.165",
37
+ "@ocap/util": "1.18.165",
38
+ "@ocap/wallet": "1.18.165",
39
39
  "debug": "^4.3.6",
40
40
  "empty-value": "^1.0.1",
41
41
  "lodash": "^4.17.21"
42
42
  },
43
- "gitHead": "f517d0620f7fb4acd26716ab76f68829587b6483"
43
+ "gitHead": "e1a9e9e807ca049f67ffbb5ac556b014461cb7a1"
44
44
  }