@dashevo/wasm-dpp 3.0.1 → 3.1.0-dev.3
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/Cargo.toml +5 -12
- package/babel.config.json +1 -1
- package/dist/dpp.js +24 -10
- package/dist/errors/patchConsensusErrors.js +1 -2
- package/dist/identifier/Identifier.d.ts +2 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +25 -11
- package/dist/utils/extend.js +1 -2
- package/dist/wasm/wasm_dpp.d.ts +4859 -4003
- package/dist/wasm/wasm_dpp.js +1670 -1592
- package/dist/wasm/wasm_dpp_bg.js +1 -1
- package/eslint.config.mjs +30 -0
- package/lib/index.ts +0 -1
- package/lib/wasm/wasm_dpp.d.ts +4859 -4003
- package/package.json +8 -10
- package/scripts/build-wasm.sh +4 -2
- package/src/data_contract/data_contract.rs +1 -1
- package/src/data_contract/state_transition/data_contract_create_transition/mod.rs +1 -0
- package/src/data_contract/state_transition/data_contract_update_transition/mod.rs +1 -0
- package/src/document/extended_document.rs +1 -1
- package/src/document/state_transition/batch_transition/mod.rs +1 -0
- package/src/errors/consensus/basic/identity/identity_asset_lock_transaction_too_many_inputs_error.rs +41 -0
- package/src/errors/consensus/basic/identity/mod.rs +2 -0
- package/src/errors/consensus/consensus_error.rs +44 -3
- package/src/errors/from.rs +1 -1
- package/src/errors/generic_consensus_error.rs +1 -1
- package/src/identity/state_transition/identity_create_transition/transition.rs +1 -0
- package/src/identity/state_transition/identity_credit_transfer_transition/transition.rs +1 -0
- package/src/identity/state_transition/identity_credit_withdrawal_transition/transition.rs +1 -0
- package/src/identity/state_transition/identity_topup_transition/transition.rs +1 -0
- package/src/identity/state_transition/identity_update_transition/transition.rs +1 -0
- package/src/identity/state_transition/transition_types.rs +1 -0
- package/src/state_transition/state_transition_factory.rs +7 -0
- package/src/voting/state_transition/masternode_vote_transition/mod.rs +6 -4
- package/test/integration/dataContract/validation/validateDataContractFactory.spec.js +2 -2
- package/test/integration/document/DocumentFacade.spec.js +1 -1
- package/test/unit/dataContract/DataContract.spec.js +6 -6
- package/test/unit/identity/Identity.spec.js +3 -3
- package/test/unit/identity/IdentityPublicKey.spec.js +2 -2
- package/tsconfig.json +1 -1
- package/.eslintignore +0 -2
- package/.eslintrc +0 -18
- package/lib/test/.eslintrc +0 -9
- package/test/.eslintrc +0 -12
- package/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/state/validateIdentityUpdateTransitionStateFactory.spec.js +0 -253
- package/test/integration/identity/stateTransition/IdentityUpdateTransition/validation/state/validatePublicKeysState.spec.js +0 -100
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
const identitySchema = require('../../../../../../../../rs-dpp/src/schema/identity/v0/identity.json');
|
|
2
|
-
const createStateRepositoryMock = require('../../../../../../../lib/test/mocks/createStateRepositoryMock');
|
|
3
|
-
const getIdentityFixture = require('../../../../../../../lib/test/fixtures/getIdentityFixture');
|
|
4
|
-
const getIdentityUpdateTransitionFixture = require('../../../../../../../lib/test/fixtures/getIdentityUpdateTransitionFixture');
|
|
5
|
-
|
|
6
|
-
const { default: loadWasmDpp } = require('../../../../../../..');
|
|
7
|
-
const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError');
|
|
8
|
-
const generateRandomIdentifierAsync = require('../../../../../../../lib/test/utils/generateRandomIdentifierAsync');
|
|
9
|
-
const getBlsAdapterMock = require('../../../../../../../lib/test/mocks/getBlsAdapterMock');
|
|
10
|
-
|
|
11
|
-
describe.skip('validateIdentityUpdateTransitionStateFactory', () => {
|
|
12
|
-
let validateIdentityUpdateTransitionState;
|
|
13
|
-
let stateRepositoryMock;
|
|
14
|
-
let stateTransition;
|
|
15
|
-
let identity;
|
|
16
|
-
let rawIdentity;
|
|
17
|
-
let blockTime;
|
|
18
|
-
let executionContext;
|
|
19
|
-
|
|
20
|
-
let Identity;
|
|
21
|
-
let IdentityPublicKey;
|
|
22
|
-
let InvalidIdentityRevisionError;
|
|
23
|
-
let IdentityPublicKeyIsReadOnlyError;
|
|
24
|
-
let IdentityPublicKeyIsDisabledError;
|
|
25
|
-
let InvalidIdentityPublicKeyIdError;
|
|
26
|
-
let MissingMasterPublicKeyError;
|
|
27
|
-
let MaxIdentityPublicKeyLimitReachedError;
|
|
28
|
-
let StateTransitionExecutionContext;
|
|
29
|
-
let IdentityUpdateTransitionStateValidator;
|
|
30
|
-
|
|
31
|
-
before(async () => {
|
|
32
|
-
({
|
|
33
|
-
Identity,
|
|
34
|
-
IdentityPublicKey,
|
|
35
|
-
InvalidIdentityRevisionError,
|
|
36
|
-
IdentityPublicKeyIsReadOnlyError,
|
|
37
|
-
IdentityPublicKeyIsDisabledError,
|
|
38
|
-
InvalidIdentityPublicKeyIdError,
|
|
39
|
-
MaxIdentityPublicKeyLimitReachedError,
|
|
40
|
-
StateTransitionExecutionContext,
|
|
41
|
-
IdentityUpdateTransitionStateValidator,
|
|
42
|
-
MissingMasterPublicKeyError,
|
|
43
|
-
} = await loadWasmDpp());
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
beforeEach(async function beforeEach() {
|
|
47
|
-
rawIdentity = (await getIdentityFixture()).toObject();
|
|
48
|
-
// Patch identity id to be acceptable by wasm-dpp
|
|
49
|
-
rawIdentity.id = await generateRandomIdentifierAsync();
|
|
50
|
-
identity = new Identity(rawIdentity);
|
|
51
|
-
|
|
52
|
-
stateRepositoryMock = createStateRepositoryMock(this.sinon);
|
|
53
|
-
stateRepositoryMock.fetchIdentity.resolves(identity);
|
|
54
|
-
|
|
55
|
-
blockTime = Date.now();
|
|
56
|
-
const blsAdapter = await getBlsAdapterMock();
|
|
57
|
-
|
|
58
|
-
stateRepositoryMock.fetchLatestPlatformBlockTime = this.sinon.stub();
|
|
59
|
-
stateRepositoryMock.fetchLatestPlatformBlockTime.resolves(blockTime);
|
|
60
|
-
|
|
61
|
-
executionContext = new StateTransitionExecutionContext();
|
|
62
|
-
const validator = new IdentityUpdateTransitionStateValidator(stateRepositoryMock, blsAdapter);
|
|
63
|
-
validateIdentityUpdateTransitionState = (st) => validator.validate(st, executionContext);
|
|
64
|
-
|
|
65
|
-
stateTransition = await getIdentityUpdateTransitionFixture();
|
|
66
|
-
|
|
67
|
-
stateTransition.setRevision(identity.getRevision() + 1);
|
|
68
|
-
stateTransition.setPublicKeyIdsToDisable(undefined);
|
|
69
|
-
|
|
70
|
-
const privateKey = '9b67f852093bc61cea0eeca38599dbfba0de28574d2ed9b99d10d33dc1bde7b2';
|
|
71
|
-
|
|
72
|
-
await stateTransition.signByPrivateKey(Buffer.from(privateKey, 'hex'), IdentityPublicKey.TYPES.ECDSA_SECP256K1);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should return InvalidIdentityRevisionError if new revision is not incremented by 1', async () => {
|
|
76
|
-
stateTransition.setRevision(rawIdentity.revision + 2);
|
|
77
|
-
|
|
78
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
79
|
-
|
|
80
|
-
await expectValidationError(result, InvalidIdentityRevisionError);
|
|
81
|
-
|
|
82
|
-
const [error] = result.getErrors();
|
|
83
|
-
expect(error.getIdentityId()).to.deep.equal(stateTransition.getIdentityId().toBuffer());
|
|
84
|
-
expect(error.getCurrentRevision()).to.equal(rawIdentity.revision);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should return IdentityPublicKeyIsReadOnlyError if disabling public key is readOnly', async () => {
|
|
88
|
-
const keys = identity.getPublicKeys();
|
|
89
|
-
keys[0].setReadOnly(true);
|
|
90
|
-
identity.setPublicKeys(keys);
|
|
91
|
-
|
|
92
|
-
stateTransition.setPublicKeyIdsToDisable([0]);
|
|
93
|
-
|
|
94
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
95
|
-
|
|
96
|
-
await expectValidationError(result, IdentityPublicKeyIsReadOnlyError);
|
|
97
|
-
|
|
98
|
-
const [error] = result.getErrors();
|
|
99
|
-
expect(error.getPublicKeyIndex()).to.equal(0);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should return IdentityPublicKeyIsDisabledError if disabling public key is already disabled', async () => {
|
|
103
|
-
const keys = identity.getPublicKeys();
|
|
104
|
-
keys[0].setDisabledAt(new Date());
|
|
105
|
-
identity.setPublicKeys(keys);
|
|
106
|
-
stateTransition.setPublicKeyIdsToDisable([0]);
|
|
107
|
-
|
|
108
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
109
|
-
|
|
110
|
-
await expectValidationError(result, IdentityPublicKeyIsDisabledError);
|
|
111
|
-
|
|
112
|
-
const [error] = result.getErrors();
|
|
113
|
-
expect(error.getPublicKeyIndex()).to.equal(0);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should throw InvalidIdentityPublicKeyIdError if identity does not contain public key with disabling ID', async () => {
|
|
117
|
-
stateTransition.setPublicKeyIdsToDisable([3]);
|
|
118
|
-
|
|
119
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
120
|
-
|
|
121
|
-
await expectValidationError(result, InvalidIdentityPublicKeyIdError);
|
|
122
|
-
|
|
123
|
-
const [error] = result.getErrors();
|
|
124
|
-
expect(error.getId()).to.equal(3);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should pass when disabling public key', async function it() {
|
|
128
|
-
stateTransition.setPublicKeyIdsToDisable([1]);
|
|
129
|
-
stateTransition.setPublicKeysToAdd(undefined);
|
|
130
|
-
|
|
131
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
132
|
-
|
|
133
|
-
expect(result.isValid()).to.be.true();
|
|
134
|
-
|
|
135
|
-
const { match } = this.sinon;
|
|
136
|
-
expect(stateRepositoryMock.fetchIdentity)
|
|
137
|
-
.to.be.calledOnceWithExactly(
|
|
138
|
-
match((val) => val.toBuffer().equals(stateTransition.getIdentityId().toBuffer())),
|
|
139
|
-
match.instanceOf(StateTransitionExecutionContext),
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
expect(stateRepositoryMock.fetchLatestPlatformBlockTime)
|
|
143
|
-
.to.be.calledOnce();
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should pass when adding public key', async function it() {
|
|
147
|
-
stateTransition.setPublicKeyIdsToDisable(undefined);
|
|
148
|
-
|
|
149
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
150
|
-
|
|
151
|
-
expect(result.isValid()).to.be.true();
|
|
152
|
-
|
|
153
|
-
const { match } = this.sinon;
|
|
154
|
-
expect(stateRepositoryMock.fetchIdentity)
|
|
155
|
-
.to.be.calledOnceWithExactly(
|
|
156
|
-
match((val) => val.toBuffer().equals(stateTransition.getIdentityId().toBuffer())),
|
|
157
|
-
match.instanceOf(StateTransitionExecutionContext),
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
expect(stateRepositoryMock.fetchLatestPlatformBlockTime)
|
|
161
|
-
.to.not.be.called();
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('should pass when both adding and disabling public keys', async function it() {
|
|
165
|
-
stateTransition.setPublicKeyIdsToDisable([1]);
|
|
166
|
-
|
|
167
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
168
|
-
|
|
169
|
-
expect(result.isValid()).to.be.true();
|
|
170
|
-
|
|
171
|
-
const { match } = this.sinon;
|
|
172
|
-
expect(stateRepositoryMock.fetchIdentity)
|
|
173
|
-
.to.be.calledOnceWithExactly(
|
|
174
|
-
match((val) => val.toBuffer().equals(stateTransition.getIdentityId().toBuffer())),
|
|
175
|
-
match.instanceOf(StateTransitionExecutionContext),
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
expect(stateRepositoryMock.fetchLatestPlatformBlockTime)
|
|
179
|
-
.to.be.calledOnce();
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should validate purpose and security level', async () => {
|
|
183
|
-
const identityKeys = identity.getPublicKeys();
|
|
184
|
-
identityKeys.forEach((key) => key.setSecurityLevel(1));
|
|
185
|
-
identity.setPublicKeys(identityKeys);
|
|
186
|
-
|
|
187
|
-
const keysToAdd = stateTransition.getPublicKeysToAdd();
|
|
188
|
-
keysToAdd.forEach((key) => key.setSecurityLevel(1));
|
|
189
|
-
stateTransition.setPublicKeysToAdd(keysToAdd);
|
|
190
|
-
|
|
191
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
192
|
-
|
|
193
|
-
await expectValidationError(result, MissingMasterPublicKeyError);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('should validate public keys to add', async () => {
|
|
197
|
-
// Reach max allowed public keys to fail validation
|
|
198
|
-
const { maxItems } = identitySchema.properties.publicKeys;
|
|
199
|
-
|
|
200
|
-
const firstKey = identity.getPublicKeys()[0].toObject();
|
|
201
|
-
const keys = Array.from({ length: maxItems + 1 })
|
|
202
|
-
.map((_, index) => new IdentityPublicKey({ ...firstKey, id: index }));
|
|
203
|
-
identity.setPublicKeys(keys);
|
|
204
|
-
|
|
205
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
206
|
-
|
|
207
|
-
await expectValidationError(result, MaxIdentityPublicKeyLimitReachedError);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// TODO: remove?
|
|
211
|
-
// Skipped, because two tests above are seem to be enough
|
|
212
|
-
it.skip('should validate resulting identity public keys', async () => {
|
|
213
|
-
// const publicKeysError = new SomeConsensusError('test');
|
|
214
|
-
//
|
|
215
|
-
// validatePublicKeysMock.returns(new ValidationResult([publicKeysError]));
|
|
216
|
-
//
|
|
217
|
-
// const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
218
|
-
//
|
|
219
|
-
// expectValidationError(result, SomeConsensusError);
|
|
220
|
-
//
|
|
221
|
-
// expect(validatePublicKeysMock).to.be.calledOnce();
|
|
222
|
-
//
|
|
223
|
-
// const publicKeys = [...identity.getPublicKeys(), ...stateTransition.getPublicKeysToAdd()];
|
|
224
|
-
//
|
|
225
|
-
// expect(validatePublicKeysMock).to.be.calledWithExactly(
|
|
226
|
-
// publicKeys.map((pk) => pk.toObject()),
|
|
227
|
-
// );
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('should return valid result on dry run', async function it() {
|
|
231
|
-
stateTransition.setPublicKeyIdsToDisable([3]);
|
|
232
|
-
|
|
233
|
-
// Make code that executes after dry run check to fail
|
|
234
|
-
stateRepositoryMock.fetchLatestPlatformBlockTime.resolves({});
|
|
235
|
-
|
|
236
|
-
executionContext.enableDryRun();
|
|
237
|
-
const result = await validateIdentityUpdateTransitionState(stateTransition);
|
|
238
|
-
|
|
239
|
-
executionContext.disableDryRun();
|
|
240
|
-
|
|
241
|
-
expect(result.isValid()).to.be.true();
|
|
242
|
-
|
|
243
|
-
const { match } = this.sinon;
|
|
244
|
-
expect(stateRepositoryMock.fetchIdentity)
|
|
245
|
-
.to.be.calledOnceWithExactly(
|
|
246
|
-
match((val) => val.toBuffer().equals(stateTransition.getIdentityId().toBuffer())),
|
|
247
|
-
match.instanceOf(StateTransitionExecutionContext),
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
expect(stateRepositoryMock.fetchLatestPlatformBlockTime)
|
|
251
|
-
.to.not.be.called();
|
|
252
|
-
});
|
|
253
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
const identitySchema = require('../../../../../../../../rs-dpp/src/schema/identity/v0/identity.json');
|
|
2
|
-
const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError');
|
|
3
|
-
|
|
4
|
-
const { default: loadWasmDpp } = require('../../../../../../../dist');
|
|
5
|
-
|
|
6
|
-
describe.skip('validatePublicKeysState', () => {
|
|
7
|
-
let IdentityUpdatePublicKeysValidator;
|
|
8
|
-
let DuplicatedIdentityPublicKeyIdStateError;
|
|
9
|
-
let DuplicatedIdentityPublicKeyStateError;
|
|
10
|
-
let IdentityPublicKey;
|
|
11
|
-
let MaxIdentityPublicKeyLimitReachedError;
|
|
12
|
-
|
|
13
|
-
let validatePublicKeys;
|
|
14
|
-
let rawPublicKeys;
|
|
15
|
-
|
|
16
|
-
before(async () => {
|
|
17
|
-
({
|
|
18
|
-
IdentityPublicKey,
|
|
19
|
-
IdentityUpdatePublicKeysValidator,
|
|
20
|
-
DuplicatedIdentityPublicKeyStateError,
|
|
21
|
-
DuplicatedIdentityPublicKeyIdStateError,
|
|
22
|
-
MaxIdentityPublicKeyLimitReachedError,
|
|
23
|
-
} = await loadWasmDpp());
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
rawPublicKeys = [
|
|
28
|
-
{
|
|
29
|
-
id: 0,
|
|
30
|
-
type: IdentityPublicKey.TYPES.ECDSA_SECP256K1,
|
|
31
|
-
data: Buffer.from('AuryIuMtRrl/VviQuyLD1l4nmxi9ogPzC9LT7tdpo0di', 'base64'),
|
|
32
|
-
purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION,
|
|
33
|
-
securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER,
|
|
34
|
-
signature: Buffer.alloc(0),
|
|
35
|
-
readOnly: false,
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
id: 1,
|
|
39
|
-
type: IdentityPublicKey.TYPES.ECDSA_SECP256K1,
|
|
40
|
-
data: Buffer.from('A8AK95PYMVX5VQKzOhcVQRCUbc9pyg3RiL7jttEMDU+L', 'base64'),
|
|
41
|
-
purpose: IdentityPublicKey.PURPOSES.ENCRYPTION,
|
|
42
|
-
securityLevel: IdentityPublicKey.SECURITY_LEVELS.MEDIUM,
|
|
43
|
-
signature: Buffer.alloc(0),
|
|
44
|
-
readOnly: false,
|
|
45
|
-
},
|
|
46
|
-
];
|
|
47
|
-
|
|
48
|
-
const validator = new IdentityUpdatePublicKeysValidator();
|
|
49
|
-
validatePublicKeys = (keys) => validator.validate(keys);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should return invalid result if there are duplicate key ids', async () => {
|
|
53
|
-
rawPublicKeys[1].id = rawPublicKeys[0].id;
|
|
54
|
-
|
|
55
|
-
const result = validatePublicKeys(rawPublicKeys);
|
|
56
|
-
|
|
57
|
-
await expectValidationError(result, DuplicatedIdentityPublicKeyIdStateError);
|
|
58
|
-
|
|
59
|
-
const [error] = result.getErrors();
|
|
60
|
-
|
|
61
|
-
expect(error.getCode()).to.equal(4022);
|
|
62
|
-
expect(error.getDuplicatedIds()).to.deep.equal([rawPublicKeys[1].id]);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should return invalid result if there are duplicate keys', async () => {
|
|
66
|
-
rawPublicKeys[1].data = rawPublicKeys[0].data;
|
|
67
|
-
|
|
68
|
-
const result = validatePublicKeys(rawPublicKeys);
|
|
69
|
-
|
|
70
|
-
await expectValidationError(result, DuplicatedIdentityPublicKeyStateError);
|
|
71
|
-
|
|
72
|
-
const [error] = result.getErrors();
|
|
73
|
-
|
|
74
|
-
expect(error.getCode()).to.equal(4021);
|
|
75
|
-
expect(error.getDuplicatedPublicKeysIds()).to.deep.equal([rawPublicKeys[1].id]);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should pass valid public keys', () => {
|
|
79
|
-
const result = validatePublicKeys(rawPublicKeys);
|
|
80
|
-
|
|
81
|
-
expect(result.isValid()).to.be.true();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should return invalid result if number of public keys is bigger than 32', async () => {
|
|
85
|
-
const { maxItems } = identitySchema.properties.publicKeys;
|
|
86
|
-
const numToAdd = maxItems - rawPublicKeys.length;
|
|
87
|
-
|
|
88
|
-
for (let i = 0; i <= numToAdd; ++i) {
|
|
89
|
-
rawPublicKeys.push(rawPublicKeys[0]);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const result = validatePublicKeys(rawPublicKeys);
|
|
93
|
-
|
|
94
|
-
await expectValidationError(result, MaxIdentityPublicKeyLimitReachedError);
|
|
95
|
-
|
|
96
|
-
const [error] = result.getErrors();
|
|
97
|
-
expect(error.getCode()).to.equal(4020);
|
|
98
|
-
expect(error.getMaxItems()).to.equal(maxItems);
|
|
99
|
-
});
|
|
100
|
-
});
|