@ocap/tx-protocols 1.13.57 → 1.13.61
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/protocols/account/declare.js +17 -17
- package/lib/protocols/account/delegate.js +23 -7
- package/lib/protocols/account/migrate.js +14 -8
- package/lib/protocols/account/revoke-delegate.js +14 -7
- package/lib/protocols/asset/create.js +21 -13
- package/lib/protocols/asset/update.js +15 -15
- package/lib/protocols/rollup/claim-reward.js +67 -58
- package/lib/protocols/rollup/create-block.js +60 -162
- package/lib/protocols/token/deposit-v2.js +43 -48
- package/lib/protocols/token/withdraw-v2.js +114 -23
- package/lib/util.js +160 -29
- package/package.json +12 -12
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
const
|
|
1
|
+
const Error = require('@ocap/util/lib/error');
|
|
2
2
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
3
|
-
const { account } = require('@ocap/state');
|
|
3
|
+
const { account, Joi } = require('@ocap/state');
|
|
4
4
|
const { toBase58 } = require('@ocap/util');
|
|
5
5
|
|
|
6
6
|
const runner = new Runner();
|
|
7
7
|
|
|
8
8
|
runner.use(pipes.VerifyMultiSig(0));
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
);
|
|
10
|
+
// verify itx
|
|
11
|
+
const schema = Joi.object({
|
|
12
|
+
issuer: Joi.DID().optional().allow(''),
|
|
13
|
+
moniker: Joi.string()
|
|
14
|
+
.regex(/^[a-zA-Z0-9][-a-zA-Z0-9_]{2,40}$/)
|
|
15
|
+
.required(),
|
|
16
|
+
data: Joi.any().optional(),
|
|
17
|
+
}).options({ stripUnknown: true, noDefaults: false });
|
|
18
|
+
runner.use(({ itx }, next) => {
|
|
19
|
+
const { error } = schema.validate(itx);
|
|
20
|
+
if (error) {
|
|
21
|
+
return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
|
|
22
|
+
}
|
|
23
|
+
return next();
|
|
24
|
+
});
|
|
25
25
|
|
|
26
26
|
// Ensure sender does not exist
|
|
27
27
|
runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState' }));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
2
|
const get = require('lodash/get');
|
|
3
|
-
const
|
|
3
|
+
const Error = require('@ocap/util/lib/error');
|
|
4
4
|
const getListField = require('@ocap/util/lib/get-list-field');
|
|
5
|
-
const { delegation, account } = require('@ocap/state');
|
|
5
|
+
const { delegation, account, Joi } = require('@ocap/state');
|
|
6
6
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
7
7
|
const { toDelegateAddress } = require('@arcblock/did-util');
|
|
8
8
|
|
|
@@ -13,6 +13,27 @@ const runner = new Runner();
|
|
|
13
13
|
|
|
14
14
|
runner.use(pipes.VerifyMultiSig(0));
|
|
15
15
|
|
|
16
|
+
// verify itx
|
|
17
|
+
const schema = Joi.object({
|
|
18
|
+
address: Joi.DID().role('ROLE_DELEGATION').required(),
|
|
19
|
+
to: Joi.DID().required(),
|
|
20
|
+
opsList: Joi.array()
|
|
21
|
+
.items(
|
|
22
|
+
Joi.object({
|
|
23
|
+
typeUrl: Joi.string().required(),
|
|
24
|
+
})
|
|
25
|
+
)
|
|
26
|
+
.min(1)
|
|
27
|
+
.required(),
|
|
28
|
+
data: Joi.any().optional(),
|
|
29
|
+
}).options({ stripUnknown: true, noDefaults: false });
|
|
30
|
+
runner.use(({ itx }, next) => {
|
|
31
|
+
const { error } = schema.validate(itx);
|
|
32
|
+
if (error) {
|
|
33
|
+
return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
|
|
34
|
+
}
|
|
35
|
+
return next();
|
|
36
|
+
});
|
|
16
37
|
runner.use(
|
|
17
38
|
pipes.VerifyInfo([
|
|
18
39
|
{
|
|
@@ -22,11 +43,6 @@ runner.use(
|
|
|
22
43
|
return true;
|
|
23
44
|
},
|
|
24
45
|
},
|
|
25
|
-
{
|
|
26
|
-
error: 'INSUFFICIENT_DATA',
|
|
27
|
-
message: 'itx.address, itx.to and itx.ops should not be empty',
|
|
28
|
-
fn: ({ itx, ops }) => !isEmpty(itx.address) && !isEmpty(itx.to) && ops.length > 0,
|
|
29
|
-
},
|
|
30
46
|
{
|
|
31
47
|
error: 'INVALID_TX',
|
|
32
48
|
message: 'Delegation address does not match',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
const isEmpty = require('empty-value');
|
|
2
1
|
const Error = require('@ocap/util/lib/error');
|
|
3
2
|
const getRelatedAddresses = require('@ocap/util/lib/get-related-addr');
|
|
4
3
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
5
4
|
const { fromPublicKey, toTypeInfo } = require('@arcblock/did');
|
|
6
|
-
const { account } = require('@ocap/state');
|
|
5
|
+
const { account, Joi } = require('@ocap/state');
|
|
7
6
|
|
|
8
7
|
// eslint-disable-next-line global-require
|
|
9
8
|
const debug = require('debug')(`${require('../../../package.json').name}:migrate`);
|
|
@@ -12,14 +11,21 @@ const runner = new Runner();
|
|
|
12
11
|
|
|
13
12
|
runner.use(pipes.VerifyMultiSig(0));
|
|
14
13
|
|
|
15
|
-
//
|
|
14
|
+
// verify itx
|
|
15
|
+
const schema = Joi.object({
|
|
16
|
+
address: Joi.DID().required(),
|
|
17
|
+
pk: Joi.any().required(),
|
|
18
|
+
data: Joi.any().optional(),
|
|
19
|
+
}).options({ stripUnknown: true, noDefaults: false });
|
|
20
|
+
runner.use(({ itx }, next) => {
|
|
21
|
+
const { error } = schema.validate(itx);
|
|
22
|
+
if (error) {
|
|
23
|
+
return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
|
|
24
|
+
}
|
|
25
|
+
return next();
|
|
26
|
+
});
|
|
16
27
|
runner.use(
|
|
17
28
|
pipes.VerifyInfo([
|
|
18
|
-
{
|
|
19
|
-
error: 'INSUFFICIENT_DATA',
|
|
20
|
-
message: 'itx.address and itx.pk should not be empty',
|
|
21
|
-
fn: ({ itx }) => !isEmpty(itx.pk) && !isEmpty(itx.address),
|
|
22
|
-
},
|
|
23
29
|
{
|
|
24
30
|
error: 'INVALID_RECEIVER_STATE',
|
|
25
31
|
message: 'Receiver pk and address does not match',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
const get = require('lodash/get');
|
|
2
2
|
const cloneDeep = require('lodash/cloneDeep');
|
|
3
|
-
const isEmpty = require('empty-value');
|
|
4
3
|
const getListField = require('@ocap/util/lib/get-list-field');
|
|
5
4
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
6
|
-
const { delegation, account } = require('@ocap/state');
|
|
5
|
+
const { delegation, account, Joi } = require('@ocap/state');
|
|
7
6
|
const { toDelegateAddress } = require('@arcblock/did-util');
|
|
8
7
|
|
|
9
8
|
// eslint-disable-next-line global-require
|
|
@@ -14,6 +13,19 @@ const runner = new Runner();
|
|
|
14
13
|
runner.use(pipes.VerifyMultiSig(0));
|
|
15
14
|
|
|
16
15
|
// Verify itx
|
|
16
|
+
const schema = Joi.object({
|
|
17
|
+
address: Joi.DID().role('ROLE_DELEGATION').required(),
|
|
18
|
+
to: Joi.DID().required(),
|
|
19
|
+
typeUrlsList: Joi.array().items(Joi.string().required()).min(1).required(),
|
|
20
|
+
data: Joi.any().optional(),
|
|
21
|
+
}).options({ stripUnknown: true, noDefaults: false });
|
|
22
|
+
runner.use(({ itx }, next) => {
|
|
23
|
+
const { error } = schema.validate(itx);
|
|
24
|
+
if (error) {
|
|
25
|
+
return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
|
|
26
|
+
}
|
|
27
|
+
return next();
|
|
28
|
+
});
|
|
17
29
|
runner.use(
|
|
18
30
|
pipes.VerifyInfo([
|
|
19
31
|
{
|
|
@@ -23,11 +35,6 @@ runner.use(
|
|
|
23
35
|
return true;
|
|
24
36
|
},
|
|
25
37
|
},
|
|
26
|
-
{
|
|
27
|
-
error: 'INSUFFICIENT_DATA',
|
|
28
|
-
message: 'itx.address, itx.to and itx.typeUrls should not be empty',
|
|
29
|
-
fn: ({ itx, typeUrls }) => !isEmpty(itx.address) && !isEmpty(itx.to) && typeUrls.length > 0,
|
|
30
|
-
},
|
|
31
38
|
{
|
|
32
39
|
error: 'INVALID_TX',
|
|
33
40
|
message: 'Delegation address does not match',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* eslint-disable indent */
|
|
2
|
-
const
|
|
2
|
+
const 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, Joi } = require('@ocap/state');
|
|
6
6
|
const { toAssetAddress } = require('@arcblock/did-util');
|
|
7
7
|
|
|
8
8
|
// eslint-disable-next-line global-require
|
|
@@ -14,18 +14,26 @@ const runner = new Runner();
|
|
|
14
14
|
|
|
15
15
|
runner.use(pipes.VerifyMultiSig(0));
|
|
16
16
|
|
|
17
|
+
// Verify itx
|
|
18
|
+
const schema = Joi.object({
|
|
19
|
+
moniker: Joi.string().min(2).max(255).required(),
|
|
20
|
+
data: Joi.any().required(),
|
|
21
|
+
readonly: Joi.boolean().default(false),
|
|
22
|
+
transferrable: Joi.boolean().default(false),
|
|
23
|
+
ttl: Joi.number().min(0).default(0),
|
|
24
|
+
parent: Joi.DID().optional().allow(''),
|
|
25
|
+
address: Joi.DID().role('ROLE_ASSET').required(),
|
|
26
|
+
issuer: Joi.DID().optional().allow(''),
|
|
27
|
+
}).options({ stripUnknown: true, noDefaults: false });
|
|
28
|
+
runner.use(({ itx }, next) => {
|
|
29
|
+
const { error } = schema.validate(itx);
|
|
30
|
+
if (error) {
|
|
31
|
+
return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
|
|
32
|
+
}
|
|
33
|
+
return next();
|
|
34
|
+
});
|
|
17
35
|
runner.use(
|
|
18
36
|
pipes.VerifyInfo([
|
|
19
|
-
{
|
|
20
|
-
error: 'INSUFFICIENT_DATA',
|
|
21
|
-
message: 'itx.address, itx.moniker and itx.data must not be empty',
|
|
22
|
-
fn: ({ itx }) => itx.address && itx.moniker && itx.data,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
error: 'INVALID_MONIKER',
|
|
26
|
-
message: 'Length of asset moniker should between 2 and 255 characters',
|
|
27
|
-
fn: ({ itx }) => itx.moniker.length >= 2 && itx.moniker.length <= 255,
|
|
28
|
-
},
|
|
29
37
|
{
|
|
30
38
|
error: 'INVALID_ASSET',
|
|
31
39
|
message: 'Asset address is not valid',
|
|
@@ -50,7 +58,7 @@ runner.use(
|
|
|
50
58
|
{
|
|
51
59
|
error: 'INVALID_ASSET',
|
|
52
60
|
message: 'This asset already exist on chain',
|
|
53
|
-
fn: (context) =>
|
|
61
|
+
fn: (context) => !context.assetState,
|
|
54
62
|
},
|
|
55
63
|
])
|
|
56
64
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
const Error = require('@ocap/util/lib/error');
|
|
1
2
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
2
|
-
const { account, asset } = require('@ocap/state');
|
|
3
|
+
const { account, asset, Joi } = require('@ocap/state');
|
|
3
4
|
|
|
4
5
|
// eslint-disable-next-line global-require
|
|
5
6
|
const debug = require('debug')(`${require('../../../package.json').name}:update-asset`);
|
|
@@ -10,20 +11,19 @@ const runner = new Runner();
|
|
|
10
11
|
|
|
11
12
|
runner.use(pipes.VerifyMultiSig(0));
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
14
|
+
// Verify itx
|
|
15
|
+
const schema = Joi.object({
|
|
16
|
+
address: Joi.DID().role('ROLE_ASSET').required(),
|
|
17
|
+
moniker: Joi.string().min(2).max(255).required(),
|
|
18
|
+
data: Joi.any().optional(),
|
|
19
|
+
}).options({ stripUnknown: true, noDefaults: false });
|
|
20
|
+
runner.use(({ itx }, next) => {
|
|
21
|
+
const { error } = schema.validate(itx);
|
|
22
|
+
if (error) {
|
|
23
|
+
return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
|
|
24
|
+
}
|
|
25
|
+
return next();
|
|
26
|
+
});
|
|
27
27
|
|
|
28
28
|
// Ensure asset exist owned by sender and can be modified
|
|
29
29
|
runner.use(pipes.ExtractState({ from: 'itx.address', to: 'assetState', status: 'INVALID_ASSET' }));
|
|
@@ -9,10 +9,10 @@ const { account, stake, evidence, rollupBlock, Joi } = require('@ocap/state');
|
|
|
9
9
|
// eslint-disable-next-line global-require
|
|
10
10
|
const debug = require('debug')(`${require('../../../package.json').name}:claim-block-reward`);
|
|
11
11
|
|
|
12
|
+
const { toStakeAddress } = require('@arcblock/did-util');
|
|
12
13
|
const VerifySigners = require('./pipes/verify-signers');
|
|
13
|
-
const { applyTokenChange,
|
|
14
|
+
const { applyTokenChange, splitTxFee, getBNSum, getRewardLocker, RATE_BASE } = require('../../util');
|
|
14
15
|
|
|
15
|
-
const ZERO = new BN(0);
|
|
16
16
|
const runner = new Runner();
|
|
17
17
|
|
|
18
18
|
// 1. verify itx
|
|
@@ -39,12 +39,21 @@ runner.use((context, next) => {
|
|
|
39
39
|
return next(new Error('INVALID_TX', 'itx.publisher must be same with tx.from'));
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
context.lockerAddress =
|
|
42
|
+
context.lockerAddress = getRewardLocker(itx.rollup);
|
|
43
43
|
|
|
44
44
|
return next();
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
// 2.
|
|
47
|
+
// 2. ensure evidence not exist
|
|
48
|
+
runner.use(pipes.ExtractState({ from: 'itx.evidence.hash', to: 'evidenceSeen', status: 'OK', table: 'evidence' }));
|
|
49
|
+
runner.use(pipes.ExtractState({ from: 'itx.blockHash', to: 'blockClaimed', status: 'OK', table: 'evidence' }));
|
|
50
|
+
runner.use(({ evidenceSeen, blockClaimed }, next) => {
|
|
51
|
+
if (evidenceSeen) return next(new Error('INVALID_TX', 'Claim evidence already seen on this chain'));
|
|
52
|
+
if (blockClaimed) return next(new Error('INVALID_TX', 'Block reward already claimed before this tx'));
|
|
53
|
+
return next();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// 3. verify rollup and block state
|
|
48
57
|
runner.use(pipes.ExtractState({ from: 'itx.rollup', to: 'rollupState', status: 'INVALID_ROLLUP', table: 'rollup' }));
|
|
49
58
|
runner.use(pipes.ExtractState({ from: 'itx.blockHash', to: 'blockState', status: 'INVALID_ROLLUP_BLOCK', table: 'rollupBlock' })); // prettier-ignore
|
|
50
59
|
runner.use((context, next) => {
|
|
@@ -55,24 +64,28 @@ runner.use((context, next) => {
|
|
|
55
64
|
if (blockState.height !== itx.blockHeight) {
|
|
56
65
|
return next(new Error('INVALID_TX', 'Rollup block height does not match'));
|
|
57
66
|
}
|
|
67
|
+
|
|
68
|
+
// If the publisher is not the producer, he should wait at least rollupState.publishWaitingPeriod
|
|
69
|
+
if (blockState.proposer !== itx.publisher) {
|
|
70
|
+
const proposedAt = +new Date(blockState.context.genesisTime);
|
|
71
|
+
const publishedAt = +new Date(context.txTime);
|
|
72
|
+
if (proposedAt + rollupState.publishWaitingPeriod * 1000 > publishedAt) {
|
|
73
|
+
return next(new Error('INVALID_TX', 'Rollup block can only be published by producer during waiting period'));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
context.producerStake = toStakeAddress(blockState.proposer, itx.rollup);
|
|
78
|
+
|
|
58
79
|
return next();
|
|
59
80
|
});
|
|
60
81
|
|
|
61
|
-
//
|
|
82
|
+
// 4. verify tx signers & signatures
|
|
62
83
|
runner.use(VerifySigners({ signersKey: 'tx.signaturesList', allowSender: true }));
|
|
63
84
|
runner.use(pipes.VerifyMultiSigV2({ signersKey: 'signers' }));
|
|
64
85
|
|
|
65
|
-
//
|
|
86
|
+
// 5. verify block reward locker
|
|
66
87
|
runner.use(pipes.ExtractState({ from: 'lockerAddress', to: 'lockerState', status: 'INVALID_LOCKER_STATE', table: 'stake' })); // prettier-ignore
|
|
67
|
-
|
|
68
|
-
// 5. ensure evidence not exist
|
|
69
|
-
runner.use(pipes.ExtractState({ from: 'itx.evidence.hash', to: 'evidenceSeen', status: 'OK', table: 'evidence' }));
|
|
70
|
-
runner.use(pipes.ExtractState({ from: 'itx.blockHash', to: 'blockClaimed', status: 'OK', table: 'evidence' }));
|
|
71
|
-
runner.use(({ evidenceSeen, blockClaimed }, next) => {
|
|
72
|
-
if (evidenceSeen) return next(new Error('INVALID_TX', 'Claim evidence already seen on this chain'));
|
|
73
|
-
if (blockClaimed) return next(new Error('INVALID_TX', 'Block reward already claimed before this tx'));
|
|
74
|
-
return next();
|
|
75
|
-
});
|
|
88
|
+
runner.use(pipes.ExtractState({ from: 'producerStake', to: 'stakeState', status: 'INVALID_STAKE_STATE', table: 'stake' })); // prettier-ignore
|
|
76
89
|
|
|
77
90
|
// 6. verify sender and signer states
|
|
78
91
|
runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INVALID_SENDER_STATE', table: 'account' })); // prettier-ignore
|
|
@@ -85,8 +98,7 @@ runner.use(pipes.ExtractState({ from: 'rollupState.tokenAddress', to: 'tokenStat
|
|
|
85
98
|
// 8. split and aggregate block reward for each tx and the block
|
|
86
99
|
runner.use(pipes.ExtractState({ from: 'blockState.txs', to: 'txs', status: 'INVALID_TX', table: 'tx' }));
|
|
87
100
|
runner.use(async (context, next) => {
|
|
88
|
-
const { itx, txs, rollupState, blockState, lockerState, tokenState } = context;
|
|
89
|
-
const { depositFeeRate, withdrawFeeRate, minDepositFee, maxDepositFee, minWithdrawFee, maxWithdrawFee } = rollupState;
|
|
101
|
+
const { itx, txs, rollupState, blockState, lockerState, tokenState, stakeState } = context;
|
|
90
102
|
const { proposerFeeShare, publisherFeeShare } = rollupState;
|
|
91
103
|
|
|
92
104
|
const shares = {
|
|
@@ -95,23 +107,28 @@ runner.use(async (context, next) => {
|
|
|
95
107
|
validator: 10000 - publisherFeeShare - proposerFeeShare,
|
|
96
108
|
};
|
|
97
109
|
|
|
98
|
-
// 1. loop through the tx and split reward
|
|
99
110
|
const changes = { account: [], stake: [] };
|
|
111
|
+
|
|
112
|
+
// 0. handle slash case
|
|
113
|
+
// TODO: the producer stake balance should be enough for slashing
|
|
114
|
+
const shouldSlash = blockState.proposer !== itx.publisher;
|
|
115
|
+
if (shouldSlash) {
|
|
116
|
+
const slashAmount = new BN(rollupState.minStakeAmount).mul(new BN(rollupState.publishSlashRate)).div(RATE_BASE);
|
|
117
|
+
const slashShare = slashAmount.div(new BN(2));
|
|
118
|
+
changes.stake.push({ address: stakeState.address, delta: `-${slashAmount.toString(10)}`, action: 'slash' });
|
|
119
|
+
changes.stake.push({ address: lockerState.address, delta: slashShare.toString(10), action: 'slash' });
|
|
120
|
+
changes.account.push({ address: itx.publisher, delta: slashShare.toString(10), action: 'reward' });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 1. loop through the tx and split reward
|
|
100
124
|
txs.forEach((x) => {
|
|
101
|
-
const
|
|
125
|
+
const { actualFee } = x.tx.itxJson;
|
|
126
|
+
const { publisher, proposer, validator } = splitTxFee({ total: actualFee, shares, stringify: false });
|
|
127
|
+
|
|
128
|
+
changes.stake.push({ address: lockerState.address, delta: `-${actualFee}`, action: 'claim' });
|
|
129
|
+
changes.account.push({ address: itx.publisher, delta: publisher.toString(10), action: 'fee' });
|
|
130
|
+
|
|
102
131
|
if (x.type === 'deposit_token_v2') {
|
|
103
|
-
const { fee, feeShares } = splitBlockReward({
|
|
104
|
-
total: amount,
|
|
105
|
-
feeRate: depositFeeRate,
|
|
106
|
-
maxFee: maxDepositFee,
|
|
107
|
-
minFee: minDepositFee,
|
|
108
|
-
shares,
|
|
109
|
-
stringify: false,
|
|
110
|
-
});
|
|
111
|
-
changes.stake.push({ address: lockerState.address, delta: `-${fee}`, action: 'claim' });
|
|
112
|
-
|
|
113
|
-
const { publisher, proposer, validator } = feeShares;
|
|
114
|
-
changes.account.push({ address: itx.publisher, delta: publisher.toString(10), action: 'fee' });
|
|
115
132
|
changes.account.push({ address: x.tx.itxJson.proposer, delta: proposer.toString(10), action: 'fee' });
|
|
116
133
|
|
|
117
134
|
// block and tx signers share the validator part
|
|
@@ -122,18 +139,6 @@ runner.use(async (context, next) => {
|
|
|
122
139
|
changes.account.push({ address: v, delta: validatorShare.toString(10), action: 'fee' })
|
|
123
140
|
);
|
|
124
141
|
} else if (x.type === 'withdraw_token_v2') {
|
|
125
|
-
const { fee, feeShares } = splitBlockReward({
|
|
126
|
-
total: amount,
|
|
127
|
-
feeRate: withdrawFeeRate,
|
|
128
|
-
maxFee: maxWithdrawFee,
|
|
129
|
-
minFee: minWithdrawFee,
|
|
130
|
-
shares,
|
|
131
|
-
stringify: false,
|
|
132
|
-
});
|
|
133
|
-
changes.stake.push({ address: lockerState.address, delta: `-${fee}`, action: 'claim' });
|
|
134
|
-
|
|
135
|
-
const { publisher, proposer, validator } = feeShares;
|
|
136
|
-
changes.account.push({ address: itx.publisher, delta: publisher.toString(10), action: 'fee' });
|
|
137
142
|
changes.account.push({ address: blockState.proposer, delta: proposer.toString(10), action: 'fee' });
|
|
138
143
|
|
|
139
144
|
// block signers share the validator part
|
|
@@ -157,7 +162,7 @@ runner.use(async (context, next) => {
|
|
|
157
162
|
acc[x] = {
|
|
158
163
|
address: x,
|
|
159
164
|
token: tokenState.address,
|
|
160
|
-
delta: grouped.stake[x].
|
|
165
|
+
delta: getBNSum(...grouped.stake[x].map((c) => c.delta)),
|
|
161
166
|
action: grouped.stake[x][grouped.stake[x].length - 1].action,
|
|
162
167
|
};
|
|
163
168
|
return acc;
|
|
@@ -166,7 +171,7 @@ runner.use(async (context, next) => {
|
|
|
166
171
|
acc[x] = {
|
|
167
172
|
address: x,
|
|
168
173
|
token: tokenState.address,
|
|
169
|
-
delta: grouped.account[x].
|
|
174
|
+
delta: getBNSum(...grouped.account[x].map((c) => c.delta)),
|
|
170
175
|
action: grouped.account[x][grouped.account[x].length - 1].action,
|
|
171
176
|
};
|
|
172
177
|
return acc;
|
|
@@ -179,8 +184,6 @@ runner.use(async (context, next) => {
|
|
|
179
184
|
return next();
|
|
180
185
|
});
|
|
181
186
|
|
|
182
|
-
// TODO: verify locker balance
|
|
183
|
-
|
|
184
187
|
// 9. extract all accounts that will be updated exist
|
|
185
188
|
runner.use((context, next) => {
|
|
186
189
|
const { updates, senderState, signerStates } = context;
|
|
@@ -208,17 +211,23 @@ runner.use((context, next) => {
|
|
|
208
211
|
});
|
|
209
212
|
|
|
210
213
|
// 10. update state
|
|
211
|
-
// FIXME: (future) we have a low probability of encounter QLDB 40 documents in a single transaction limit
|
|
212
214
|
runner.use(
|
|
213
215
|
async (context, next) => {
|
|
214
|
-
const { tx, itx, updates, statedb, senderState, blockState, lockerState, accountStates } = context;
|
|
215
|
-
|
|
216
|
-
// This balance from the locker will be checked when apply updates
|
|
217
|
-
const lockerUpdates = applyTokenChange(lockerState, updates.stake[lockerState.address]);
|
|
216
|
+
const { tx, itx, updates, statedb, senderState, blockState, lockerState, stakeState, accountStates } = context;
|
|
218
217
|
|
|
219
|
-
const [
|
|
220
|
-
// update
|
|
221
|
-
|
|
218
|
+
const [newStakeStates, newAccountStates, newBlockState] = await Promise.all([
|
|
219
|
+
// update stake states
|
|
220
|
+
Promise.all(
|
|
221
|
+
[lockerState, stakeState].map((x) =>
|
|
222
|
+
updates.stake[x.address]
|
|
223
|
+
? statedb.stake.update(
|
|
224
|
+
x.address,
|
|
225
|
+
stake.update(x, applyTokenChange(x, updates.stake[x.address]), context),
|
|
226
|
+
context
|
|
227
|
+
)
|
|
228
|
+
: x
|
|
229
|
+
)
|
|
230
|
+
),
|
|
222
231
|
|
|
223
232
|
// update accounts(proposer, publisher, validator)
|
|
224
233
|
Promise.all(
|
|
@@ -254,12 +263,12 @@ runner.use(
|
|
|
254
263
|
),
|
|
255
264
|
]);
|
|
256
265
|
|
|
257
|
-
context.
|
|
258
|
-
context.updatedAccounts.forEach((x) => (x.delta = x.delta.toString(10))); // eslint-disable-line
|
|
266
|
+
context.stakeStates = newStakeStates;
|
|
259
267
|
context.accountStates = newAccountStates;
|
|
260
|
-
context.stakeState = newLockerState;
|
|
261
268
|
context.blockState = newBlockState;
|
|
262
269
|
|
|
270
|
+
context.updatedAccounts = cloneDeep([...Object.values(updates.account), ...Object.values(updates.stake)]);
|
|
271
|
+
|
|
263
272
|
debug('claim-block-reward', itx);
|
|
264
273
|
|
|
265
274
|
next();
|