@ocap/tx-protocols 1.18.95 → 1.18.97

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.
@@ -1,11 +1,13 @@
1
1
  /* eslint-disable max-len */
2
2
  const get = require('lodash/get');
3
+ const cloneDeep = require('lodash/cloneDeep');
4
+ const pick = require('lodash/pick');
3
5
  const { Joi } = require('@arcblock/validator');
4
6
  const { CustomError: Error } = require('@ocap/util/lib/error');
5
- const { getListField } = require('@ocap/util/lib/get-list-field');
6
7
  const { delegation, account } = require('@ocap/state');
7
8
  const { Runner, pipes } = require('@ocap/tx-pipeline');
8
9
  const { toDelegateAddress } = require('@arcblock/did-util');
10
+ const { formatMessage } = require('@ocap/message');
9
11
 
10
12
  // eslint-disable-next-line global-require
11
13
  const debug = require('debug')(`${require('../../../package.json').name}:delegate`);
@@ -17,6 +19,29 @@ const runner = new Runner();
17
19
 
18
20
  runner.use(pipes.VerifyMultiSig(0));
19
21
 
22
+ const rateLimit = Joi.object({
23
+ interval: Joi.number().integer().greater(0).required(),
24
+ anchor: Joi.number().integer().min(0).optional().default(0),
25
+ });
26
+
27
+ const tokenLimit = Joi.object({
28
+ address: Joi.DID().role('ROLE_TOKEN').required(),
29
+ toList: Joi.array().items(Joi.DID()).max(32).unique().optional().default([]),
30
+ txCount: Joi.number().integer().min(0).empty('').optional().default(0),
31
+ txAllowance: Joi.BN().optional().default('0'),
32
+ totalAllowance: Joi.BN().optional().default('0'),
33
+ validUntil: Joi.number().integer().min(0).empty('').optional().default(0),
34
+ rate: rateLimit.optional(),
35
+ });
36
+
37
+ const assetLimit = Joi.object({
38
+ address: Joi.array().items(Joi.DID().role('ROLE_ASSET')).unique().max(1024).optional().default([]),
39
+ toList: Joi.array().items(Joi.DID()).unique().max(32).optional().default([]),
40
+ txCount: Joi.number().integer().min(0).empty('').optional().default(0),
41
+ validUntil: Joi.number().integer().min(0).empty('').optional().default(0),
42
+ rate: rateLimit.optional(),
43
+ });
44
+
20
45
  // verify itx
