@ocap/tx-protocols 1.19.15 → 1.19.16
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/LICENSE +1 -1
- package/lib/execute.js +67 -41
- package/lib/pipes/ensure-cost.js +18 -7
- package/lib/pipes/ensure-gas.js +2 -2
- package/lib/protocols/asset/pipes/exec-mint-hook.js +1 -0
- package/lib/protocols/trade/transfer-v2.js +34 -23
- package/package.json +14 -14
package/LICENSE
CHANGED
package/lib/execute.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
const { promisify } = require('util');
|
|
3
3
|
const get = require('lodash/get');
|
|
4
4
|
const pick = require('lodash/pick');
|
|
5
|
-
const omit = require('lodash/omit');
|
|
6
5
|
const camelCase = require('lodash/camelCase');
|
|
7
6
|
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
8
7
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
@@ -132,49 +131,76 @@ module.exports = ({ filter, runAsLambda }) => {
|
|
|
132
131
|
return async (context, protocols) => {
|
|
133
132
|
let ctx = null;
|
|
134
133
|
let error = null;
|
|
134
|
+
let runCount = 0;
|
|
135
|
+
const retryLimit = context.statedb?.config?.retryLimit || 0;
|
|
136
|
+
const shouldRetry = context.statedb?.config?.shouldRetry || (() => false);
|
|
137
|
+
const startTime = Date.now();
|
|
138
|
+
|
|
139
|
+
await runAsLambda(async (txn) => {
|
|
140
|
+
runCount += 1;
|
|
141
|
+
|
|
142
|
+
// create a new context each time in case we are retrying
|
|
143
|
+
ctx = pick(context, ['txBase64', 'statedb', 'indexdb', 'config', 'states', 'filter', 'extra', 'logger']);
|
|
144
|
+
Object.defineProperty(ctx, 'txn', { value: txn });
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
await execute(ctx, protocols, true);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
if (runCount <= retryLimit && shouldRetry(err)) {
|
|
150
|
+
// throw the error to retry
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
153
|
+
error = err;
|
|
154
|
+
}
|
|
135
155
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (error) {
|
|
153
|
-
await ensureGasFeePaid(ctx);
|
|
154
|
-
// Recreate tx to pick up gas related fields
|
|
155
|
-
txState = context.states.tx.create(ctx, error ? error.code || 'INTERNAL' : 'OK');
|
|
156
|
-
}
|
|
157
|
-
await context.statedb.tx.create(txState.hash, txState, ctx);
|
|
158
|
-
});
|
|
159
|
-
flushEvents(ctx, { txState });
|
|
160
|
-
ctx?.logger?.info('Tx finalized', {
|
|
161
|
-
txHash: ctx.txHash,
|
|
162
|
-
txStatus: error ? error.code || 'INTERNAL' : 'OK',
|
|
163
|
-
txState,
|
|
164
|
-
error,
|
|
165
|
-
});
|
|
166
|
-
} catch (err) {
|
|
167
|
-
const txState = context.states.tx.create(ctx, error ? error.code || 'INTERNAL' : 'OK', false);
|
|
168
|
-
ctx?.logger?.error('Failed to save invalid transaction to statedb', {
|
|
169
|
-
error: err,
|
|
170
|
-
txHash: ctx.txHash,
|
|
171
|
-
txState,
|
|
172
|
-
});
|
|
156
|
+
if (error && !shouldPersistTx(error)) {
|
|
157
|
+
ctx.logger?.error('Failed to execute transaction', { error, txHash: ctx.txHash });
|
|
158
|
+
// throw the error to the abort transaction
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// create tx
|
|
163
|
+
const txStatus = error ? error.code || 'INTERNAL' : 'OK';
|
|
164
|
+
try {
|
|
165
|
+
let txState = context.states.tx.create(ctx, txStatus);
|
|
166
|
+
flushEvents(ctx, { txState });
|
|
167
|
+
|
|
168
|
+
if (error) {
|
|
169
|
+
await ensureGasFeePaid(ctx);
|
|
170
|
+
// Recreate tx to pick up gas related fields
|
|
171
|
+
txState = context.states.tx.create(ctx, txStatus);
|
|
173
172
|
}
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
|
|
174
|
+
await context.statedb.tx.create(txState.hash, txState, ctx);
|
|
175
|
+
flushEvents(ctx, { txState });
|
|
176
|
+
|
|
177
|
+
ctx.logger?.info('Tx finalized', {
|
|
178
|
+
txHash: ctx.txHash,
|
|
179
|
+
txStatus,
|
|
180
|
+
txState,
|
|
181
|
+
error,
|
|
182
|
+
runCount,
|
|
183
|
+
duration: Date.now() - startTime,
|
|
184
|
+
});
|
|
185
|
+
} catch (err) {
|
|
186
|
+
if (runCount <= retryLimit && shouldRetry(err)) {
|
|
187
|
+
// throw error to retry
|
|
188
|
+
throw err;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const txState = context.states.tx.create(ctx, txStatus, false);
|
|
192
|
+
ctx.logger?.error('Failed to save invalid transaction to statedb', {
|
|
193
|
+
error: err,
|
|
194
|
+
protocolError: error,
|
|
195
|
+
txHash: ctx.txHash,
|
|
196
|
+
txState,
|
|
197
|
+
duration: Date.now() - startTime,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// throw error to abort transaction
|
|
201
|
+
throw err;
|
|
176
202
|
}
|
|
177
|
-
}
|
|
203
|
+
});
|
|
178
204
|
|
|
179
205
|
if (error) {
|
|
180
206
|
throw error;
|
package/lib/pipes/ensure-cost.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const noop = require('lodash/noop');
|
|
2
2
|
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
3
|
-
const { fromTokenToUnit, BN, fromUnitToToken } = require('@ocap/util');
|
|
3
|
+
const { fromTokenToUnit, BN, fromUnitToToken, hexToNumber } = require('@ocap/util');
|
|
4
4
|
const JWT = require('@arcblock/jwt');
|
|
5
5
|
const { account } = require('@ocap/state');
|
|
6
6
|
const { toAddress } = require('@arcblock/did');
|
|
@@ -71,6 +71,17 @@ module.exports = function CreateEnsureTxCostPipe({
|
|
|
71
71
|
context.gasVaultChange = changes.gas;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
// pick fee vault
|
|
75
|
+
if (changes.fee) {
|
|
76
|
+
if (config.vaults.txFees?.length) {
|
|
77
|
+
const feeIndex = hexToNumber(txHash.slice(-8)) % config.vaults.txFees.length;
|
|
78
|
+
const feeVault = config.vaults.txFees[feeIndex];
|
|
79
|
+
context.feeVault = feeVault;
|
|
80
|
+
} else {
|
|
81
|
+
context.feeVault = config.vaults.txFee;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
74
85
|
let isCostCharged = false;
|
|
75
86
|
if (senderState && txCost.gt(ZERO)) {
|
|
76
87
|
const expected = new BN(gasEstimate.payment || 0).add(txCost);
|
|
@@ -96,8 +107,8 @@ module.exports = function CreateEnsureTxCostPipe({
|
|
|
96
107
|
// to be called in later pipes
|
|
97
108
|
context.updateVaults = async function updateVaults() {
|
|
98
109
|
const [feeVaultState, gasVaultState] = await Promise.all([
|
|
99
|
-
statedb.account.get(
|
|
100
|
-
statedb.account.get(gasVault, context),
|
|
110
|
+
changes.fee ? statedb.account.get(context.feeVault, context) : null,
|
|
111
|
+
changes.gas ? statedb.account.get(gasVault, context) : null,
|
|
101
112
|
]);
|
|
102
113
|
|
|
103
114
|
const [newFeeVaultState, newGasVaultState] = await Promise.all([
|
|
@@ -107,18 +118,18 @@ module.exports = function CreateEnsureTxCostPipe({
|
|
|
107
118
|
account.update(feeVaultState, applyTokenUpdates([changes.fee], feeVaultState, 'add'), context),
|
|
108
119
|
context
|
|
109
120
|
)
|
|
110
|
-
:
|
|
121
|
+
: null,
|
|
111
122
|
changes.gas
|
|
112
123
|
? statedb.account.update(
|
|
113
124
|
gasVaultState.address,
|
|
114
125
|
account.update(gasVaultState, applyTokenUpdates([changes.gas], gasVaultState, 'add'), context),
|
|
115
126
|
context
|
|
116
127
|
)
|
|
117
|
-
:
|
|
128
|
+
: null,
|
|
118
129
|
]);
|
|
119
130
|
|
|
120
|
-
context.feeVaultState = newFeeVaultState;
|
|
121
|
-
context.gasVaultState = newGasVaultState;
|
|
131
|
+
if (newFeeVaultState) context.feeVaultState = newFeeVaultState;
|
|
132
|
+
if (newGasVaultState) context.gasVaultState = newGasVaultState;
|
|
122
133
|
|
|
123
134
|
context.updatedAccounts = context.updatedAccounts || [];
|
|
124
135
|
if (changes.fee) {
|
package/lib/pipes/ensure-gas.js
CHANGED
|
@@ -27,8 +27,8 @@ module.exports = function CreateGasEnsureFn(estimateTxGas) {
|
|
|
27
27
|
|
|
28
28
|
// gas receiver address
|
|
29
29
|
const { txGas: gasVaults } = config.vaults;
|
|
30
|
-
const
|
|
31
|
-
const gasVault = gasVaults[
|
|
30
|
+
const gasIndex = hexToNumber(txHash.slice(-8)) % gasVaults.length;
|
|
31
|
+
const gasVault = gasVaults[gasIndex];
|
|
32
32
|
context.gasVault = gasVault;
|
|
33
33
|
|
|
34
34
|
debug({
|
|
@@ -6,9 +6,7 @@ const { Joi, schemas } = require('@arcblock/validator');
|
|
|
6
6
|
const { BN } = require('@ocap/util');
|
|
7
7
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
8
8
|
const { account, delegation } = require('@ocap/state');
|
|
9
|
-
|
|
10
|
-
// eslint-disable-next-line global-require
|
|
11
|
-
const debug = require('debug')(`${require('../../../package.json').name}:transfer-v2`);
|
|
9
|
+
const { toStakeAddress } = require('@arcblock/did-util');
|
|
12
10
|
|
|
13
11
|
const EnsureTxGas = require('../../pipes/ensure-gas');
|
|
14
12
|
const EnsureTxCost = require('../../pipes/ensure-cost');
|
|
@@ -82,7 +80,38 @@ runner.use((context, next) => {
|
|
|
82
80
|
next();
|
|
83
81
|
});
|
|
84
82
|
|
|
85
|
-
runner.use(
|
|
83
|
+
runner.use(
|
|
84
|
+
EnsureTxGas((context) => {
|
|
85
|
+
// FIXME: payment check
|
|
86
|
+
const result = { create: 0, update: 2, payment: 0 };
|
|
87
|
+
result.update += context.assetStates?.length || 0;
|
|
88
|
+
|
|
89
|
+
if (context.receiverState) {
|
|
90
|
+
result.update += 1;
|
|
91
|
+
} else {
|
|
92
|
+
result.create += 1;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return result;
|
|
96
|
+
})
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Pre-query states to cache them for later use
|
|
100
|
+
runner.use(async (context, next) => {
|
|
101
|
+
const { statedb, tx, gasVault } = context;
|
|
102
|
+
const accounts = [tx.from, context.receiver, gasVault].filter(Boolean);
|
|
103
|
+
|
|
104
|
+
await Promise.all([
|
|
105
|
+
...accounts.map((x) => statedb.account.get(x, context)),
|
|
106
|
+
statedb.stake.get(toStakeAddress(tx.from, tx.from), context),
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
next();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
runner.use(
|
|
113
|
+
pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INVALID_SENDER_STATE', table: 'account' })
|
|
114
|
+
);
|
|
86
115
|
runner.use(pipes.VerifyAccountMigration({ stateKey: 'senderState', addressKey: 'tx.from' }));
|
|
87
116
|
|
|
88
117
|
runner.use(pipes.ExtractState({ from: 'tokenAddress', to: 'tokenStates', status: 'INVALID_TOKEN', table: 'token' }));
|
|
@@ -119,21 +148,6 @@ runner.use(pipes.VerifyTransferrable({ assets: 'assetStates' }));
|
|
|
119
148
|
runner.use(pipes.VerifyUpdater({ assetKey: 'assetStates', ownerKey: 'senderState' }));
|
|
120
149
|
|
|
121
150
|
// Ensure tx fee and gas
|
|
122
|
-
runner.use(
|
|
123
|
-
EnsureTxGas((context) => {
|
|
124
|
-
// FIXME: payment check
|
|
125
|
-
const result = { create: 0, update: 2, payment: 0 };
|
|
126
|
-
result.update += context.assetStates?.length || 0;
|
|
127
|
-
|
|
128
|
-
if (context.receiverState) {
|
|
129
|
-
result.update += 1;
|
|
130
|
-
} else {
|
|
131
|
-
result.create += 1;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return result;
|
|
135
|
-
})
|
|
136
|
-
);
|
|
137
151
|
runner.use(EnsureTxCost({ attachSenderChanges: true }));
|
|
138
152
|
|
|
139
153
|
// transfer assets to new owner
|
|
@@ -152,7 +166,6 @@ runner.use(
|
|
|
152
166
|
async (context, next) => {
|
|
153
167
|
const {
|
|
154
168
|
tx,
|
|
155
|
-
itx,
|
|
156
169
|
tokens,
|
|
157
170
|
senderState,
|
|
158
171
|
receiverAddr,
|
|
@@ -196,13 +209,11 @@ runner.use(
|
|
|
196
209
|
: delegationState,
|
|
197
210
|
]);
|
|
198
211
|
|
|
199
|
-
await updateVaults();
|
|
200
|
-
|
|
201
212
|
context.senderState = newSenderState;
|
|
202
213
|
context.receiverState = newReceiverState;
|
|
203
214
|
context.delegationState = newDelegationState;
|
|
204
215
|
|
|
205
|
-
|
|
216
|
+
await updateVaults();
|
|
206
217
|
|
|
207
218
|
next();
|
|
208
219
|
},
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.19.
|
|
6
|
+
"version": "1.19.16",
|
|
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.19.
|
|
25
|
-
"@arcblock/did-util": "1.19.
|
|
26
|
-
"@arcblock/jwt": "1.19.
|
|
27
|
-
"@arcblock/validator": "1.19.
|
|
28
|
-
"@ocap/asset": "1.19.
|
|
29
|
-
"@ocap/mcrypto": "1.19.
|
|
30
|
-
"@ocap/merkle-tree": "1.19.
|
|
31
|
-
"@ocap/message": "1.19.
|
|
32
|
-
"@ocap/state": "1.19.
|
|
33
|
-
"@ocap/tx-pipeline": "1.19.
|
|
34
|
-
"@ocap/util": "1.19.
|
|
35
|
-
"@ocap/wallet": "1.19.
|
|
24
|
+
"@arcblock/did": "1.19.16",
|
|
25
|
+
"@arcblock/did-util": "1.19.16",
|
|
26
|
+
"@arcblock/jwt": "1.19.16",
|
|
27
|
+
"@arcblock/validator": "1.19.16",
|
|
28
|
+
"@ocap/asset": "1.19.16",
|
|
29
|
+
"@ocap/mcrypto": "1.19.16",
|
|
30
|
+
"@ocap/merkle-tree": "1.19.16",
|
|
31
|
+
"@ocap/message": "1.19.16",
|
|
32
|
+
"@ocap/state": "1.19.16",
|
|
33
|
+
"@ocap/tx-pipeline": "1.19.16",
|
|
34
|
+
"@ocap/util": "1.19.16",
|
|
35
|
+
"@ocap/wallet": "1.19.16",
|
|
36
36
|
"debug": "^4.3.6",
|
|
37
37
|
"deep-diff": "^1.0.2",
|
|
38
38
|
"empty-value": "^1.0.1",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"jest": "^29.7.0",
|
|
48
48
|
"start-server-and-test": "^1.14.0"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "9c3c349e48fc3c5a63aba6068ba5d31cc46d81c9"
|
|
51
51
|
}
|