@ocap/tx-protocols 1.18.21 → 1.18.23

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/execute.js CHANGED
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable consistent-return */
2
+ const { promisify } = require('util');
2
3
  const get = require('lodash/get');
3
4
  const pick = require('lodash/pick');
4
5
  const omit = require('lodash/omit');
@@ -6,6 +7,12 @@ const camelCase = require('lodash/camelCase');
6
7
  const { CustomError: Error } = require('@ocap/util/lib/error');
7
8
  const { Runner, pipes } = require('@ocap/tx-pipeline');
8
9
 
10
+ const createEnsureGasFn = require('./pipes/ensure-gas');
11
+ const createEnsureCostFn = require('./pipes/ensure-cost');
12
+
13
+ const ensureTxGas = promisify(createEnsureGasFn(() => ({ create: 0, update: 2 })));
14
+ const ensureTxCost = promisify(createEnsureCostFn({ attachSenderChanges: true, gasOnly: true }));
15
+
9
16
  const getTxName = (typeUrl) => camelCase(typeUrl.split(':').pop());
10
17
 
11
18
  const flushEvents = (context, extra = {}) => {
@@ -16,6 +23,7 @@ const flushEvents = (context, extra = {}) => {
16
23
  };
17
24
 
18
25
  const shouldPersistTx = (err) => !err || get(err, 'props.persist');
26
+ const logError = (...args) => process.env.NODE_ENV !== 'test' && console.error(...args);
19
27
 
20
28
  module.exports = ({ filter, runAsLambda }) => {
21
29
  // pipeline before executing the transaction
@@ -41,13 +49,33 @@ module.exports = ({ filter, runAsLambda }) => {
41
49
  pre.use(pipes.VerifyTxSize);
42
50
  pre.use(pipes.VerifySignature);
43
51
 
52
+ // charge gas fee for errored tx
53
+ const ensureGasFeePaid = async (context) => {
54
+ try {
55
+ await ensureTxGas(context);
56
+ await ensureTxCost(context);
57
+
58
+ const { tx, statedb, senderState, senderUpdates, updateVaults } = context;
59
+
60
+ await Promise.all([
61
+ statedb.account.update(
62
+ senderState.address,
63
+ context.states.account.update(senderState, { nonce: tx.nonce, pk: tx.pk, ...senderUpdates }, context),
64
+ context
65
+ ),
66
+
67
+ updateVaults(),
68
+ ]);
69
+ } catch (err) {
70
+ logError('Failed to charge gas fee for errored tx', err);
71
+ }
72
+ };
73
+
44
74
  const execute = async (context, protocols, isRetrySupported = false) =>
45
75
  new Promise((resolve, reject) => {
46
76
  pre.run(context, (err) => {
47
77
  if (err) {
48
- if (process.env.NODE_ENV !== 'test') {
49
- console.error('Failed to prepare transaction', err);
50
- }
78
+ logError('Failed to prepare transaction', err);
51
79
  return reject(err);
52
80
  }
53
81
 
@@ -62,12 +90,16 @@ module.exports = ({ filter, runAsLambda }) => {
62
90
  // we should only flush events when retry is not supported
63
91
  // otherwise the outer caller should handle these 2
64
92
  if (shouldPersistTx(err) && isRetrySupported === false) {
93
+ if (err) {
94
+ await ensureGasFeePaid(context);
95
+ }
96
+
65
97
  let txState;
66
98
  try {
67
99
  txState = context.states.tx.create(context, err ? err.code || 'INTERNAL' : 'OK');
68
100
  await context.statedb.tx.create(txState.hash, txState, context);
69
101
  } catch (e) {
70
- console.error('Failed to save transaction to statedb', e);
102
+ logError('Failed to save transaction to statedb', e);
71
103
  return reject(e);
72
104
  }
73
105
 
@@ -76,7 +108,7 @@ module.exports = ({ filter, runAsLambda }) => {
76
108
 
77
109
  // after executing the transaction
78
110
  if (err) {
79
- // console.error('Failed to execute transaction', err);
111
+ logError('Failed to execute transaction', err);
80
112
  return reject(err);
81
113
  }
82
114
 
@@ -102,15 +134,20 @@ module.exports = ({ filter, runAsLambda }) => {
102
134
  } finally {
103
135
  if (shouldPersistTx(error)) {
104
136
  try {
105
- const txState = context.states.tx.create(ctx, error ? error.code || 'INTERNAL' : 'OK');
137
+ let txState = context.states.tx.create(ctx, error ? error.code || 'INTERNAL' : 'OK');
106
138
  flushEvents(ctx, { txState });
107
139
  await runAsLambda(async (txn) => {
108
140
  const newCtx = { ...omit(ctx, ['txn']), txn };
141
+ if (error) {
142
+ await ensureGasFeePaid(newCtx);
143
+ // Recreate tx to pick up gas related fields
144
+ txState = context.states.tx.create(newCtx, error ? error.code || 'INTERNAL' : 'OK');
145
+ }
109
146
  await context.statedb.tx.create(txState.hash, txState, newCtx);
110
147
  flushEvents(newCtx, { txState });
111
148
  });
112
149
  } catch (err) {
113
- console.error('failed to save invalid transaction to statedb', err);
150
+ logError('failed to save invalid transaction to statedb', err);
114
151
  }
115
152
  }
116
153
  }
@@ -15,7 +15,7 @@ const { applyTokenUpdates, isGasStakeValid } = require('../util');
15
15
  // - gas: charged for every tx for creating/updating states on the ledger
16
16
  // - service fee: charged for creating tokens/assets/factories/rollups
17
17
  // - protocol fee: charged for moving tokens across the bridge
18
- module.exports = function CreateEnsureTxCostPipe({ attachSenderChanges = true } = {}) {
18
+ module.exports = function CreateEnsureTxCostPipe({ attachSenderChanges = true, gasOnly = false } = {}) {
19
19
  return async function EnsureTxCost(context, next) {
20
20
  // TODO: we are using the sender as gas payer, this may change in future
21
21
  const { config, statedb, txType, senderState, gasEstimate, gasVaultState, totalGas } = context;
@@ -25,7 +25,7 @@ module.exports = function CreateEnsureTxCostPipe({ attachSenderChanges = true }
25
25
  const changes = {};
26
26
  let txCost = new BN(0);
27
27
  const txFee = config.transaction.txFee[txType];
28
- if (txFee) {
28
+ if (!gasOnly && txFee) {
29
29
  const totalFee = fromTokenToUnit(txFee, config.token.decimal);
30
30
  txCost = txCost.add(totalFee);
31
31
  changes.fee = { address: config.token.address, value: totalFee.toString(10) };
@@ -145,6 +145,8 @@ module.exports = function CreateEnsureTxCostPipe({ attachSenderChanges = true }
145
145
  });
146
146
  }
147
147
  }
148
+
149
+ context.gasPaid = true;
148
150
  };
149
151
  } else {
150
152
  context.senderUpdates = {};
@@ -79,20 +79,23 @@ runner.use(
79
79
  ])
80
80
  );
81
81
 
82
- // Ensure uniq ness of token symbol
83
- runner.use(async (context, next) => {
84
- const { symbol } = context.config.token;
85
- if (symbol.toLowerCase() === context.itx.symbol.toLowerCase()) {
86
- return next(new Error('DUPLICATE_SYMBOL', `Token symbol can not be ${symbol}`));
87
- }
82
+ // Ensure uniqueness of token symbol
83
+ runner.use(
84
+ async function EnsureTokenSymbol(context, next) {
85
+ const { symbol } = context.config.token;
86
+ if (symbol.toLowerCase() === context.itx.symbol.toLowerCase()) {
87
+ return next(new Error('DUPLICATE_SYMBOL', `Token symbol can not be ${symbol}`));
88
+ }
88
89
 
89
- const exist = await context.statedb.token.existBySymbol(context.itx.symbol, context);
90
- if (exist) {
91
- return next(new Error('DUPLICATE_SYMBOL', 'Token symbol already exists'));
92
- }
90
+ const exist = await context.statedb.token.existBySymbol(context.itx.symbol, context);
91
+ if (exist) {
92
+ return next(new Error('DUPLICATE_SYMBOL', 'Token symbol already exists'));
93
+ }
93
94
 
94
- return next();
95
- });
95
+ return next();
96
+ },
97
+ { persistError: true }
98
+ );
96
99
 
97
100
  // Ensure tx fee and gas
98
101
  runner.use(
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.18.21",
6
+ "version": "1.18.23",
7
7
  "description": "Predefined tx pipeline sets to execute certain type of transactions",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -21,18 +21,18 @@
21
21
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "@arcblock/did": "1.18.21",
25
- "@arcblock/did-util": "1.18.21",
26
- "@arcblock/jwt": "1.18.21",
27
- "@arcblock/validator": "1.18.21",
28
- "@ocap/asset": "1.18.21",
29
- "@ocap/mcrypto": "1.18.21",
30
- "@ocap/merkle-tree": "1.18.21",
31
- "@ocap/message": "1.18.21",
32
- "@ocap/state": "1.18.21",
33
- "@ocap/tx-pipeline": "1.18.21",
34
- "@ocap/util": "1.18.21",
35
- "@ocap/wallet": "1.18.21",
24
+ "@arcblock/did": "1.18.23",
25
+ "@arcblock/did-util": "1.18.23",
26
+ "@arcblock/jwt": "1.18.23",
27
+ "@arcblock/validator": "1.18.23",
28
+ "@ocap/asset": "1.18.23",
29
+ "@ocap/mcrypto": "1.18.23",
30
+ "@ocap/merkle-tree": "1.18.23",
31
+ "@ocap/message": "1.18.23",
32
+ "@ocap/state": "1.18.23",
33
+ "@ocap/tx-pipeline": "1.18.23",
34
+ "@ocap/util": "1.18.23",
35
+ "@ocap/wallet": "1.18.23",
36
36
  "debug": "^4.3.4",
37
37
  "deep-diff": "^1.0.2",
38
38
  "empty-value": "^1.0.1",
@@ -47,5 +47,5 @@
47
47
  "jest": "^27.5.1",
48
48
  "start-server-and-test": "^1.14.0"
49
49
  },
50
- "gitHead": "be5df6f70d48c8c5026cd7bb42d53b270ddeb344"
50
+ "gitHead": "bb57afdd756f4a9608e53e40c91060c32b97f2f8"
51
51
  }