21
46
  const schema = Joi.object({
22
47
  address: Joi.DID().prefix().role('ROLE_DELEGATION').required(),
@@ -25,28 +50,33 @@ const schema = Joi.object({
25
50
  .items(
26
51
  Joi.object({
27
52
  typeUrl: Joi.string().required(),
53
+ limit: Joi.object({
54
+ tokensList: Joi.array()
55
+ .items(tokenLimit)
56
+ .unique((a, b) => a.address === b.address)
57
+ .max(32),
58
+ assetsList: Joi.array().items(assetLimit).max(32),
59
+ })
60
+ .optional()
61
+ .default({ tokensList: [], assetsList: [] }),
28
62
  })
29
63
  )
64
+ .unique((a, b) => a.typeUrl === b.typeUrl)
30
65
  .min(1)
31
66
  .required(),
32
67
  data: Joi.any().optional(),
33
68
  }).options({ stripUnknown: true, noDefaults: false });
34
- runner.use(({ itx }, next) => {
35
- const { error } = schema.validate(itx);
69
+ runner.use((context, next) => {
70
+ const { error, value } = schema.validate(context.itx);
36
71
  if (error) {
37
72
  return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
38
73
  }
74
+ context.formattedItx = formatMessage('DelegateTx', context.itx);
75
+ context.validatedItx = value;
39
76
  return next();
40
77
  });
41
78
  runner.use(
42
79
  pipes.VerifyInfo([
43
- {
44
- error: 'OK',
45
- fn: (context) => {
46
- context.ops = getListField(context, 'itx.ops');
47
- return true;
48
- },
49
- },
50
80
  {
51
81
  error: 'INVALID_TX',
52
82
  message: 'Delegation address does not match',
@@ -55,10 +85,9 @@ runner.use(
55
85
  {
56
86
  error: 'INVALID_DELEGATION_TYPE_URL',
57
87
  message: 'Delegation type url is not allowed',
58
- fn: ({ ops, config }) => {
88
+ fn: ({ formattedItx, config }) => {
59
89
  const allowed = get(config, 'transaction.delegate.typeUrls', []);
60
- // TODO: support rule simulating and checking here
61
- return ops.every((op) => allowed.includes(op.typeUrl));
90
+ return formattedItx.ops.every((op) => allowed.includes(op.typeUrl));
62
91
  },
63
92
  },
64
93
  ])
@@ -98,15 +127,40 @@ runner.use(EnsureTxCost({ attachSenderChanges: true }));
98
127
  // Create delegation state
99
128
  runner.use(
100
129
  async (context, next) => {
101
- const { tx, itx, ops, statedb, senderState, receiverState, delegationState, senderUpdates, updateVaults } = context;
102
-
103
- const opsObj = ops.reduce(
104
- (acc, x) => {
130
+ const {
131
+ tx,
132
+ itx,
133
+ formattedItx,
134
+ validatedItx,
135
+ statedb,
136
+ senderState,
137
+ receiverState,
138
+ delegationState,
139
+ senderUpdates,
140
+ updateVaults,
141
+ } = context;
142
+
143
+ const merged = cloneDeep(formattedItx.ops).reduce(
144
+ (acc, x, i) => {
105
145
  acc[x.typeUrl] = {
106
- balance: 0,
107
- numTxs: 0,
108
- // TODO: 可以考虑结合现有的 DSL 去实现
109
- rule: true,
146
+ // This performs a reset of the limit
147
+ limit: {
148
+ tokens: (x.limit?.tokens || []).map((y, j) => {
149
+ const input = validatedItx.opsList[i].limit.tokensList[j];
150
+ return Object.assign(y, pick(input, ['txCount', 'txAllowance', 'totalAllowance', 'validUntil', 'rate']), {
151
+ txSent: 0,
152
+ lastTx: y.rate ? y.rate.anchor : 0,
153
+ spentAllowance: '0',
154
+ });
155
+ }),
156
+ assets: (x.limit?.assets || []).map((y, j) => {
157
+ const input = validatedItx.opsList[i].limit.assetsList[j];
158
+ return Object.assign(y, pick(input, ['txCount', 'validUntil', 'rate']), {
159
+ txSent: 0,
160
+ lastTx: y.rate ? y.rate.anchor : 0,
161
+ });
162
+ }),
163
+ },
110
164
  };
111
165
  return acc;
112
166
  },
@@ -129,10 +183,9 @@ runner.use(
129
183
  ? Promise.resolve(receiverState)
130
184
  : statedb.account.create(receiver, account.create({ address: receiver }, context), context),
131
185
 
132
- // update delegation
133
186
  delegationState
134
- ? statedb.delegation.update(itx.address, delegation.update(delegationState, { ...itx, ops: opsObj }, context), context) // prettier-ignore
135
- : statedb.delegation.create(itx.address, delegation.create({ ...itx, ops: opsObj }, context), context),
187
+ ? statedb.delegation.update(itx.address, delegation.update(delegationState, { ...itx, ops: merged }, context), context) // prettier-ignore
188
+ : statedb.delegation.create(itx.address, delegation.create({ ...itx, from: tx.from, ops: merged }, context), context), // prettier-ignore
136
189
 
137
190
  updateVaults(),
138
191
  ]);
@@ -50,7 +50,6 @@ runner.use(
50
50
  message: 'Delegation type url is not allowed',
51
51
  fn: ({ typeUrls, config }) => {
52
52
  const allowed = get(config, 'transaction.delegate.typeUrls', []);
53
- // TODO: support rule simulating and checking here
54
53
  return typeUrls.every((x) => allowed.includes(x));
55
54
  },
56
55
  },
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable function-paren-newline */
2
2
  const { promisify } = require('util');
3
3
  const { Runner, pipes } = require('@ocap/tx-pipeline');
4
- const { account, asset, factory } = require('@ocap/state');
4
+ const { account, asset, factory, delegation } = require('@ocap/state');
5
5
  const { CustomError: Error } = require('@ocap/util/lib/error');
6
6
  const { Joi, schemas } = require('@arcblock/validator');
7
7
 
@@ -61,9 +61,16 @@ runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INV
61
61
  runner.use(pipes.VerifyAccountMigration({ stateKey: 'senderState', addressKey: 'tx.from' }));
62
62
 
63
63
  runner.use(pipes.ExtractState({ from: 'tx.delegator', to: 'delegatorState', status: 'OK' }));
64
- runner.use(pipes.VerifyDelegation({ type: 'signature', signerKey: 'senderState', delegatorKey: 'delegatorState' }));
65
-
66
64
  runner.use(pipes.ExtractState({ from: 'itx.factory', to: 'factoryState', status: 'INVALID_FACTORY_STATE', table: 'factory' })); // prettier-ignore
65
+ runner.use(
66
+ pipes.VerifyDelegation({
67
+ type: 'signature',
68
+ signerKey: 'senderState',
69
+ delegatorKey: 'delegatorState',
70
+ getRequirements: (context) => context.factoryState.input.tokens.map((x) => ({ type: 'token', ...x })),
71
+ })
72
+ );
73
+
67
74
  runner.use(pipes.ExtractState({ from: 'factoryState.owner', to: 'factoryOwnerState', status: 'INVALID_OWNER_STATE' , table: 'account'})); // prettier-ignore
68
75
  runner.use(pipes.ExtractState({ from: 'factoryState.trustedIssuers', to: 'issuerStates', status: 'INVALID_ISSUER_STATE' , table: 'account'})); // prettier-ignore
69
76
  runner.use(pipes.ExtractState({ from: 'itx.issuer.id', to: 'issuerState', status: 'INVALID_ISSUER_STATE', table: 'account' })); // prettier-ignore
@@ -124,7 +131,7 @@ runner.use(verifyItxAssets);
124
131
  runner.use(
125
132
  EnsureTxGas((context) => {
126
133
  // FIXME: payment check
127
- const result = { create: 1, update: 2, payment: 0 };
134
+ const result = { create: 1, update: 3, payment: 0 };
128
135
  if (context.assetStates) {
129
136
  result.update += context.assetStates.length;
130
137
  }
@@ -148,6 +155,7 @@ runner.use(
148
155
  assetOwner,
149
156
  factoryState,
150
157
  factoryTokens,
158
+ delegationState,
151
159
  mintedAsset,
152
160
  mintedAddress,
153
161
  senderChange,
@@ -177,7 +185,7 @@ runner.use(
177
185
  context.tokenUpdates = applyTokenUpdates(factoryTokens, { tokens: {} }, 'add');
178
186
  }
179
187
 
180
- const [newSenderState, assetState, newFactoryState, newAssetStates] = await Promise.all([
188
+ const [newSenderState, assetState, newFactoryState, newAssetStates, newDelegationState] = await Promise.all([
181
189
  // Update sender state
182
190
  statedb.account.update(senderState.address, account.update(senderState, senderUpdates, context), context),
183
191
 
@@ -198,6 +206,11 @@ runner.use(
198
206
  )
199
207
  ),
200
208
 
209
+ // Update delegation state
210
+ context.isDelegationChanged
211
+ ? statedb.delegation.update(delegationState.address, delegation.update(delegationState, {}, context), context)
212
+ : delegationState,
213
+
201
214
  // Update delegator state
202
215
  context.delegatorState
203
216
  ? statedb.account.update(assetOwner.address, account.update(assetOwner, ownerUpdates, context), context)
@@ -210,6 +223,7 @@ runner.use(
210
223
  context.assetState = assetState;
211
224
  context.factoryState = newFactoryState;
212
225
  context.assetStates = newAssetStates;
226
+ context.delegationState = newDelegationState;
213
227
 
214
228
  debug('acquire-v2', assetState);
215
229
 
@@ -2,14 +2,14 @@
2
2
  const { CustomError: Error } = require('@ocap/util/lib/error');
3
3
  const cloneDeep = require('lodash/cloneDeep');
4
4
  const { Runner, pipes } = require('@ocap/tx-pipeline');
5
- const { account, asset } = require('@ocap/state');
5
+ const { account, asset, delegation } = require('@ocap/state');
6
6
  const { formatMessage } = require('@ocap/message');
7
7
  const { toAssetAddress } = require('@arcblock/did-util');
8
8
 
9
9
  // eslint-disable-next-line global-require
10
10
  const debug = require('debug')(`${require('../../../package.json').name}:create-asset`);
11
11
 
12
- const { decodeAnySafe } = require('../../util');
12
+ const { decodeAnySafe, getDelegationRequirements } = require('../../util');
13
13
 
14
14
  const EnsureTxGas = require('../../pipes/ensure-gas');
15
15
  const EnsureTxCost = require('../../pipes/ensure-cost');
@@ -71,14 +71,21 @@ runner.use(pipes.VerifyAccountMigration({ stateKey: 'senderState', addressKey: '
71
71
  // Ensure delegation
72
72
  runner.use(pipes.ExtractState({ from: 'tx.delegator', to: 'delegatorState', status: 'OK', table: 'account' }));
73
73
  runner.use(pipes.VerifyAccountMigration({ stateKey: 'delegatorState', addressKey: 'tx.delegator' }));
74
- runner.use(pipes.VerifyDelegation({ type: 'signature', signerKey: 'senderState', delegatorKey: 'delegatorState' }));
74
+ runner.use(
75
+ pipes.VerifyDelegation({
76
+ type: 'signature',
77
+ signerKey: 'senderState',
78
+ delegatorKey: 'delegatorState',
79
+ getRequirements: getDelegationRequirements,
80
+ })
81
+ );
75
82
 
76
83
  // Check if parent is a factory
77
84
  runner.use(pipes.ExtractState({ from: 'itx.parent', to: 'factoryState', status: 'OK', table: 'factory' }));
78
85
  runner.use(
79
86
  pipes.VerifyInfo([
80
87
  // For security consideration, user can not create fake assets from a factory
81
- // https://github.com/ArcBlock/asset-chain/issues/97
88
+ // https://github.com/ArcBlock/blockchain/issues/97
82
89
  {
83
90
  error: 'CREATE_FROM_FACTORY',
84
91
  message: 'You can only get asset from factory from acquire or mint',
@@ -92,16 +99,17 @@ runner.use(
92
99
  runner.use(pipes.ExtractState({ from: 'itx.parent', to: 'parentAsset', status: 'INVALID_ASSET', table: 'asset' }));
93
100
 
94
101
  // Ensure tx fee and gas
95
- runner.use(EnsureTxGas(() => ({ create: 1, update: 1, payment: 0 })));
102
+ runner.use(EnsureTxGas(() => ({ create: 1, update: 2, payment: 0 })));
96
103
  runner.use(EnsureTxCost({ attachSenderChanges: true }));
97
104
 
98
105
  // Update asset state
99
106
  runner.use(
100
107
  async (context, next) => {
101
- const { tx, itx, assetData, statedb, senderState, delegatorState, senderUpdates, updateVaults } = context;
108
+ const { tx, itx, assetData, statedb, senderState, delegatorState, delegationState, senderUpdates, updateVaults } =
109
+ context;
102
110
  const owner = delegatorState ? delegatorState.address : senderState.address;
103
111
 
104
- const [newSenderState, assetState] = await Promise.all([
112
+ const [newSenderState, assetState, newDelegationState] = await Promise.all([
105
113
  statedb.account.update(
106
114
  senderState.address,
107
115
  account.update(senderState, { nonce: tx.nonce, pk: tx.pk, ...senderUpdates }, context),
@@ -115,10 +123,16 @@ runner.use(
115
123
  ),
116
124
 
117
125
  updateVaults(),
126
+
127
+ // Update delegation state
128
+ context.isDelegationChanged
129
+ ? statedb.delegation.update(delegationState.address, delegation.update(delegationState, {}, context), context)
130
+ : delegationState,
118
131
  ]);
119
132
 
120
133
  context.senderState = newSenderState;
121
134
  context.assetState = assetState;
135
+ context.delegationState = newDelegationState;
122
136
 
123
137
  debug('createAsset', assetState);
124
138
 
@@ -16,7 +16,7 @@ module.exports = (context, next) => {
16
16
  return next(new Error('INVALID_ASSET', 'Input asset does not exist on chain'));
17
17
  }
18
18
 
19
- // https://github.com/ArcBlock/asset-chain/issues/96
19
+ // https://github.com/ArcBlock/blockchain/issues/96
20
20
  // If factory.input.assets are all factory
21
21
  if (expectedFactoryStates.length === expectedAssets.length) {
22
22
  // Just ensure that input assets are minted from any of the factory
@@ -7,13 +7,13 @@ const { isValidFactory } = require('@ocap/asset');
7
7
  const { formatMessage } = require('@ocap/message');
8
8
  const { Runner, pipes } = require('@ocap/tx-pipeline');
9
9
  const { BN } = require('@ocap/util');
10
- const { account, factory } = require('@ocap/state');
10
+ const { account, factory, delegation } = require('@ocap/state');
11
11
  const { toFactoryAddress } = require('@arcblock/did-util');
12
12
 
13
13
  // eslint-disable-next-line global-require
14
14
  const debug = require('debug')(`${require('../../../package.json').name}:create-factory`);
15
15
 
16
- const { decodeAnySafe } = require('../../util');
16
+ const { decodeAnySafe, getDelegationRequirements } = require('../../util');
17
17
  const EnsureTxGas = require('../../pipes/ensure-gas');
18
18
  const EnsureTxCost = require('../../pipes/ensure-cost');
19
19
 
@@ -87,7 +87,14 @@ runner.use(pipes.VerifyAccountMigration({ stateKey: 'senderState', addressKey: '
87
87
  // Ensure delegation
88
88
  runner.use(pipes.ExtractState({ from: 'tx.delegator', to: 'delegatorState', status: 'OK', table: 'account' }));
89
89
  runner.use(pipes.VerifyAccountMigration({ stateKey: 'delegatorState', addressKey: 'tx.delegator' }));
90
- runner.use(pipes.VerifyDelegation({ type: 'signature', signerKey: 'senderState', delegatorKey: 'delegatorState' }));
90
+ runner.use(
91
+ pipes.VerifyDelegation({
92
+ type: 'signature',
93
+ signerKey: 'senderState',
94
+ delegatorKey: 'delegatorState',
95
+ getRequirements: getDelegationRequirements,
96
+ })
97
+ );
91
98
 
92
99
  // Ensure tokens exist if we are creating an factory that consume tokens
93
100
  runner.use(pipes.ExtractState({ from: 'factoryTokens', to: 'tokenStates', table: 'token', status: 'INVALID_FACTORY_INPUT' })); // prettier-ignore
@@ -113,17 +120,27 @@ runner.use(pipes.ExtractState({ from: 'factoryProps.trustedIssuers', to: 'issuer
113
120
  runner.use(pipes.VerifyAccountMigration({ stateKey: 'issuerStates', addressKey: 'factoryProps.trustedIssuers' }));
114
121
 
115
122
  // Ensure tx fee and gas
116
- runner.use(EnsureTxGas(() => ({ create: 1, update: 1, payment: 0 })));
123
+ runner.use(EnsureTxGas(() => ({ create: 1, update: 2, payment: 0 })));
117
124
  runner.use(EnsureTxCost({ attachSenderChanges: true }));
118
125
 
119
126
  // Create factory state
120
127
  runner.use(
121
128
  async (context, next) => {
122
- const { tx, itx, statedb, senderState, delegatorState, senderUpdates, factoryProps, updateVaults } = context;
129
+ const {
130
+ tx,
131
+ itx,
132
+ statedb,
133
+ senderState,
134
+ delegatorState,
135
+ delegationState,
136
+ senderUpdates,
137
+ factoryProps,
138
+ updateVaults,
139
+ } = context;
123
140
  const tokens = { [context.config.token.address]: '0' };
124
141
  const owner = delegatorState ? delegatorState.address : senderState.address;
125
142
 
126
- const [newSenderState, factoryState] = await Promise.all([
143
+ const [newSenderState, factoryState, newDelegationState] = await Promise.all([
127
144
  // Update owner state
128
145
  statedb.account.update(
129
146
  senderState.address,
@@ -134,11 +151,17 @@ runner.use(
134
151
  // Create factory state
135
152
  statedb.factory.create(itx.address, factory.create({ ...factoryProps, tokens, owner }, context), context),
136
153
 
154
+ // Update delegation state
155
+ context.isDelegationChanged
156
+ ? statedb.delegation.update(delegationState.address, delegation.update(delegationState, {}, context), context)
157
+ : delegationState,
158
+
137
159
  updateVaults(),
138
160
  ]);
139
161
 
140
162
  context.senderState = newSenderState;
141
163
  context.factoryState = factoryState;
164
+ context.delegationState = newDelegationState;
142
165
 
143
166
  debug('createFactory', factoryState);
144
167
 
@@ -3,7 +3,7 @@ const isEmpty = require('empty-value');
3
3
  const cloneDeep = require('lodash/cloneDeep');
4
4
  const { CustomError: Error } = require('@ocap/util/lib/error');
5
5
  const { Runner, pipes } = require('@ocap/tx-pipeline');
6
- const { account, rollup } = require('@ocap/state');
6
+ const { account, rollup, delegation } = require('@ocap/state');
7
7
  const { formatMessage } = require('@ocap/message');
8
8
  const { isFromPublicKey, isEthereumDid } = require('@arcblock/did');
9
9
  const { toRollupAddress } = require('@arcblock/did-util');
@@ -11,7 +11,7 @@ const { toRollupAddress } = require('@arcblock/did-util');
11
11
  // eslint-disable-next-line global-require
12
12
  const debug = require('debug')(`${require('../../../package.json').name}:create-rollup`);
13
13
 
14
- const { decodeAnySafe } = require('../../util');
14
+ const { decodeAnySafe, getDelegationRequirements } = require('../../util');
15
15
  const EnsureTxGas = require('../../pipes/ensure-gas');
16
16
  const EnsureTxCost = require('../../pipes/ensure-cost');
17
17
 
@@ -73,7 +73,14 @@ runner.use(pipes.ExtractState({ from: 'seedValidators', to: 'validatorStates', s
73
73
  runner.use(pipes.VerifyAccountMigration({ signerKey: 'validatorStates', stateKey: 'senderState', addressKey: 'tx.from' })); // prettier-ignore
74
74
  runner.use(pipes.ExtractState({ from: 'tx.delegator', to: 'delegatorState', status: 'OK', table: 'account' }));
75
75
  runner.use(pipes.VerifyAccountMigration({ stateKey: 'delegatorState', addressKey: 'tx.delegator' }));
76
- runner.use(pipes.VerifyDelegation({ type: 'signature', signerKey: 'senderState', delegatorKey: 'delegatorState' }));
76
+ runner.use(
77
+ pipes.VerifyDelegation({
78
+ type: 'signature',
79
+ signerKey: 'senderState',
80
+ delegatorKey: 'delegatorState',
81
+ getRequirements: getDelegationRequirements,
82
+ })
83
+ );
77
84
 
78
85
  // 2. ensure token exist and match
79
86
  runner.use(pipes.ExtractState({ from: 'itx.tokenAddress', to: 'tokenState', status: 'INVALID_TOKEN', table: 'token' }));
@@ -111,16 +118,26 @@ runner.use(
111
118
  );
112
119
 
113
120
  // Ensure tx fee and gas
114
- runner.use(EnsureTxGas(() => ({ create: 1, update: 1, payment: 0 })));
121
+ runner.use(EnsureTxGas(() => ({ create: 1, update: 2, payment: 0 })));
115
122
  runner.use(EnsureTxCost({ attachSenderChanges: true }));
116
123
 
117
124
  // 5. create rollup state
118
125
  runner.use(
119
126
  async (context, next) => {
120
- const { tx, formattedItx, rollupData, statedb, senderState, delegatorState, senderUpdates, updateVaults } = context;
127
+ const {
128
+ tx,
129
+ formattedItx,
130
+ rollupData,
131
+ statedb,
132
+ senderState,
133
+ delegatorState,
134
+ delegationState,
135
+ senderUpdates,
136
+ updateVaults,
137
+ } = context;
121
138
 
122
139
  const issuer = delegatorState ? delegatorState.address : senderState.address;
123
- const [newSenderState, rollupState] = await Promise.all([
140
+ const [newSenderState, rollupState, newDelegationState] = await Promise.all([
124
141
  statedb.account.update(
125
142
  senderState.address,
126
143
  account.update(senderState, { nonce: tx.nonce, ...senderUpdates }, context),
@@ -141,6 +158,11 @@ runner.use(
141
158
  context
142
159
  ),
143
160
 
161
+ // Update delegation state
162
+ context.isDelegationChanged
163
+ ? statedb.delegation.update(delegationState.address, delegation.update(delegationState, {}, context), context)
164
+ : delegationState,
165
+
144
166
  updateVaults(),
145
167
  ]);
146
168
 
@@ -149,6 +171,7 @@ runner.use(
149
171
 
150
172
  context.senderState = newSenderState;
151
173
  context.rollupState = rollupState;
174
+ context.delegationState = newDelegationState;
152
175
 
153
176
  debug('create-rollup', rollupState);
154
177
 
@@ -3,13 +3,13 @@ const cloneDeep = require('lodash/cloneDeep');
3
3
  const { Joi, schemas } = require('@arcblock/validator');
4
4
  const { CustomError: Error } = require('@ocap/util/lib/error');
5
5
  const { Runner, pipes } = require('@ocap/tx-pipeline');
6
- const { account, token } = require('@ocap/state');
6
+ const { account, token, delegation } = require('@ocap/state');
7
7
  const { toTokenAddress } = require('@arcblock/did-util');
8
8
  const { fromTokenToUnit } = require('@ocap/util');
9
9
 
10
10
  // eslint-disable-next-line global-require
11
11
  const debug = require('debug')(`${require('../../../package.json').name}:create-token`);
12
- const { decodeAnySafe } = require('../../util');
12
+ const { decodeAnySafe, getDelegationRequirements } = require('../../util');
13
13
 
14
14
  const EnsureTxGas = require('../../pipes/ensure-gas');
15
15
  const EnsureTxCost = require('../../pipes/ensure-cost');
@@ -66,7 +66,14 @@ runner.use(pipes.VerifyAccountMigration({ stateKey: 'senderState', addressKey: '
66
66
  // Ensure delegation
67
67
  runner.use(pipes.ExtractState({ from: 'tx.delegator', to: 'delegatorState', status: 'OK', table: 'account' }));
68
68
  runner.use(pipes.VerifyAccountMigration({ stateKey: 'delegatorState', addressKey: 'tx.delegator' }));
69
- runner.use(pipes.VerifyDelegation({ type: 'signature', signerKey: 'senderState', delegatorKey: 'delegatorState' }));
69
+ runner.use(
70
+ pipes.VerifyDelegation({
71
+ type: 'signature',
72
+ signerKey: 'senderState',
73
+ delegatorKey: 'delegatorState',
74
+ getRequirements: getDelegationRequirements,
75
+ })
76
+ );
70
77
 
71
78
  // Ensure token not exist
72
79
  runner.use(pipes.ExtractState({ from: 'itx.address', to: 'tokenState', table: 'token', status: 'OK' }));
@@ -101,7 +108,7 @@ runner.use(
101
108
  // Ensure tx fee and gas
102
109
  runner.use(
103
110
  EnsureTxGas((context) => {
104
- const result = { create: 1, update: 1, payment: 0 };
111
+ const result = { create: 1, update: 2, payment: 0 };
105
112
  if (context.delegatorState) {
106
113
  result.update += 1;
107
114
  }
@@ -114,7 +121,7 @@ runner.use(EnsureTxCost({ attachSenderChanges: true }));
114
121
  // Update sender state, token state
115
122
  runner.use(
116
123
  async (context, next) => {
117
- const { tx, itx, statedb, senderState, delegatorState, senderUpdates, updateVaults } = context;
124
+ const { tx, itx, statedb, senderState, delegatorState, delegationState, senderUpdates, updateVaults } = context;
118
125
  const data = decodeAnySafe(itx.data);
119
126
  const owner = delegatorState ? delegatorState.address : senderState.address;
120
127
 
@@ -130,7 +137,7 @@ runner.use(
130
137
  senderUpdates.tokens[itx.address] = itx.initialSupply;
131
138
  }
132
139
 
133
- const [newSenderState, tokenState, newDelegatorState] = await Promise.all([
140
+ const [newSenderState, tokenState, newDelegatorState, newDelegationState] = await Promise.all([
134
141
  statedb.account.update(
135
142
  senderState.address,
136
143
  account.update(senderState, { nonce: tx.nonce, pk: tx.pk, ...senderUpdates }, context),
@@ -147,12 +154,18 @@ runner.use(
147
154
  )
148
155
  : null,
149
156
 
157
+ // Update delegation state
158
+ context.isDelegationChanged
159
+ ? statedb.delegation.update(delegationState.address, delegation.update(delegationState, {}, context), context)
160
+ : delegationState,
161
+
150
162
  updateVaults(),
151
163
  ]);
152
164
 
153
165
  context.senderState = newSenderState;
154
166
  context.tokenState = tokenState;
155
167
  context.delegatorState = newDelegatorState;
168
+ context.delegationState = newDelegationState;
156
169
 
157
170
  debug('create token v2', tokenState);
158
171
 
@@ -99,6 +99,8 @@ runner.use(
99
99
  signerKey: 'signerStates',
100
100
  delegatorKey: 'delegatorStates',
101
101
  delegationKey: 'delegationStates',
102
+ // FIXME: this is not working
103
+ getRequirements: () => [],
102
104
  })
103
105
  );
104
106
 
@@ -134,7 +136,7 @@ runner.use(pipes.VerifyUpdater({ assetKey: 'priv.receiverAssets', ownerKey: 'rec
134
136
  runner.use(
135
137
  EnsureTxGas((context) => {
136
138
  // FIXME: payment check
137
- const ops = { create: 0, update: 2, payment: 0 };
139
+ const ops = { create: 0, update: 3, payment: 0 };
138
140
  ops.update += context.senderAssets.length;
139
141
  ops.update += context.receiverAssets.length;
140
142
 
@@ -5,7 +5,7 @@ const { decodeBigInt } = require('@ocap/message');
5
5
  const { Joi, schemas } = require('@arcblock/validator');
6
6
  const { BN } = require('@ocap/util');
7
7
  const { Runner, pipes } = require('@ocap/tx-pipeline');
8
- const { account } = require('@ocap/state');
8
+ const { account, delegation } = require('@ocap/state');
9
9
 
10
10
  // eslint-disable-next-line global-require
11
11
  const debug = require('debug')(`${require('../../../package.json').name}:transfer-v2`);
@@ -97,7 +97,17 @@ runner.use(pipes.VerifyTokenBalance({ ownerKey: 'senderState', conditionKey: 'to
97
97
 
98
98
  runner.use(pipes.ExtractState({ from: 'tx.delegator', to: 'delegatorState', status: 'OK', table: 'account' }));
99
99
  runner.use(pipes.VerifyAccountMigration({ stateKey: 'delegatorState', addressKey: 'tx.delegator' }));
100
- runner.use(pipes.VerifyDelegation({ type: 'signature', signerKey: 'senderState', delegatorKey: 'delegatorState' }));
100
+ runner.use(
101
+ pipes.VerifyDelegation({
102
+ type: 'signature',
103
+ signerKey: 'senderState',
104
+ delegatorKey: 'delegatorState',
105
+ getRequirements: (ctx) => [
106
+ ...ctx.tokens.map((x) => ({ type: 'token', ...x, to: ctx.itx.to })),
107
+ ...ctx.assets.map((x) => ({ type: 'asset', address: x, to: ctx.itx.to })),
108
+ ],
109
+ })
110
+ );
101
111
 
102
112
  runner.use(pipes.ExtractReceiver({ from: 'itx.to', to: 'receiver' }));
103
113
  runner.use(pipes.ExtractState({ from: 'receiver', to: 'receiverState', status: 'OK', table: 'account' }));
@@ -111,7 +121,7 @@ runner.use(pipes.VerifyUpdater({ assetKey: 'assetStates', ownerKey: 'senderState
111
121
  runner.use(
112
122
  EnsureTxGas((context) => {
113
123
  // FIXME: payment check
114
- const result = { create: 0, update: 1, payment: 0 };
124
+ const result = { create: 0, update: 2, payment: 0 };
115
125
  result.update += context.assetStates?.length || 0;
116
126
 
117
127
  if (context.receiverState) {
@@ -136,7 +146,18 @@ runner.use(pipes.UpdateOwner({ assets: 'assetStates', owner: 'receiverAddr' }));
136
146
  // update statedb: transfer tokens to new owner
137
147
  runner.use(
138
148
  async (context, next) => {
139
- const { tx, itx, tokens, senderState, receiverAddr, receiverState, statedb, senderChange, updateVaults } = context;
149
+ const {
150
+ tx,
151
+ itx,
152
+ tokens,
153
+ senderState,
154
+ receiverAddr,
155
+ receiverState,
156
+ statedb,
157
+ senderChange,
158
+ delegationState,
159
+ updateVaults,
160
+ } = context;
140
161
 
141
162
  const { tokens: senderTokens = {} } = senderState;
142
163
  const { tokens: receiverTokens = {} } = receiverState || {};
@@ -150,7 +171,7 @@ runner.use(
150
171
  ? applyTokenChange({ tokens: senderTokens }, senderChange)
151
172
  : { tokens: senderTokens };
152
173
 
153
- const [newSenderState, newReceiverState] = await Promise.all([
174
+ const [newSenderState, newReceiverState, newDelegationState] = await Promise.all([
154
175
  // Update sender state
155
176
  statedb.account.update(
156
177
  senderState.address,
@@ -165,11 +186,17 @@ runner.use(
165
186
  context
166
187
  ),
167
188
 
189
+ // Update delegation state
190
+ context.isDelegationChanged
191
+ ? statedb.delegation.update(delegationState.address, delegation.update(delegationState, {}, context), context)
192
+ : delegationState,
193
+
168
194
  updateVaults(),
169
195
  ]);
170
196
 
171
197
  context.senderState = newSenderState;
172
198
  context.receiverState = newReceiverState;
199
+ context.delegationState = newDelegationState;
173
200
 
174
201
  debug('transfer-v2', { from: tx.from, to: itx.to, tokens });
175
202
 
package/lib/util.js CHANGED
@@ -160,6 +160,22 @@ const getTxFee = ({ amount, feeRate, maxFee, minFee, stringify = true }) => {
160
160
  };
161
161
  };
162
162
 
163
+ const getDelegationRequirements = (context) => {
164
+ const { txType, config } = context;
165
+ const txFee = config.transaction.txFee[txType];
166
+ if (!txFee) {
167
+ return [];
168
+ }
169
+
170
+ return [
171
+ {
172
+ type: 'token',
173
+ address: config.token.address,
174
+ value: fromTokenToUnit(txFee, config.token.decimal).toString(),
175
+ },
176
+ ];
177
+ };
178
+
163
179
  const splitTxFee = ({ total, shares = {}, stringify = true }) => {
164
180
  const totalAmount = new BN(total);
165
181
  if (totalAmount.isNeg()) {
@@ -397,4 +413,5 @@ module.exports = {
397
413
  isGasStakeInput,
398
414
  isGasStakeValid,
399
415
  RATE_BASE,
416
+ getDelegationRequirements,
400
417
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.18.95",
6
+ "version": "1.18.97",
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.95",
25
- "@arcblock/did-util": "1.18.95",
26
- "@arcblock/jwt": "1.18.95",
27
- "@arcblock/validator": "1.18.95",
28
- "@ocap/asset": "1.18.95",
29
- "@ocap/mcrypto": "1.18.95",
30
- "@ocap/merkle-tree": "1.18.95",
31
- "@ocap/message": "1.18.95",
32
- "@ocap/state": "1.18.95",
33
- "@ocap/tx-pipeline": "1.18.95",
34
- "@ocap/util": "1.18.95",
35
- "@ocap/wallet": "1.18.95",
24
+ "@arcblock/did": "1.18.97",
25
+ "@arcblock/did-util": "1.18.97",
26
+ "@arcblock/jwt": "1.18.97",
27
+ "@arcblock/validator": "1.18.97",
28
+ "@ocap/asset": "1.18.97",
29
+ "@ocap/mcrypto": "1.18.97",
30
+ "@ocap/merkle-tree": "1.18.97",
31
+ "@ocap/message": "1.18.97",
32
+ "@ocap/state": "1.18.97",
33
+ "@ocap/tx-pipeline": "1.18.97",
34
+ "@ocap/util": "1.18.97",
35
+ "@ocap/wallet": "1.18.97",
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": "1d243016b59c4532a8ca6f01d810e4a8f4b3ccb2"
50
+ "gitHead": "691aabefd37f53b6fbcf771ce130f09197f0b268"
51
51
  }