@ocap/state 1.6.5 → 1.6.10
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/contexts/state.js +19 -0
- package/lib/index.js +50 -1
- package/lib/states/account.js +62 -28
- package/lib/states/asset.js +78 -16
- package/lib/states/blacklist.js +2 -1
- package/lib/states/chain.js +36 -0
- package/lib/states/delegation.js +1 -5
- package/lib/states/evidence.js +35 -0
- package/lib/states/factory.js +62 -0
- package/lib/states/rollup-block.js +81 -0
- package/lib/states/rollup.js +238 -0
- package/lib/states/stake.js +74 -0
- package/lib/states/token.js +37 -0
- package/lib/states/tx.js +398 -16
- package/package.json +12 -6
- package/lib/states/contexts/stake.js +0 -13
- package/lib/states/contexts/state.js +0 -12
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// StateContext utils for account/asset/delegate etc.
|
|
2
|
+
|
|
3
|
+
const create = ({ txHash, txTime }) => ({
|
|
4
|
+
genesisTime: txTime || '',
|
|
5
|
+
genesisTx: txHash || '',
|
|
6
|
+
renaissanceTime: txTime || '',
|
|
7
|
+
renaissanceTx: txHash || '',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const update = (context, { txHash, txTime }) => ({
|
|
11
|
+
...context,
|
|
12
|
+
// NOTE: for historical reasons, some account state does not have genesisTime
|
|
13
|
+
genesisTime: context.genesisTime || '2020-01-15T00:00:00.000Z',
|
|
14
|
+
genesisTx: context.genesisTx || '',
|
|
15
|
+
renaissanceTime: txTime || '',
|
|
16
|
+
renaissanceTx: txHash || '',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
module.exports = { create, update };
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,57 @@
|
|
|
1
|
+
const chain = require('./states/chain');
|
|
1
2
|
const account = require('./states/account');
|
|
2
3
|
const asset = require('./states/asset');
|
|
3
4
|
const delegation = require('./states/delegation');
|
|
4
5
|
const tx = require('./states/tx');
|
|
6
|
+
const factory = require('./states/factory');
|
|
7
|
+
const token = require('./states/token');
|
|
8
|
+
const stake = require('./states/stake');
|
|
9
|
+
const rollup = require('./states/rollup');
|
|
10
|
+
const rollupBlock = require('./states/rollup-block');
|
|
11
|
+
const evidence = require('./states/evidence');
|
|
5
12
|
|
|
6
13
|
const Blacklist = require('./states/blacklist');
|
|
7
14
|
|
|
8
|
-
module.exports = {
|
|
15
|
+
module.exports = {
|
|
16
|
+
chain,
|
|
17
|
+
account,
|
|
18
|
+
asset,
|
|
19
|
+
factory,
|
|
20
|
+
delegation,
|
|
21
|
+
tx,
|
|
22
|
+
token,
|
|
23
|
+
stake,
|
|
24
|
+
rollup,
|
|
25
|
+
rollupBlock,
|
|
26
|
+
evidence,
|
|
27
|
+
|
|
28
|
+
// indexes of the statedb to speed up reading
|
|
29
|
+
indexes: [
|
|
30
|
+
'account',
|
|
31
|
+
'asset',
|
|
32
|
+
'delegation',
|
|
33
|
+
'tx',
|
|
34
|
+
'factory',
|
|
35
|
+
'token',
|
|
36
|
+
'stake',
|
|
37
|
+
'rollup',
|
|
38
|
+
'rollupBlock',
|
|
39
|
+
'rollupValidator',
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
tables: [
|
|
43
|
+
'chain',
|
|
44
|
+
'account',
|
|
45
|
+
'asset',
|
|
46
|
+
'delegation',
|
|
47
|
+
'tx',
|
|
48
|
+
'factory',
|
|
49
|
+
'token',
|
|
50
|
+
'stake',
|
|
51
|
+
'rollup',
|
|
52
|
+
'rollupBlock',
|
|
53
|
+
'evidence', // a simple key-value store
|
|
54
|
+
],
|
|
55
|
+
|
|
56
|
+
Blacklist,
|
|
57
|
+
};
|
package/lib/states/account.js
CHANGED
|
@@ -1,59 +1,93 @@
|
|
|
1
1
|
const pick = require('lodash/pick');
|
|
2
2
|
const uniq = require('lodash/uniq');
|
|
3
3
|
const flatten = require('lodash/flatten');
|
|
4
|
-
const
|
|
4
|
+
const Error = require('@ocap/util/lib/error');
|
|
5
|
+
const { isFromPublicKey } = require('@arcblock/did');
|
|
6
|
+
const { isEthereumDid, toChecksumAddress } = require('@arcblock/did/lib/type');
|
|
7
|
+
const { toBase58, BN } = require('@ocap/util');
|
|
5
8
|
|
|
6
|
-
const
|
|
9
|
+
const Joi = require('@arcblock/validator');
|
|
10
|
+
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
11
|
+
|
|
12
|
+
const schema = Joi.object({
|
|
13
|
+
address: Joi.DID().required(),
|
|
14
|
+
pk: Joi.string().allow(''),
|
|
15
|
+
issuer: Joi.DID().allow(''),
|
|
16
|
+
moniker: Joi.string().trim().min(2).max(40).allow(''),
|
|
17
|
+
nonce: Joi.number().min(0).default(0),
|
|
18
|
+
tokens: Joi.object().pattern(Joi.DID().role('ROLE_TOKEN'), Joi.BN().min(0)).default({}),
|
|
19
|
+
migratedTo: Joi.array().items(Joi.DID()).default([]),
|
|
20
|
+
migratedFrom: Joi.array().items(Joi.DID()).default([]),
|
|
21
|
+
context: Joi.schemas.context,
|
|
22
|
+
data: Joi.any().optional(),
|
|
23
|
+
}).options({ stripUnknown: true, noDefaults: false });
|
|
7
24
|
|
|
8
25
|
const create = (attrs, context) => {
|
|
9
26
|
const account = {
|
|
10
|
-
|
|
11
|
-
gasBalance: '0',
|
|
12
|
-
nonce: 2,
|
|
13
|
-
numTxs: 1,
|
|
14
|
-
numAssets: 0,
|
|
27
|
+
nonce: 0,
|
|
15
28
|
migratedTo: [],
|
|
16
29
|
migratedFrom: [],
|
|
17
|
-
|
|
30
|
+
tokens: {},
|
|
18
31
|
context: createStateContext(context),
|
|
19
|
-
|
|
20
|
-
...pick(attrs, ['address', 'pk', 'issuer', 'balance', 'moniker', 'data', 'migratedFrom', 'poke']),
|
|
32
|
+
...pick(attrs, ['address', 'pk', 'issuer', 'moniker', 'data', 'nonce', 'migratedFrom', 'tokens']),
|
|
21
33
|
};
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
account.pk = toBase64(toUint8Array(account.pk));
|
|
35
|
+
if (!account.moniker) {
|
|
36
|
+
account.moniker = [account.address.slice(0, 6), account.address.slice(-5)].join('-');
|
|
26
37
|
}
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
account.data = null;
|
|
30
|
-
}
|
|
39
|
+
account.address = ensureChecksumAddress(account.address);
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return account;
|
|
41
|
+
return validate(account);
|
|
35
42
|
};
|
|
36
43
|
|
|
37
44
|
const update = (state, attrs, context) => {
|
|
45
|
+
if (attrs.nonce && new BN(attrs.nonce).eq(new BN(state.nonce))) {
|
|
46
|
+
throw new Error('INVALID_NONCE', 'nonce must be greater in newer transactions');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ensure we are updating the correct pk
|
|
50
|
+
if (attrs.pk && isFromPublicKey(state.address, attrs.pk) === false) {
|
|
51
|
+
delete attrs.pk;
|
|
52
|
+
}
|
|
53
|
+
|
|
38
54
|
const account = {
|
|
39
55
|
...state,
|
|
40
|
-
|
|
41
|
-
numTxs: state.numTxs + 1,
|
|
42
|
-
...pick(attrs, ['balance', 'moniker', 'data', 'migratedTo', 'nonce', 'poke', 'numAssets']),
|
|
56
|
+
...pick(attrs, ['moniker', 'data', 'migratedTo', 'nonce', 'tokens', 'pk']),
|
|
43
57
|
migratedTo: uniq(flatten(attrs.migratedTo ? [attrs.migratedTo].concat(state.migratedTo) : state.migratedTo)),
|
|
44
58
|
context: updateStateContext(state.context, context),
|
|
45
59
|
};
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
return validate(account);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const updateOrCreate = (state, attrs, context) => {
|
|
65
|
+
if (state) {
|
|
66
|
+
return update(state, attrs, context);
|
|
49
67
|
}
|
|
50
68
|
|
|
51
|
-
|
|
69
|
+
return create(attrs, context);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const validate = (state) => {
|
|
73
|
+
// ensure we have correct pk
|
|
74
|
+
if (state.pk && typeof state.pk !== 'string') {
|
|
75
|
+
state.pk = toBase58(state.pk);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const { value, error } = schema.validate(state);
|
|
79
|
+
if (error) {
|
|
80
|
+
throw new Error('INVALID_ACCOUNT', `Invalid account: ${error.details.map((x) => x.message).join(', ')}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!value.data) {
|
|
84
|
+
value.data = null;
|
|
85
|
+
}
|
|
52
86
|
|
|
53
|
-
return
|
|
87
|
+
return value;
|
|
54
88
|
};
|
|
55
89
|
|
|
56
|
-
const getRelatedAddresses = (state) => [state.address].concat(state.migratedFrom).filter(Boolean);
|
|
57
90
|
const isMigrated = (state) => (state.migratedTo || []).length > 0;
|
|
91
|
+
const ensureChecksumAddress = (address) => (isEthereumDid(address) ? toChecksumAddress(address) : address);
|
|
58
92
|
|
|
59
|
-
module.exports = { create, update,
|
|
93
|
+
module.exports = { create, update, updateOrCreate, validate, isMigrated, ensureChecksumAddress };
|
package/lib/states/asset.js
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
const pick = require('lodash/pick');
|
|
2
|
+
const Joi = require('@arcblock/validator');
|
|
3
|
+
const Error = require('@ocap/util/lib/error');
|
|
2
4
|
|
|
3
|
-
const { create: createStateContext, update: updateStateContext } = require('
|
|
5
|
+
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
6
|
+
|
|
7
|
+
const props = {
|
|
8
|
+
address: Joi.DID().role('ROLE_ASSET').required(),
|
|
9
|
+
moniker: Joi.string().min(2).max(255).required(),
|
|
10
|
+
data: Joi.any().required(),
|
|
11
|
+
readonly: Joi.boolean().default(false),
|
|
12
|
+
transferrable: Joi.boolean().default(false),
|
|
13
|
+
ttl: Joi.number().min(0).default(0),
|
|
14
|
+
parent: Joi.DID().optional().allow(''),
|
|
15
|
+
issuer: Joi.DID().optional().allow(''),
|
|
16
|
+
endpoint: Joi.object({
|
|
17
|
+
id: Joi.string().uri({ scheme: ['http', 'https'] }).required(), // prettier-ignore
|
|
18
|
+
scope: Joi.string().valid('public', 'private').default('public'),
|
|
19
|
+
}).optional(),
|
|
20
|
+
display: Joi.object({
|
|
21
|
+
type: Joi.string().valid('svg', 'url', 'uri').required(),
|
|
22
|
+
content: Joi.string()
|
|
23
|
+
.when('type', { is: 'uri', then: Joi.string().dataUri().required() })
|
|
24
|
+
.when('type', { is: 'url', then: Joi.string().uri({ scheme: ['http', 'https'] }).required() }), // prettier-ignore
|
|
25
|
+
}).optional(),
|
|
26
|
+
tags: Joi.array().items(Joi.string().min(1)).optional(),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const schema = Joi.object({
|
|
30
|
+
...props,
|
|
31
|
+
owner: Joi.DID().required(),
|
|
32
|
+
consumedTime: Joi.date().iso().raw().allow(''),
|
|
33
|
+
context: Joi.schemas.context,
|
|
34
|
+
}).options({
|
|
35
|
+
stripUnknown: true,
|
|
36
|
+
noDefaults: false,
|
|
37
|
+
});
|
|
4
38
|
|
|
5
39
|
const create = (attrs, context) => {
|
|
6
40
|
const asset = {
|
|
@@ -10,34 +44,62 @@ const create = (attrs, context) => {
|
|
|
10
44
|
readonly: false,
|
|
11
45
|
transferrable: true,
|
|
12
46
|
ttl: 0, // means unlimited
|
|
13
|
-
|
|
47
|
+
tags: [],
|
|
14
48
|
context: createStateContext(context),
|
|
15
|
-
...pick(attrs, [
|
|
49
|
+
...pick(attrs, [
|
|
50
|
+
'address',
|
|
51
|
+
'owner',
|
|
52
|
+
'issuer',
|
|
53
|
+
'moniker',
|
|
54
|
+
'parent',
|
|
55
|
+
'data',
|
|
56
|
+
'readonly',
|
|
57
|
+
'transferrable',
|
|
58
|
+
'ttl',
|
|
59
|
+
'endpoint',
|
|
60
|
+
'display',
|
|
61
|
+
'tags',
|
|
62
|
+
]),
|
|
16
63
|
};
|
|
17
64
|
|
|
18
|
-
|
|
19
|
-
asset.data = null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
context.emit('asset.create', asset, context);
|
|
23
|
-
|
|
24
|
-
return asset;
|
|
65
|
+
return validate(asset);
|
|
25
66
|
};
|
|
26
67
|
|
|
27
68
|
const update = (state, attrs, context) => {
|
|
28
69
|
const asset = {
|
|
29
70
|
...state,
|
|
30
|
-
...pick(attrs, ['moniker', 'data', 'owner']),
|
|
71
|
+
...pick(attrs, ['moniker', 'data', 'owner', 'consumedTime']),
|
|
31
72
|
context: updateStateContext(state.context, context),
|
|
32
73
|
};
|
|
33
74
|
|
|
34
|
-
if (!asset.
|
|
35
|
-
asset.
|
|
75
|
+
if (!asset.consumedTime) {
|
|
76
|
+
asset.consumedTime = '';
|
|
36
77
|
}
|
|
37
78
|
|
|
38
|
-
|
|
79
|
+
return validate(asset);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const validate = (state) => {
|
|
83
|
+
const { value, error } = schema.validate(state);
|
|
84
|
+
if (error) {
|
|
85
|
+
throw new Error('INVALID_ASSET', `Invalid asset: ${error.details.map((x) => x.message).join(', ')}`);
|
|
86
|
+
}
|
|
39
87
|
|
|
40
|
-
|
|
88
|
+
['endpoint', 'display'].forEach((key) => {
|
|
89
|
+
if (!value[key]) {
|
|
90
|
+
delete value[key];
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return value;
|
|
41
95
|
};
|
|
42
96
|
|
|
43
|
-
module.exports = {
|
|
97
|
+
module.exports = {
|
|
98
|
+
create,
|
|
99
|
+
update,
|
|
100
|
+
validate,
|
|
101
|
+
schema: Joi.object(props).options({
|
|
102
|
+
stripUnknown: true,
|
|
103
|
+
noDefaults: false,
|
|
104
|
+
}),
|
|
105
|
+
};
|
package/lib/states/blacklist.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
+
const Error = require('@ocap/util/lib/error');
|
|
2
3
|
const { BloomFilter } = require('bloom-filters');
|
|
3
4
|
|
|
4
5
|
class Blacklist {
|
|
@@ -10,7 +11,7 @@ class Blacklist {
|
|
|
10
11
|
const json = JSON.parse(fs.readFileSync(dumpPath));
|
|
11
12
|
filter = BloomFilter.fromJSON(json);
|
|
12
13
|
} catch (err) {
|
|
13
|
-
throw new Error(`Cat not read serialized blacklist from json${dumpPath}`);
|
|
14
|
+
throw new Error('INTERNAL', `Cat not read serialized blacklist from json${dumpPath}`);
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const pick = require('lodash/pick');
|
|
2
|
+
const isEqual = require('lodash/isEqual');
|
|
3
|
+
const Error = require('@ocap/util/lib/error');
|
|
4
|
+
|
|
5
|
+
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
6
|
+
|
|
7
|
+
const create = (attrs) => {
|
|
8
|
+
const context = { txTime: new Date().toISOString() };
|
|
9
|
+
const chain = {
|
|
10
|
+
context: createStateContext(context),
|
|
11
|
+
...pick(attrs, ['address', 'chainId', 'version', 'transaction', 'moderator', 'accounts', 'token', 'vaults']),
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
return chain;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const update = (state, updates) => {
|
|
18
|
+
const immutableAttrs = ['chainId', 'token', 'moderator'];
|
|
19
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
20
|
+
for (const attr of immutableAttrs) {
|
|
21
|
+
if (updates[attr] && isEqual(updates[attr], state[attr]) === false) {
|
|
22
|
+
throw new Error('FORBIDDEN', `Cannot update ${attr} field on chain state`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const context = { txTime: new Date().toISOString() };
|
|
27
|
+
const chain = {
|
|
28
|
+
...state,
|
|
29
|
+
...pick(updates, ['version', 'transaction', 'accounts', 'vaults']),
|
|
30
|
+
context: updateStateContext(state.context, context),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return chain;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
module.exports = { create, update };
|
package/lib/states/delegation.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const pick = require('lodash/pick');
|
|
2
2
|
|
|
3
|
-
const { create: createStateContext, update: updateStateContext } = require('
|
|
3
|
+
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
4
4
|
|
|
5
5
|
const create = (attrs, context) => {
|
|
6
6
|
const delegation = {
|
|
@@ -12,8 +12,6 @@ const create = (attrs, context) => {
|
|
|
12
12
|
delegation.data = null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
context.emit('delegation.create', delegation, context);
|
|
16
|
-
|
|
17
15
|
return delegation;
|
|
18
16
|
};
|
|
19
17
|
|
|
@@ -28,8 +26,6 @@ const update = (state, attrs, context) => {
|
|
|
28
26
|
delegation.data = null;
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
context.emit('delegation.update', delegation, context);
|
|
32
|
-
|
|
33
29
|
return delegation;
|
|
34
30
|
};
|
|
35
31
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const pick = require('lodash/pick');
|
|
2
|
+
const Error = require('@ocap/util/lib/error');
|
|
3
|
+
const Joi = require('@arcblock/validator');
|
|
4
|
+
|
|
5
|
+
const { create: createStateContext } = require('../contexts/state');
|
|
6
|
+
|
|
7
|
+
const schema = Joi.object({
|
|
8
|
+
hash: Joi.string().trim().required(),
|
|
9
|
+
context: Joi.schemas.context,
|
|
10
|
+
data: Joi.any().optional(),
|
|
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 };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const pick = require('lodash/pick');
|
|
2
|
+
const { compile, merge, getQuota } = require('@ocap/contract');
|
|
3
|
+
|
|
4
|
+
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
5
|
+
|
|
6
|
+
const compileHook = (hook, quota) => {
|
|
7
|
+
if (hook.type === 'contract') {
|
|
8
|
+
hook.compiled = merge(compile(hook.hook, quota));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return hook;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const create = (attrs, context) => {
|
|
15
|
+
const factory = {
|
|
16
|
+
address: '',
|
|
17
|
+
owner: '',
|
|
18
|
+
name: '',
|
|
19
|
+
description: '',
|
|
20
|
+
settlement: 'instant',
|
|
21
|
+
limit: 0,
|
|
22
|
+
numMinted: 0,
|
|
23
|
+
lastSettlement: '',
|
|
24
|
+
balance: '0',
|
|
25
|
+
tokens: {},
|
|
26
|
+
trustedIssuers: [],
|
|
27
|
+
stake: null,
|
|
28
|
+
context: createStateContext(context),
|
|
29
|
+
...pick(attrs, [
|
|
30
|
+
'address',
|
|
31
|
+
'owner',
|
|
32
|
+
'name',
|
|
33
|
+
'description',
|
|
34
|
+
'settlement',
|
|
35
|
+
'limit',
|
|
36
|
+
'tokens',
|
|
37
|
+
'trustedIssuers',
|
|
38
|
+
'input',
|
|
39
|
+
'output',
|
|
40
|
+
'display',
|
|
41
|
+
'hooks',
|
|
42
|
+
'data',
|
|
43
|
+
]),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (!factory.data) {
|
|
47
|
+
factory.data = null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const quota = getQuota(factory.input);
|
|
51
|
+
factory.hooks = (factory.hooks || []).map((x) => compileHook(x, quota));
|
|
52
|
+
|
|
53
|
+
return factory;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const update = (state, attrs, context) => ({
|
|
57
|
+
...state,
|
|
58
|
+
...pick(attrs, ['numMinted', 'tokens']),
|
|
59
|
+
context: updateStateContext(state.context, context),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
module.exports = { create, update };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const pick = require('lodash/pick');
|
|
2
|
+
const Error = require('@ocap/util/lib/error');
|
|
3
|
+
const Joi = require('@arcblock/validator');
|
|
4
|
+
|
|
5
|
+
const { create: createStateContext, update: updateStateContext } = require('../contexts/state');
|
|
6
|
+
|
|
7
|
+
const schema = Joi.object({
|
|
8
|
+
hash: Joi.string().regex(Joi.patterns.txHash).required(),
|
|
9
|
+
height: Joi.number().integer().greater(0).required(),
|
|
10
|
+
merkleRoot: Joi.string().regex(Joi.patterns.txHash).required(),
|
|
11
|
+
previousHash: Joi.string().when('height', {
|
|
12
|
+
is: 1,
|
|
13
|
+
then: Joi.string().optional().allow(''),
|
|
14
|
+
otherwise: Joi.string().regex(Joi.patterns.txHash).required(),
|
|
15
|
+
}),
|
|
16
|
+
txsHash: Joi.string().regex(Joi.patterns.txHash).required(),
|
|
17
|
+
txs: Joi.array().items(Joi.string().regex(Joi.patterns.txHash).required()).min(1).unique().required(),
|
|
18
|
+
|
|
19
|
+
proposer: Joi.DID().wallet('ethereum').required(),
|
|
20
|
+
signatures: Joi.schemas.multiSig.min(1).required(),
|
|
21
|
+
|
|
22
|
+
rollup: Joi.DID().role('ROLE_ROLLUP').required(),
|
|
23
|
+
|
|
24
|
+
mintedAmount: Joi.BN().min(0).optional().default('0'),
|
|
25
|
+
burnedAmount: Joi.BN().min(0).optional().default('0'),
|
|
26
|
+
rewardAmount: Joi.BN().min(0).optional().default('0'),
|
|
27
|
+
|
|
28
|
+
minReward: Joi.BN().min(0).required(),
|
|
29
|
+
|
|
30
|
+
context: Joi.schemas.context,
|
|
31
|
+
data: Joi.any().optional(),
|
|
32
|
+
}).options({ stripUnknown: true, noDefaults: false });
|
|
33
|
+
|
|
34
|
+
const create = (attrs, context) => {
|
|
35
|
+
const block = {
|
|
36
|
+
context: createStateContext(context),
|
|
37
|
+
...pick(attrs, [
|
|
38
|
+
'hash',
|
|
39
|
+
'height',
|
|
40
|
+
'merkleRoot',
|
|
41
|
+
'previousHash',
|
|
42
|
+
'txsHash',
|
|
43
|
+
'txs',
|
|
44
|
+
'proposer',
|
|
45
|
+
'signatures',
|
|
46
|
+
'rollup',
|
|
47
|
+
'mintedAmount',
|
|
48
|
+
'burnedAmount',
|
|
49
|
+
'rewardAmount',
|
|
50
|
+
'minReward',
|
|
51
|
+
'data',
|
|
52
|
+
]),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return validate(block);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// We only support update block context
|
|
59
|
+
const update = (state, context) => {
|
|
60
|
+
const rollup = {
|
|
61
|
+
...state,
|
|
62
|
+
context: updateStateContext(state.context, context),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return validate(rollup);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const validate = (state) => {
|
|
69
|
+
const { value, error } = schema.validate(state);
|
|
70
|
+
if (error) {
|
|
71
|
+
throw new Error('INVALID_ROLLUP_BLOCK', `Invalid rollup block: ${error.details.map((x) => x.message).join(', ')}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!value.data) {
|
|
75
|
+
value.data = null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return value;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
module.exports = { create, update, validate, schema };
|