@ocap/tx-pipeline 1.18.164 → 1.18.165

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }