@ocap/state 1.28.9 → 1.29.0
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/esm/_virtual/rolldown_runtime.mjs +18 -0
- package/esm/contexts/state.d.mts +15 -0
- package/esm/contexts/state.mjs +17 -0
- package/esm/index.d.mts +20 -0
- package/esm/index.mjs +47 -0
- package/esm/states/account.d.mts +18 -0
- package/esm/states/account.mjs +91 -0
- package/esm/states/asset.d.mts +14 -0
- package/esm/states/asset.mjs +80 -0
- package/esm/states/blacklist.d.mts +36 -0
- package/esm/states/blacklist.mjs +71 -0
- package/esm/states/chain.d.mts +30 -0
- package/esm/states/chain.mjs +52 -0
- package/esm/states/delegation.d.mts +11 -0
- package/esm/states/delegation.mjs +42 -0
- package/esm/states/evidence.d.mts +12 -0
- package/esm/states/evidence.mjs +35 -0
- package/esm/states/factory.d.mts +12 -0
- package/esm/states/factory.mjs +76 -0
- package/esm/states/rollup-block.d.mts +13 -0
- package/esm/states/rollup-block.mjs +75 -0
- package/esm/states/rollup.d.mts +18 -0
- package/esm/states/rollup.mjs +215 -0
- package/esm/states/stake.d.mts +13 -0
- package/esm/states/stake.mjs +89 -0
- package/esm/states/token-factory.d.mts +13 -0
- package/esm/states/token-factory.mjs +76 -0
- package/esm/states/token.d.mts +14 -0
- package/esm/states/token.mjs +109 -0
- package/esm/states/tx.d.mts +233 -0
- package/esm/states/tx.mjs +867 -0
- package/esm/util.d.mts +6 -0
- package/esm/util.mjs +18 -0
- package/lib/_virtual/rolldown_runtime.cjs +43 -0
- package/lib/contexts/state.cjs +19 -0
- package/lib/contexts/state.d.cts +15 -0
- package/lib/index.cjs +121 -0
- package/lib/index.d.cts +20 -0
- package/lib/states/account.cjs +106 -0
- package/lib/states/account.d.cts +18 -0
- package/lib/states/asset.cjs +91 -0
- package/lib/states/asset.d.cts +14 -0
- package/lib/states/blacklist.cjs +74 -0
- package/lib/states/blacklist.d.cts +36 -0
- package/lib/states/chain.cjs +62 -0
- package/lib/states/chain.d.cts +30 -0
- package/lib/states/delegation.cjs +50 -0
- package/lib/states/delegation.d.cts +11 -0
- package/lib/states/evidence.cjs +44 -0
- package/lib/states/evidence.d.cts +12 -0
- package/lib/states/factory.cjs +85 -0
- package/lib/states/factory.d.cts +12 -0
- package/lib/states/rollup-block.cjs +85 -0
- package/lib/states/rollup-block.d.cts +13 -0
- package/lib/states/rollup.cjs +230 -0
- package/lib/states/rollup.d.cts +18 -0
- package/lib/states/stake.cjs +99 -0
- package/lib/states/stake.d.cts +13 -0
- package/lib/states/token-factory.cjs +86 -0
- package/lib/states/token-factory.d.cts +13 -0
- package/lib/states/token.cjs +121 -0
- package/lib/states/token.d.cts +14 -0
- package/lib/states/tx.cjs +889 -0
- package/lib/states/tx.d.cts +233 -0
- package/lib/util.cjs +19 -0
- package/lib/util.d.cts +6 -0
- package/package.json +46 -14
- package/lib/contexts/state.js +0 -19
- package/lib/index.js +0 -63
- package/lib/states/account.js +0 -95
- package/lib/states/asset.js +0 -91
- package/lib/states/blacklist.js +0 -103
- package/lib/states/chain.js +0 -49
- package/lib/states/delegation.js +0 -46
- package/lib/states/evidence.js +0 -35
- package/lib/states/factory.js +0 -92
- package/lib/states/rollup-block.js +0 -84
- package/lib/states/rollup.js +0 -297
- package/lib/states/stake.js +0 -83
- package/lib/states/token-factory.js +0 -74
- package/lib/states/token.js +0 -124
- package/lib/states/tx.js +0 -896
- package/lib/util.js +0 -28
package/lib/states/blacklist.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
const fs = require('node:fs');
|
|
2
|
-
const { isSameDid } = require('@ocap/util');
|
|
3
|
-
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
4
|
-
const { BloomFilter } = require('bloom-filters');
|
|
5
|
-
|
|
6
|
-
class Blacklist {
|
|
7
|
-
constructor({
|
|
8
|
-
size = 256,
|
|
9
|
-
numHashFns = 4,
|
|
10
|
-
dumpPath = '',
|
|
11
|
-
dumpInterval = 0,
|
|
12
|
-
getBlockedAccounts = null,
|
|
13
|
-
getTrustedAccounts = null,
|
|
14
|
-
} = {}) {
|
|
15
|
-
let filter;
|
|
16
|
-
|
|
17
|
-
if (dumpPath && fs.existsSync(dumpPath)) {
|
|
18
|
-
try {
|
|
19
|
-
const json = JSON.parse(fs.readFileSync(dumpPath));
|
|
20
|
-
filter = BloomFilter.fromJSON(json);
|
|
21
|
-
} catch (_err) {
|
|
22
|
-
throw new Error('INTERNAL', `Cat not read serialized blacklist from json${dumpPath}`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!filter) {
|
|
27
|
-
filter = new BloomFilter(size, numHashFns);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
this.dumpPath = dumpPath;
|
|
31
|
-
this.filter = filter;
|
|
32
|
-
this.getBlockedAccounts = getBlockedAccounts;
|
|
33
|
-
this.getTrustedAccounts = getTrustedAccounts;
|
|
34
|
-
|
|
35
|
-
if (dumpPath && dumpInterval > 0) {
|
|
36
|
-
this.timer = setTimeout(() => {
|
|
37
|
-
this.dump();
|
|
38
|
-
|
|
39
|
-
// Schedule for next
|
|
40
|
-
this.timer = setTimeout(() => {
|
|
41
|
-
this.dump();
|
|
42
|
-
}, dumpInterval);
|
|
43
|
-
this.timer.unref();
|
|
44
|
-
}, dumpInterval);
|
|
45
|
-
this.timer.unref();
|
|
46
|
-
|
|
47
|
-
process.on('beforeExit', () => {
|
|
48
|
-
this.dump();
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
has(key) {
|
|
54
|
-
return this.filter.has(key);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
add(key) {
|
|
58
|
-
return this.filter.add(key);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async isBlocked(address) {
|
|
62
|
-
if (typeof this.getBlockedAccounts === 'function') {
|
|
63
|
-
const blocked = await this.getBlockedAccounts();
|
|
64
|
-
if (blocked.length) {
|
|
65
|
-
return blocked.some((b) => isSameDid(b, address));
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async isTrusted(address) {
|
|
73
|
-
if (typeof this.getTrustedAccounts === 'function') {
|
|
74
|
-
const trustedAccounts = await this.getTrustedAccounts();
|
|
75
|
-
if (trustedAccounts.length) {
|
|
76
|
-
return trustedAccounts.some((b) => isSameDid(b, address));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async getTrustedAccountConfig(address) {
|
|
84
|
-
if (typeof this.getTrustedAccounts === 'function') {
|
|
85
|
-
const trustedAccounts = await this.getTrustedAccounts();
|
|
86
|
-
return trustedAccounts.find((x) => isSameDid(x.address, address));
|
|
87
|
-
}
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
dump() {
|
|
92
|
-
try {
|
|
93
|
-
const exported = this.filter.saveAsJSON();
|
|
94
|
-
const tmpFile = `${this.dumpPath}~`;
|
|
95
|
-
fs.writeFileSync(tmpFile, JSON.stringify(exported));
|
|
96
|
-
fs.renameSync(tmpFile, this.dumpPath);
|
|
97
|
-
} catch (_err) {
|
|
98
|
-
// Do nothing
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
module.exports = Blacklist;
|
package/lib/states/chain.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
const pick = require('lodash/pick');
|
|
2
|
-
const isEqual = require('lodash/isEqual');
|
|
3
|
-
const get = require('lodash/get');
|
|
4
|
-
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
5
|
-
|
|
6
|
-
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
7
|
-
|
|
8
|
-
const create = (attrs) => {
|
|
9
|
-
const context = { txTime: new Date().toISOString() };
|
|
10
|
-
const chain = {
|
|
11
|
-
context: createStateContext(context),
|
|
12
|
-
...pick(attrs, ['address', 'chainId', 'version', 'transaction', 'moderator', 'accounts', 'token', 'vaults']),
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
return chain;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const update = (state, updates) => {
|
|
19
|
-
const immutableAttrs = ['chainId', 'moderator', 'token.symbol', 'token.decimal'];
|
|
20
|
-
|
|
21
|
-
// should not update these attrs cause the token address is generated from these attrs
|
|
22
|
-
if (!process.env.CHAIN_TOKEN_ADDRESS) {
|
|
23
|
-
immutableAttrs.push(
|
|
24
|
-
'token.name',
|
|
25
|
-
'token.description',
|
|
26
|
-
'token.unit',
|
|
27
|
-
'token.totalSupply',
|
|
28
|
-
'token.initialSupply',
|
|
29
|
-
'token.foreignToken'
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
for (const attr of immutableAttrs) {
|
|
34
|
-
if (get(updates, attr) && !isEqual(get(updates, attr), get(state, attr))) {
|
|
35
|
-
throw new Error('FORBIDDEN', `Cannot update ${attr} field on chain state`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const context = { txTime: new Date().toISOString() };
|
|
40
|
-
const chain = {
|
|
41
|
-
...state,
|
|
42
|
-
...pick(updates, ['version', 'transaction', 'accounts', 'vaults', 'token']),
|
|
43
|
-
context: updateStateContext(state.context, context),
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
return chain;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
module.exports = { create, update };
|
package/lib/states/delegation.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
const pick = require('lodash/pick');
|
|
2
|
-
const { toAddress } = require('@ocap/util');
|
|
3
|
-
|
|
4
|
-
// FIXME: add better validation
|
|
5
|
-
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
6
|
-
const { nullify } = require('../util');
|
|
7
|
-
|
|
8
|
-
const create = (attrs, context) => {
|
|
9
|
-
const delegation = {
|
|
10
|
-
context: createStateContext(context),
|
|
11
|
-
...pick(attrs, ['address', 'to', 'from', 'ops', 'data']),
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
if (!delegation.data) {
|
|
15
|
-
delegation.data = null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
delegation.address = toAddress(delegation.address);
|
|
19
|
-
delegation.ops = nullify(delegation.ops);
|
|
20
|
-
|
|
21
|
-
return delegation;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const update = (state, attrs, context) => {
|
|
25
|
-
const delegation = {
|
|
26
|
-
...state,
|
|
27
|
-
...pick(attrs, ['ops', 'data']),
|
|
28
|
-
context: updateStateContext(state.context, context),
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
if (!delegation.data) {
|
|
32
|
-
delegation.data = null;
|
|
33
|
-
}
|
|
34
|
-
if (!state.to && attrs.to) {
|
|
35
|
-
delegation.to = toAddress(attrs.to);
|
|
36
|
-
}
|
|
37
|
-
if (!state.from && attrs.from) {
|
|
38
|
-
delegation.from = toAddress(attrs.from);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
delegation.ops = nullify(delegation.ops);
|
|
42
|
-
|
|
43
|
-
return delegation;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
module.exports = { create, update };
|
package/lib/states/evidence.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
const pick = require('lodash/pick');
|
|
2
|
-
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
3
|
-
const { Joi, schemas, patterns } = require('@arcblock/validator');
|
|
4
|
-
|
|
5
|
-
const { create: createStateContext } = require('../contexts/state');
|
|
6
|
-
|
|
7
|
-
const schema = Joi.object({
|
|
8
|
-
hash: Joi.string().regex(patterns.txHash).required(),
|
|
9
|
-
context: schemas.context,
|
|
10
|
-
data: Joi.any().optional().allow(null),
|
|
11
|
-
}).options({ stripUnknown: true, noDefaults: false });
|
|
12
|
-
|
|
13
|
-
const create = (attrs, context) => {
|
|
14
|
-
const evidence = {
|
|
15
|
-
context: createStateContext(context),
|
|
16
|
-
...pick(attrs, ['hash', 'data']),
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
return validate(evidence);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const validate = (state) => {
|
|
23
|
-
const { value, error } = schema.validate(state);
|
|
24
|
-
if (error) {
|
|
25
|
-
throw new Error('INVALID_EVIDENCE', `Invalid evidence state: ${error.details.map((x) => x.message).join(', ')}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!value.data) {
|
|
29
|
-
value.data = null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return value;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
module.exports = { create, validate, schema };
|
package/lib/states/factory.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
const pick = require('lodash/pick');
|
|
2
|
-
const { Joi, schemas } = require('@arcblock/validator');
|
|
3
|
-
const { compile, merge, getQuota } = require('@ocap/contract');
|
|
4
|
-
const { toAddress } = require('@ocap/util');
|
|
5
|
-
|
|
6
|
-
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
7
|
-
|
|
8
|
-
const stateSchema = Joi.object({
|
|
9
|
-
...schemas.factoryProps,
|
|
10
|
-
owner: Joi.DID().prefix().required(),
|
|
11
|
-
numMinted: Joi.number().min(0).default(0),
|
|
12
|
-
lastSettlement: Joi.date().iso().raw().allow(''),
|
|
13
|
-
tokens: Joi.object().pattern(Joi.DID().prefix().role('ROLE_TOKEN'), Joi.BN().min(0)).default({}),
|
|
14
|
-
context: schemas.context,
|
|
15
|
-
}).options({
|
|
16
|
-
stripUnknown: true,
|
|
17
|
-
noDefaults: false,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const compileHook = (hook, quota) => {
|
|
21
|
-
if (hook.type === 'contract') {
|
|
22
|
-
hook.compiled = merge(compile(hook.hook, quota));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return hook;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const create = (attrs, context) => {
|
|
29
|
-
const factory = {
|
|
30
|
-
numMinted: 0,
|
|
31
|
-
lastSettlement: '',
|
|
32
|
-
tokens: {},
|
|
33
|
-
context: createStateContext(context),
|
|
34
|
-
...pick(attrs, [
|
|
35
|
-
'address',
|
|
36
|
-
'owner',
|
|
37
|
-
'name',
|
|
38
|
-
'description',
|
|
39
|
-
'settlement',
|
|
40
|
-
'limit',
|
|
41
|
-
'tokens',
|
|
42
|
-
'trustedIssuers',
|
|
43
|
-
'input',
|
|
44
|
-
'output',
|
|
45
|
-
'display',
|
|
46
|
-
'hooks',
|
|
47
|
-
'data',
|
|
48
|
-
]),
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
if (!factory.data) {
|
|
52
|
-
factory.data = null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const quota = getQuota(factory.input);
|
|
56
|
-
factory.hooks = (factory.hooks || []).map((x) => compileHook(x, quota));
|
|
57
|
-
|
|
58
|
-
factory.address = toAddress(factory.address);
|
|
59
|
-
|
|
60
|
-
return validate(factory);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const update = (state, attrs, context) => {
|
|
64
|
-
const factory = {
|
|
65
|
-
...state,
|
|
66
|
-
...pick(attrs, ['numMinted', 'tokens']),
|
|
67
|
-
context: updateStateContext(state.context, context),
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
if (factory.output?.tags === null) {
|
|
71
|
-
factory.output.tags = [];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return validate(factory);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const validate = (state) => {
|
|
78
|
-
const { value, error } = stateSchema.validate(state);
|
|
79
|
-
if (error) {
|
|
80
|
-
throw new Error(`Invalid factory: ${error.details.map((x) => x.message).join(', ')}`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
['display'].forEach((key) => {
|
|
84
|
-
if (!value[key]) {
|
|
85
|
-
delete value[key];
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return value;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
module.exports = { create, update, validate };
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
const pick = require('lodash/pick');
|
|
2
|
-
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
3
|
-
const { Joi, schemas, patterns } = require('@arcblock/validator');
|
|
4
|
-
|
|
5
|
-
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
6
|
-
|
|
7
|
-
const schema = Joi.object({
|
|
8
|
-
hash: Joi.string().regex(patterns.txHash).required(),
|
|
9
|
-
height: Joi.number().integer().greater(0).required(),
|
|
10
|
-
merkleRoot: Joi.string().regex(patterns.txHash).required(),
|
|
11
|
-
previousHash: Joi.string().when('height', {
|
|
12
|
-
is: 1,
|
|
13
|
-
then: Joi.string().optional().allow(null).allow(''),
|
|
14
|
-
otherwise: Joi.string().regex(patterns.txHash).required(),
|
|
15
|
-
}),
|
|
16
|
-
txsHash: Joi.string().regex(patterns.txHash).required(),
|
|
17
|
-
txs: Joi.array().items(Joi.string().regex(patterns.txHash).required()).min(1).unique().required(),
|
|
18
|
-
|
|
19
|
-
proposer: Joi.DID().prefix().wallet('ethereum').required(),
|
|
20
|
-
signatures: schemas.multiSig.min(1).required(),
|
|
21
|
-
|
|
22
|
-
rollup: Joi.DID().prefix().role('ROLE_ROLLUP').required(),
|
|
23
|
-
|
|
24
|
-
mintedAmount: Joi.BN().min(0).optional().allow(null).default('0'),
|
|
25
|
-
burnedAmount: Joi.BN().min(0).optional().allow(null).default('0'),
|
|
26
|
-
rewardAmount: Joi.BN().min(0).optional().allow(null).default('0'),
|
|
27
|
-
|
|
28
|
-
minReward: Joi.BN().min(0).required(),
|
|
29
|
-
|
|
30
|
-
governance: Joi.boolean().default(false),
|
|
31
|
-
|
|
32
|
-
context: schemas.context,
|
|
33
|
-
data: Joi.any().optional().allow(null),
|
|
34
|
-
}).options({ stripUnknown: true, noDefaults: false });
|
|
35
|
-
|
|
36
|
-
const create = (attrs, context) => {
|
|
37
|
-
const block = {
|
|
38
|
-
context: createStateContext(context),
|
|
39
|
-
...pick(attrs, [
|
|
40
|
-
'hash',
|
|
41
|
-
'height',
|
|
42
|
-
'merkleRoot',
|
|
43
|
-
'previousHash',
|
|
44
|
-
'txsHash',
|
|
45
|
-
'txs',
|
|
46
|
-
'proposer',
|
|
47
|
-
'signatures',
|
|
48
|
-
'rollup',
|
|
49
|
-
'mintedAmount',
|
|
50
|
-
'burnedAmount',
|
|
51
|
-
'rewardAmount',
|
|
52
|
-
'minReward',
|
|
53
|
-
'governance',
|
|
54
|
-
'data',
|
|
55
|
-
]),
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
return validate(block);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// We only support update block context
|
|
62
|
-
const update = (state, context) => {
|
|
63
|
-
const rollup = {
|
|
64
|
-
...state,
|
|
65
|
-
context: updateStateContext(state.context, context),
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
return validate(rollup);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const validate = (state) => {
|
|
72
|
-
const { value, error } = schema.validate(state);
|
|
73
|
-
if (error) {
|
|
74
|
-
throw new Error('INVALID_ROLLUP_BLOCK', `Invalid rollup block: ${error.details.map((x) => x.message).join(', ')}`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (!value.data) {
|
|
78
|
-
value.data = null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return value;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
module.exports = { create, update, validate, schema };
|
package/lib/states/rollup.js
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
const pick = require('lodash/pick');
|
|
2
|
-
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
3
|
-
const { toAddress } = require('@ocap/util');
|
|
4
|
-
|
|
5
|
-
const { Joi, schemas, patterns } = require('@arcblock/validator');
|
|
6
|
-
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
7
|
-
|
|
8
|
-
const validator = Joi.object({
|
|
9
|
-
pk: Joi.string().required(),
|
|
10
|
-
address: Joi.DID().prefix().required(),
|
|
11
|
-
endpoint: Joi.string()
|
|
12
|
-
.uri({ scheme: [/https?/] })
|
|
13
|
-
.required(),
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const schema = Joi.object({
|
|
17
|
-
// a closed rollup must be paused
|
|
18
|
-
address: Joi.DID().prefix().role('ROLE_ROLLUP').required(),
|
|
19
|
-
tokenAddress: Joi.DID().prefix().role('ROLE_TOKEN').required(),
|
|
20
|
-
contractAddress: Joi.DID().prefix().wallet('ethereum').required(),
|
|
21
|
-
vaultAddress: Joi.DID().wallet('ethereum').optional().allow(null).allow(''), // FIXME: this should be enforced
|
|
22
|
-
migrateHistory: Joi.array().items(Joi.DID().prefix().wallet('ethereum')).default([]),
|
|
23
|
-
vaultHistory: Joi.array().items(Joi.DID().prefix().wallet('ethereum')).default([]),
|
|
24
|
-
paused: Joi.boolean().default(false),
|
|
25
|
-
closed: Joi.boolean().default(false),
|
|
26
|
-
|
|
27
|
-
seedValidators: Joi.array().items(validator).min(1).required(),
|
|
28
|
-
validators: Joi.array().items(validator).max(24).default([]),
|
|
29
|
-
|
|
30
|
-
minStakeAmount: Joi.BN().positive().required(),
|
|
31
|
-
maxStakeAmount: Joi.BN().min(Joi.ref('minStakeAmount')).required(),
|
|
32
|
-
|
|
33
|
-
minSignerCount: Joi.number()
|
|
34
|
-
.integer()
|
|
35
|
-
.min(Joi.ref('seedValidators', { adjust: (v) => v.length }))
|
|
36
|
-
.required(),
|
|
37
|
-
maxSignerCount: Joi.number().integer().min(1).max(8).min(Joi.ref('minSignerCount')).required(),
|
|
38
|
-
|
|
39
|
-
minBlockSize: Joi.number().integer().min(1).required(),
|
|
40
|
-
maxBlockSize: Joi.number().integer().min(1).max(15).min(Joi.ref('minBlockSize')).required(),
|
|
41
|
-
|
|
42
|
-
minBlockInterval: Joi.number()
|
|
43
|
-
.integer()
|
|
44
|
-
.min(1)
|
|
45
|
-
.max(60 * 60)
|
|
46
|
-
.required(), // in seconds
|
|
47
|
-
minBlockConfirmation: Joi.number().integer().min(1).max(100).required(),
|
|
48
|
-
|
|
49
|
-
minDepositAmount: Joi.BN().positive().less(Joi.ref('minStakeAmount')).required(),
|
|
50
|
-
maxDepositAmount: Joi.BN().greater(Joi.ref('minDepositAmount')).less(Joi.ref('minStakeAmount')).required(),
|
|
51
|
-
minWithdrawAmount: Joi.BN().positive().required(),
|
|
52
|
-
maxWithdrawAmount: Joi.BN().greater(Joi.ref('minWithdrawAmount')).required(),
|
|
53
|
-
|
|
54
|
-
depositFeeRate: Joi.number().integer().min(0).max(10000).required(),
|
|
55
|
-
withdrawFeeRate: Joi.number().integer().min(0).max(10000).required(),
|
|
56
|
-
proposerFeeShare: Joi.number().integer().min(1).max(10000).required(),
|
|
57
|
-
publisherFeeShare: Joi.number().integer().min(1).max(10000).required(),
|
|
58
|
-
minDepositFee: Joi.BN().positive().required(),
|
|
59
|
-
maxDepositFee: Joi.BN().min(Joi.ref('minDepositFee')).required(),
|
|
60
|
-
minWithdrawFee: Joi.BN().positive().required(),
|
|
61
|
-
maxWithdrawFee: Joi.BN().min(Joi.ref('minWithdrawFee')).required(),
|
|
62
|
-
|
|
63
|
-
blockHeight: Joi.number().integer().min(0).required(),
|
|
64
|
-
blockHash: Joi.string().regex(patterns.txHash).optional().allow(null).allow(''),
|
|
65
|
-
issuer: Joi.DID().prefix().optional().allow(null),
|
|
66
|
-
|
|
67
|
-
leaveWaitingPeriod: Joi.number().integer().min(Joi.ref('minBlockInterval')).default(0),
|
|
68
|
-
publishWaitingPeriod: Joi.number().integer().min(Joi.ref('minBlockInterval')).default(0),
|
|
69
|
-
publishSlashRate: Joi.number().integer().min(1).max(10000).required(),
|
|
70
|
-
|
|
71
|
-
context: schemas.context,
|
|
72
|
-
data: Joi.any().optional().allow(null),
|
|
73
|
-
}).options({ stripUnknown: true, noDefaults: false });
|
|
74
|
-
|
|
75
|
-
const create = (attrs, context) => {
|
|
76
|
-
const rollup = {
|
|
77
|
-
context: createStateContext(context),
|
|
78
|
-
paused: false,
|
|
79
|
-
closed: false,
|
|
80
|
-
migrateHistory: [],
|
|
81
|
-
vaultHistory: [],
|
|
82
|
-
blockHeight: 0,
|
|
83
|
-
blockHash: '',
|
|
84
|
-
...pick(attrs, [
|
|
85
|
-
'address',
|
|
86
|
-
'tokenAddress',
|
|
87
|
-
'vaultAddress',
|
|
88
|
-
'contractAddress',
|
|
89
|
-
'seedValidators',
|
|
90
|
-
'validators',
|
|
91
|
-
'minStakeAmount',
|
|
92
|
-
'maxStakeAmount',
|
|
93
|
-
'minSignerCount',
|
|
94
|
-
'maxSignerCount',
|
|
95
|
-
'minBlockSize',
|
|
96
|
-
'maxBlockSize',
|
|
97
|
-
'minBlockInterval',
|
|
98
|
-
'minBlockConfirmation',
|
|
99
|
-
'minDepositAmount',
|
|
100
|
-
'maxDepositAmount',
|
|
101
|
-
'minWithdrawAmount',
|
|
102
|
-
'maxWithdrawAmount',
|
|
103
|
-
'depositFeeRate',
|
|
104
|
-
'withdrawFeeRate',
|
|
105
|
-
'proposerFeeShare',
|
|
106
|
-
'publisherFeeShare',
|
|
107
|
-
'minDepositFee',
|
|
108
|
-
'maxDepositFee',
|
|
109
|
-
'minWithdrawFee',
|
|
110
|
-
'maxWithdrawFee',
|
|
111
|
-
'leaveWaitingPeriod',
|
|
112
|
-
'publishWaitingPeriod',
|
|
113
|
-
'publishSlashRate',
|
|
114
|
-
'issuer',
|
|
115
|
-
'data',
|
|
116
|
-
]),
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
rollup.address = toAddress(rollup.address);
|
|
120
|
-
|
|
121
|
-
return validate(rollup);
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const update = (state, updates, context) => {
|
|
125
|
-
// latest block height must be incremented on each update
|
|
126
|
-
// latest block hash must be updated together with block height
|
|
127
|
-
if (updates.blockHeight && !updates.blockHash) {
|
|
128
|
-
throw new Error('INVALID_ROLLUP_UPDATE', 'blockHash must be updated together with blockHeight');
|
|
129
|
-
}
|
|
130
|
-
if (updates.blockHash && !updates.blockHeight) {
|
|
131
|
-
throw new Error('INVALID_ROLLUP_UPDATE', 'blockHeight must be updated together with blockHash');
|
|
132
|
-
}
|
|
133
|
-
if (updates.blockHeight && updates.blockHash) {
|
|
134
|
-
if (updates.blockHeight !== state.blockHeight + 1) {
|
|
135
|
-
throw new Error('INVALID_ROLLUP_UPDATE', 'blockHeight must be incremented');
|
|
136
|
-
}
|
|
137
|
-
if (updates.blockHash === state.blockHash) {
|
|
138
|
-
throw new Error('INVALID_ROLLUP_UPDATE', 'blockHash can not remain unchanged between blocks');
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (typeof updates.blockHeight !== 'undefined' && !updates.blockHeight) {
|
|
143
|
-
throw new Error('INVALID_ROLLUP_UPDATE', 'blockHeight can not be unset');
|
|
144
|
-
}
|
|
145
|
-
if (typeof updates.blockHash !== 'undefined' && !updates.blockHash) {
|
|
146
|
-
throw new Error('INVALID_ROLLUP_UPDATE', 'blockHash can not be unset');
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const rollup = {
|
|
150
|
-
...state,
|
|
151
|
-
...pick(updates, [
|
|
152
|
-
'validators',
|
|
153
|
-
'minStakeAmount',
|
|
154
|
-
'maxStakeAmount',
|
|
155
|
-
'minSignerCount',
|
|
156
|
-
'maxSignerCount',
|
|
157
|
-
'minBlockSize',
|
|
158
|
-
'maxBlockSize',
|
|
159
|
-
'minBlockInterval',
|
|
160
|
-
'minBlockConfirmation',
|
|
161
|
-
'minDepositAmount',
|
|
162
|
-
'maxDepositAmount',
|
|
163
|
-
'minWithdrawAmount',
|
|
164
|
-
'maxWithdrawAmount',
|
|
165
|
-
'depositFeeRate',
|
|
166
|
-
'withdrawFeeRate',
|
|
167
|
-
'publisherFeeShare',
|
|
168
|
-
'minDepositFee',
|
|
169
|
-
'maxDepositFee',
|
|
170
|
-
'minWithdrawFee',
|
|
171
|
-
'maxWithdrawFee',
|
|
172
|
-
'blockHeight',
|
|
173
|
-
'blockHash',
|
|
174
|
-
'leaveWaitingPeriod',
|
|
175
|
-
'publishWaitingPeriod',
|
|
176
|
-
'publishSlashRate',
|
|
177
|
-
'data',
|
|
178
|
-
]),
|
|
179
|
-
context: updateStateContext(state.context, context),
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
return validate(rollup);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const pause = (state, context) => {
|
|
186
|
-
if (state.closed) {
|
|
187
|
-
throw new Error('INVALID_PAUSE_ATTEMPT', 'rollup already closed');
|
|
188
|
-
}
|
|
189
|
-
if (state.paused) {
|
|
190
|
-
throw new Error('INVALID_PAUSE_ATTEMPT', 'rollup already paused');
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const rollup = {
|
|
194
|
-
...state,
|
|
195
|
-
paused: true,
|
|
196
|
-
context: updateStateContext(state.context, context),
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
return validate(rollup);
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const close = (state, context) => {
|
|
203
|
-
if (state.closed) {
|
|
204
|
-
throw new Error('INVALID_CLOSE_ATTEMPT', 'rollup already closed');
|
|
205
|
-
}
|
|
206
|
-
if (!state.paused) {
|
|
207
|
-
throw new Error('INVALID_CLOSE_ATTEMPT', 'rollup must be paused');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const rollup = {
|
|
211
|
-
...state,
|
|
212
|
-
closed: true,
|
|
213
|
-
context: updateStateContext(state.context, context),
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
return validate(rollup);
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const resume = (state, context) => {
|
|
220
|
-
if (state.closed) {
|
|
221
|
-
throw new Error('INVALID_RESUME_ATTEMPT', 'rollup is closed');
|
|
222
|
-
}
|
|
223
|
-
if (state.paused === false) {
|
|
224
|
-
throw new Error('INVALID_RESUME_ATTEMPT', 'rollup not paused');
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const rollup = {
|
|
228
|
-
...state,
|
|
229
|
-
paused: false,
|
|
230
|
-
context: updateStateContext(state.context, context),
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
return validate(rollup);
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const migrateContract = (state, to, context) => {
|
|
237
|
-
if (state.closed) {
|
|
238
|
-
throw new Error('INVALID_MIGRATE_ATTEMPT', 'rollup is closed');
|
|
239
|
-
}
|
|
240
|
-
if (state.paused === false) {
|
|
241
|
-
throw new Error('INVALID_MIGRATE_ATTEMPT', 'rollup not paused');
|
|
242
|
-
}
|
|
243
|
-
if (state.contractAddress === to) {
|
|
244
|
-
throw new Error('INVALID_MIGRATE_ATTEMPT', 'can not migrate contract to self');
|
|
245
|
-
}
|
|
246
|
-
if (state.migrateHistory.includes(to)) {
|
|
247
|
-
throw new Error('INVALID_MIGRATE_ATTEMPT', 'can not migrate contract to history contracts');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const rollup = {
|
|
251
|
-
...state,
|
|
252
|
-
contractAddress: to,
|
|
253
|
-
migrateHistory: [...state.migrateHistory, state.contractAddress],
|
|
254
|
-
context: updateStateContext(state.context, context),
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
return validate(rollup);
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const migrateVault = (state, to, context) => {
|
|
261
|
-
if (state.closed) {
|
|
262
|
-
throw new Error('INVALID_MIGRATE_ATTEMPT', 'rollup is closed');
|
|
263
|
-
}
|
|
264
|
-
if (state.paused === false) {
|
|
265
|
-
throw new Error('INVALID_MIGRATE_ATTEMPT', 'rollup not paused');
|
|
266
|
-
}
|
|
267
|
-
if (state.vaultAddress === to) {
|
|
268
|
-
throw new Error('INVALID_MIGRATE_ATTEMPT', 'can not migrate vault to self');
|
|
269
|
-
}
|
|
270
|
-
if (state.vaultHistory.includes(to)) {
|
|
271
|
-
throw new Error('INVALID_MIGRATE_ATTEMPT', 'can not migrate vault to history vaults');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const rollup = {
|
|
275
|
-
...state,
|
|
276
|
-
vaultAddress: to,
|
|
277
|
-
vaultHistory: [...state.vaultHistory, state.vaultAddress],
|
|
278
|
-
context: updateStateContext(state.context, context),
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
return validate(rollup);
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const validate = (state) => {
|
|
285
|
-
const { value, error } = schema.validate(state);
|
|
286
|
-
if (error) {
|
|
287
|
-
throw new Error('INVALID_ROLLUP_PROPS', error.details.map((x) => x.message).join(', '));
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (!value.data) {
|
|
291
|
-
value.data = null;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return value;
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
module.exports = { create, update, pause, close, resume, migrateContract, migrateVault, validate, schema };
|