@dashevo/document-history-contract 4.1.0-beta.2

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/.mocharc.yml ADDED
@@ -0,0 +1,2 @@
1
+ require: test/bootstrap.js
2
+ recursive: true
package/Cargo.toml ADDED
@@ -0,0 +1,13 @@
1
+ [package]
2
+ name = "document-history-contract"
3
+ description = "Document history data contract schema and tools"
4
+ version.workspace = true
5
+ edition = "2021"
6
+ rust-version.workspace = true
7
+ license = "MIT"
8
+
9
+ [dependencies]
10
+ thiserror = "2.0.12"
11
+ platform-version = { path = "../rs-platform-version" }
12
+ serde_json = { version = "1.0" }
13
+ platform-value = { path = "../rs-platform-value" }
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Dash Core Group, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Document History Contract
2
+
3
+ [![Build Status](https://github.com/dashpay/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashpay/platform/actions/workflows/release.yml)
4
+
5
+ System data contract recording document transfer, purchase, and price-change
6
+ events for document types that opt in via the `keepsTransferHistory`,
7
+ `keepsPurchaseHistory`, and `keepsPricingHistory` configuration flags.
8
+
9
+ History documents are written by the protocol itself during document
10
+ transfer, purchase, and price-update state transitions
11
+ (`creationRestrictionMode: 2` — they can never be created directly), and are
12
+ immutable and permanent once written.
13
+
14
+ The document types carry provable aggregation trees:
15
+
16
+ - `purchase` is doctype-averageable on `price` (O(1) provable all-time sale
17
+ count, total volume, and average sale price), and its `byContract` and
18
+ `byDocument` indices are range-averageable over `$createdAt` (provable
19
+ per-contract and per-document sale count / volume / average between dates).
20
+ - `priceUpdate` is doctype-countable, and its `byContract` and `byDocument`
21
+ indices are range-averageable over `$createdAt` (provable average asking
22
+ price between dates). These are listing-event-weighted averages, not
23
+ time-weighted prices.
24
+ - `transfer` is doctype-countable, and its `byContract` index is
25
+ range-countable over `$createdAt` (provable transfer counts per contract
26
+ and time window).
27
+
28
+ ## Table of Contents
29
+
30
+ - [Contributing](#contributing)
31
+ - [License](#license)
32
+
33
+ ## Contributing
34
+
35
+ Feel free to dive in! [Open an issue](https://github.com/dashpay/platform/issues/new/choose) or submit PRs.
36
+
37
+ ## License
38
+
39
+ [MIT](LICENSE) © Dash Core Group, Inc.
@@ -0,0 +1,10 @@
1
+ import baseConfig from '../../eslint/base.mjs';
2
+ import mochaTestConfig from '../../eslint/mocha-tests.mjs';
3
+
4
+ export default [
5
+ ...baseConfig,
6
+ mochaTestConfig,
7
+ {
8
+ ignores: ['dist/**', 'node_modules/**'],
9
+ },
10
+ ];
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ ownerId: '11111111111111111111111111111111',
3
+ contractId: '6voHRaoiPcfmMhbqCA9dixH98xcgPQ9UEcuaXjpVu3LD',
4
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@dashevo/document-history-contract",
3
+ "version": "4.1.0-beta.2",
4
+ "description": "The document history contract",
5
+ "scripts": {
6
+ "lint": "eslint .",
7
+ "test": "yarn run test:unit",
8
+ "test:unit": "mocha 'test/unit/**/*.spec.js'"
9
+ },
10
+ "contributors": [
11
+ {
12
+ "name": "Samuel Westrich",
13
+ "email": "sam@dash.org",
14
+ "url": "https://github.com/quantumexplorer"
15
+ }
16
+ ],
17
+ "license": "MIT",
18
+ "devDependencies": {
19
+ "@dashevo/wasm-dpp": "workspace:*",
20
+ "chai": "^4.3.10",
21
+ "dirty-chai": "^2.0.1",
22
+ "eslint": "^9.18.0",
23
+ "mocha": "^11.1.0",
24
+ "sinon": "^18.0.1",
25
+ "sinon-chai": "^3.7.0"
26
+ }
27
+ }
@@ -0,0 +1,304 @@
1
+ {
2
+ "transfer": {
3
+ "type": "object",
4
+ "documentsMutable": false,
5
+ "canBeDeleted": false,
6
+ "creationRestrictionMode": 2,
7
+ "documentsCountable": true,
8
+ "indices": [
9
+ {
10
+ "name": "byDocument",
11
+ "properties": [
12
+ {
13
+ "dataContractId": "asc"
14
+ },
15
+ {
16
+ "documentId": "asc"
17
+ },
18
+ {
19
+ "$createdAt": "asc"
20
+ }
21
+ ]
22
+ },
23
+ {
24
+ "name": "byContract",
25
+ "properties": [
26
+ {
27
+ "dataContractId": "asc"
28
+ },
29
+ {
30
+ "$createdAt": "asc"
31
+ }
32
+ ],
33
+ "countable": "countable",
34
+ "rangeCountable": true
35
+ },
36
+ {
37
+ "name": "from",
38
+ "properties": [
39
+ {
40
+ "$ownerId": "asc"
41
+ },
42
+ {
43
+ "$createdAt": "asc"
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "name": "to",
49
+ "properties": [
50
+ {
51
+ "toIdentityId": "asc"
52
+ },
53
+ {
54
+ "$createdAt": "asc"
55
+ }
56
+ ]
57
+ }
58
+ ],
59
+ "properties": {
60
+ "dataContractId": {
61
+ "type": "array",
62
+ "byteArray": true,
63
+ "minItems": 32,
64
+ "maxItems": 32,
65
+ "description": "The data contract of the transferred document",
66
+ "position": 0,
67
+ "contentMediaType": "application/x.dash.dpp.identifier"
68
+ },
69
+ "documentTypeName": {
70
+ "type": "string",
71
+ "maxLength": 64,
72
+ "description": "The document type of the transferred document",
73
+ "position": 1
74
+ },
75
+ "documentId": {
76
+ "type": "array",
77
+ "byteArray": true,
78
+ "minItems": 32,
79
+ "maxItems": 32,
80
+ "description": "The transferred document",
81
+ "position": 2,
82
+ "contentMediaType": "application/x.dash.dpp.identifier"
83
+ },
84
+ "toIdentityId": {
85
+ "type": "array",
86
+ "byteArray": true,
87
+ "minItems": 32,
88
+ "maxItems": 32,
89
+ "description": "The identity the document was transferred to",
90
+ "position": 3,
91
+ "contentMediaType": "application/x.dash.dpp.identifier"
92
+ }
93
+ },
94
+ "required": [
95
+ "dataContractId",
96
+ "documentTypeName",
97
+ "documentId",
98
+ "toIdentityId",
99
+ "$createdAt",
100
+ "$createdAtBlockHeight"
101
+ ],
102
+ "additionalProperties": false
103
+ },
104
+ "purchase": {
105
+ "type": "object",
106
+ "documentsMutable": false,
107
+ "canBeDeleted": false,
108
+ "creationRestrictionMode": 2,
109
+ "documentsAverageable": "price",
110
+ "indices": [
111
+ {
112
+ "name": "byDocument",
113
+ "properties": [
114
+ {
115
+ "dataContractId": "asc"
116
+ },
117
+ {
118
+ "documentId": "asc"
119
+ },
120
+ {
121
+ "$createdAt": "asc"
122
+ }
123
+ ],
124
+ "averageable": "price",
125
+ "rangeAverageable": true
126
+ },
127
+ {
128
+ "name": "byContract",
129
+ "properties": [
130
+ {
131
+ "dataContractId": "asc"
132
+ },
133
+ {
134
+ "$createdAt": "asc"
135
+ }
136
+ ],
137
+ "averageable": "price",
138
+ "rangeAverageable": true
139
+ },
140
+ {
141
+ "name": "buyer",
142
+ "properties": [
143
+ {
144
+ "$ownerId": "asc"
145
+ },
146
+ {
147
+ "$createdAt": "asc"
148
+ }
149
+ ]
150
+ },
151
+ {
152
+ "name": "seller",
153
+ "properties": [
154
+ {
155
+ "sellerId": "asc"
156
+ },
157
+ {
158
+ "$createdAt": "asc"
159
+ }
160
+ ]
161
+ },
162
+ {
163
+ "name": "byPrice",
164
+ "properties": [
165
+ {
166
+ "dataContractId": "asc"
167
+ },
168
+ {
169
+ "price": "asc"
170
+ }
171
+ ]
172
+ }
173
+ ],
174
+ "properties": {
175
+ "dataContractId": {
176
+ "type": "array",
177
+ "byteArray": true,
178
+ "minItems": 32,
179
+ "maxItems": 32,
180
+ "description": "The data contract of the purchased document",
181
+ "position": 0,
182
+ "contentMediaType": "application/x.dash.dpp.identifier"
183
+ },
184
+ "documentTypeName": {
185
+ "type": "string",
186
+ "maxLength": 64,
187
+ "description": "The document type of the purchased document",
188
+ "position": 1
189
+ },
190
+ "documentId": {
191
+ "type": "array",
192
+ "byteArray": true,
193
+ "minItems": 32,
194
+ "maxItems": 32,
195
+ "description": "The purchased document",
196
+ "position": 2,
197
+ "contentMediaType": "application/x.dash.dpp.identifier"
198
+ },
199
+ "sellerId": {
200
+ "type": "array",
201
+ "byteArray": true,
202
+ "minItems": 32,
203
+ "maxItems": 32,
204
+ "description": "The identity that sold the document",
205
+ "position": 3,
206
+ "contentMediaType": "application/x.dash.dpp.identifier"
207
+ },
208
+ "price": {
209
+ "type": "integer",
210
+ "description": "The price paid for the document in credits. Unbounded integer so the property is sum-aggregatable (i64); actual values are consensus-validated non-negative credit amounts written only by the protocol",
211
+ "position": 4
212
+ }
213
+ },
214
+ "required": [
215
+ "dataContractId",
216
+ "documentTypeName",
217
+ "documentId",
218
+ "sellerId",
219
+ "price",
220
+ "$createdAt",
221
+ "$createdAtBlockHeight"
222
+ ],
223
+ "additionalProperties": false
224
+ },
225
+ "priceUpdate": {
226
+ "type": "object",
227
+ "$comment": "Time-range aggregates over these records are weighted by listing events: a document relisted N times in the window contributes N records. They are activity-weighted averages, not time-weighted prices.",
228
+ "documentsMutable": false,
229
+ "canBeDeleted": false,
230
+ "creationRestrictionMode": 2,
231
+ "documentsCountable": true,
232
+ "indices": [
233
+ {
234
+ "name": "byDocument",
235
+ "properties": [
236
+ {
237
+ "dataContractId": "asc"
238
+ },
239
+ {
240
+ "documentId": "asc"
241
+ },
242
+ {
243
+ "$createdAt": "asc"
244
+ }
245
+ ],
246
+ "averageable": "price",
247
+ "rangeAverageable": true
248
+ },
249
+ {
250
+ "name": "byContract",
251
+ "properties": [
252
+ {
253
+ "dataContractId": "asc"
254
+ },
255
+ {
256
+ "$createdAt": "asc"
257
+ }
258
+ ],
259
+ "averageable": "price",
260
+ "rangeAverageable": true
261
+ }
262
+ ],
263
+ "properties": {
264
+ "dataContractId": {
265
+ "type": "array",
266
+ "byteArray": true,
267
+ "minItems": 32,
268
+ "maxItems": 32,
269
+ "description": "The data contract of the listed document",
270
+ "position": 0,
271
+ "contentMediaType": "application/x.dash.dpp.identifier"
272
+ },
273
+ "documentTypeName": {
274
+ "type": "string",
275
+ "maxLength": 64,
276
+ "description": "The document type of the listed document",
277
+ "position": 1
278
+ },
279
+ "documentId": {
280
+ "type": "array",
281
+ "byteArray": true,
282
+ "minItems": 32,
283
+ "maxItems": 32,
284
+ "description": "The listed document",
285
+ "position": 2,
286
+ "contentMediaType": "application/x.dash.dpp.identifier"
287
+ },
288
+ "price": {
289
+ "type": "integer",
290
+ "description": "The new asking price for the document in credits. Unbounded integer so the property is sum-aggregatable (i64); actual values are consensus-validated non-negative credit amounts written only by the protocol",
291
+ "position": 3
292
+ }
293
+ },
294
+ "required": [
295
+ "dataContractId",
296
+ "documentTypeName",
297
+ "documentId",
298
+ "price",
299
+ "$createdAt",
300
+ "$createdAtBlockHeight"
301
+ ],
302
+ "additionalProperties": false
303
+ }
304
+ }
package/src/error.rs ADDED
@@ -0,0 +1,17 @@
1
+ use platform_version::version::FeatureVersion;
2
+
3
+ #[derive(thiserror::Error, Debug)]
4
+ pub enum Error {
5
+ /// Platform expected some specific versions
6
+ #[error("platform unknown version on {method}, received: {received}")]
7
+ UnknownVersionMismatch {
8
+ /// method
9
+ method: String,
10
+ /// the allowed versions for this method
11
+ known_versions: Vec<FeatureVersion>,
12
+ /// requested core height
13
+ received: FeatureVersion,
14
+ },
15
+ #[error("schema deserialize error: {0}")]
16
+ InvalidSchemaJson(#[from] serde_json::Error),
17
+ }
package/src/lib.rs ADDED
@@ -0,0 +1,37 @@
1
+ mod error;
2
+ pub mod v1;
3
+
4
+ pub use crate::error::Error;
5
+ use platform_value::{Identifier, IdentifierBytes32};
6
+ use platform_version::version::PlatformVersion;
7
+ use serde_json::Value;
8
+
9
+ pub const ID_BYTES: [u8; 32] = [
10
+ 88, 18, 140, 208, 179, 231, 242, 57, 225, 203, 4, 210, 245, 95, 136, 92, 160, 167, 112, 118,
11
+ 173, 238, 83, 62, 234, 230, 222, 16, 231, 30, 99, 98,
12
+ ];
13
+
14
+ pub const OWNER_ID_BYTES: [u8; 32] = [0; 32];
15
+
16
+ pub const ID: Identifier = Identifier(IdentifierBytes32(ID_BYTES));
17
+ pub const OWNER_ID: Identifier = Identifier(IdentifierBytes32(OWNER_ID_BYTES));
18
+ pub fn load_definitions(platform_version: &PlatformVersion) -> Result<Option<Value>, Error> {
19
+ match platform_version.system_data_contracts.document_history {
20
+ 1 => Ok(None),
21
+ version => Err(Error::UnknownVersionMismatch {
22
+ method: "document_history_contract::load_definitions".to_string(),
23
+ known_versions: vec![1],
24
+ received: version,
25
+ }),
26
+ }
27
+ }
28
+ pub fn load_documents_schemas(platform_version: &PlatformVersion) -> Result<Value, Error> {
29
+ match platform_version.system_data_contracts.document_history {
30
+ 1 => v1::load_documents_schemas(),
31
+ version => Err(Error::UnknownVersionMismatch {
32
+ method: "document_history_contract::load_documents_schemas".to_string(),
33
+ known_versions: vec![1],
34
+ received: version,
35
+ }),
36
+ }
37
+ }
package/src/v1/mod.rs ADDED
@@ -0,0 +1,45 @@
1
+ use crate::Error;
2
+ use serde_json::Value;
3
+
4
+ pub mod document_types {
5
+ pub mod transfer {
6
+ pub const NAME: &str = "transfer";
7
+
8
+ pub mod properties {
9
+ pub const DATA_CONTRACT_ID: &str = "dataContractId";
10
+ pub const DOCUMENT_TYPE_NAME: &str = "documentTypeName";
11
+ pub const DOCUMENT_ID: &str = "documentId";
12
+ pub const TO_IDENTITY_ID: &str = "toIdentityId";
13
+ }
14
+ }
15
+
16
+ pub mod purchase {
17
+ pub const NAME: &str = "purchase";
18
+
19
+ pub mod properties {
20
+ pub const DATA_CONTRACT_ID: &str = "dataContractId";
21
+ pub const DOCUMENT_TYPE_NAME: &str = "documentTypeName";
22
+ pub const DOCUMENT_ID: &str = "documentId";
23
+ pub const SELLER_ID: &str = "sellerId";
24
+ pub const PRICE: &str = "price";
25
+ }
26
+ }
27
+
28
+ pub mod price_update {
29
+ pub const NAME: &str = "priceUpdate";
30
+
31
+ pub mod properties {
32
+ pub const DATA_CONTRACT_ID: &str = "dataContractId";
33
+ pub const DOCUMENT_TYPE_NAME: &str = "documentTypeName";
34
+ pub const DOCUMENT_ID: &str = "documentId";
35
+ pub const PRICE: &str = "price";
36
+ }
37
+ }
38
+ }
39
+
40
+ pub fn load_documents_schemas() -> Result<Value, Error> {
41
+ serde_json::from_str(include_str!(
42
+ "../../schema/v1/document-history-contract-documents.json"
43
+ ))
44
+ .map_err(Error::InvalidSchemaJson)
45
+ }
@@ -0,0 +1,30 @@
1
+ const sinon = require('sinon');
2
+ const sinonChai = require('sinon-chai');
3
+
4
+ const { expect, use } = require('chai');
5
+ const dirtyChai = require('dirty-chai');
6
+
7
+ const {
8
+ default: loadWasmDpp,
9
+ } = require('@dashevo/wasm-dpp');
10
+
11
+ use(dirtyChai);
12
+ use(sinonChai);
13
+
14
+ exports.mochaHooks = {
15
+ beforeAll: loadWasmDpp,
16
+
17
+ beforeEach() {
18
+ if (!this.sinon) {
19
+ this.sinon = sinon.createSandbox();
20
+ } else {
21
+ this.sinon.restore();
22
+ }
23
+ },
24
+
25
+ afterEach() {
26
+ this.sinon.restore();
27
+ },
28
+ };
29
+
30
+ global.expect = expect;
@@ -0,0 +1,172 @@
1
+ const crypto = require('crypto');
2
+ const {
3
+ DashPlatformProtocol,
4
+ JsonSchemaError,
5
+ } = require('@dashevo/wasm-dpp');
6
+ const generateRandomIdentifier = require('@dashevo/wasm-dpp/lib/test/utils/generateRandomIdentifierAsync');
7
+ const { expect } = require('chai');
8
+ const documentHistoryContractDocumentsSchema = require('../../schema/v1/document-history-contract-documents.json');
9
+
10
+ const expectJsonSchemaError = (validationResult, errorCount = 1) => {
11
+ const errors = validationResult.getErrors();
12
+ expect(errors).to.have.length(errorCount);
13
+
14
+ const error = validationResult.getErrors()[0];
15
+ expect(error).to.be.instanceof(JsonSchemaError);
16
+
17
+ return error;
18
+ };
19
+
20
+ describe('Document History Contract', () => {
21
+ let dpp;
22
+ let dataContract;
23
+ let identityId;
24
+
25
+ beforeEach(async () => {
26
+ dpp = new DashPlatformProtocol({ generate: () => crypto.randomBytes(32) });
27
+ identityId = await generateRandomIdentifier();
28
+ dataContract = dpp.dataContract.create(
29
+ identityId,
30
+ BigInt(1),
31
+ documentHistoryContractDocumentsSchema,
32
+ );
33
+ });
34
+
35
+ it('should have a valid contract definition', async () => {
36
+ const createContract = () => dpp.dataContract.create(
37
+ identityId,
38
+ BigInt(1),
39
+ documentHistoryContractDocumentsSchema,
40
+ );
41
+
42
+ expect(createContract).to.not.throw();
43
+ });
44
+
45
+ describe('documents', () => {
46
+ describe('transfer', () => {
47
+ let rawTransferDocument;
48
+
49
+ beforeEach(() => {
50
+ rawTransferDocument = {
51
+ dataContractId: crypto.randomBytes(32),
52
+ documentTypeName: 'domain',
53
+ documentId: crypto.randomBytes(32),
54
+ toIdentityId: crypto.randomBytes(32),
55
+ };
56
+ });
57
+
58
+ ['dataContractId', 'documentTypeName', 'documentId', 'toIdentityId'].forEach((property) => {
59
+ describe(property, () => {
60
+ it('should be defined', async () => {
61
+ delete rawTransferDocument[property];
62
+ const document = dpp.document.create(dataContract, identityId, 'transfer', rawTransferDocument);
63
+ const validationResult = document.validate(dpp.protocolVersion);
64
+ const error = expectJsonSchemaError(validationResult);
65
+ expect(error.keyword).to.equal('required');
66
+ expect(error.params.missingProperty).to.equal(property);
67
+ });
68
+ });
69
+ });
70
+
71
+ it('should not have additional properties', async () => {
72
+ rawTransferDocument.someOtherProperty = 42;
73
+ const document = dpp.document.create(dataContract, identityId, 'transfer', rawTransferDocument);
74
+ const validationResult = document.validate(dpp.protocolVersion);
75
+ const error = expectJsonSchemaError(validationResult);
76
+ expect(error.keyword).to.equal('additionalProperties');
77
+ expect(error.params.additionalProperties).to.deep.equal(['someOtherProperty']);
78
+ });
79
+ });
80
+
81
+ describe('purchase', () => {
82
+ let rawPurchaseDocument;
83
+
84
+ beforeEach(() => {
85
+ rawPurchaseDocument = {
86
+ dataContractId: crypto.randomBytes(32),
87
+ documentTypeName: 'domain',
88
+ documentId: crypto.randomBytes(32),
89
+ sellerId: crypto.randomBytes(32),
90
+ price: 100000,
91
+ };
92
+ });
93
+
94
+ ['dataContractId', 'documentTypeName', 'documentId', 'sellerId', 'price'].forEach((property) => {
95
+ describe(property, () => {
96
+ it('should be defined', async () => {
97
+ delete rawPurchaseDocument[property];
98
+ const document = dpp.document.create(dataContract, identityId, 'purchase', rawPurchaseDocument);
99
+ const validationResult = document.validate(dpp.protocolVersion);
100
+ const error = expectJsonSchemaError(validationResult);
101
+ expect(error.keyword).to.equal('required');
102
+ expect(error.params.missingProperty).to.equal(property);
103
+ });
104
+ });
105
+ });
106
+
107
+ describe('price', () => {
108
+ it('should be an integer', async () => {
109
+ rawPurchaseDocument.price = 'a lot';
110
+ const document = dpp.document.create(dataContract, identityId, 'purchase', rawPurchaseDocument);
111
+ const validationResult = document.validate(dpp.protocolVersion);
112
+ const error = expectJsonSchemaError(validationResult);
113
+ expect(error.keyword).to.equal('type');
114
+ });
115
+
116
+ // The schema deliberately has no `minimum`: the property stays an
117
+ // unbounded (sum-aggregatable i64) integer, and actual values are
118
+ // consensus-validated credit amounts written only by the protocol.
119
+ it('should accept negative values at the schema layer', async () => {
120
+ rawPurchaseDocument.price = -1;
121
+ const document = dpp.document.create(dataContract, identityId, 'purchase', rawPurchaseDocument);
122
+ const validationResult = document.validate(dpp.protocolVersion);
123
+ expect(validationResult.isValid()).to.be.true();
124
+ });
125
+ });
126
+
127
+ it('should not have additional properties', async () => {
128
+ rawPurchaseDocument.someOtherProperty = 42;
129
+ const document = dpp.document.create(dataContract, identityId, 'purchase', rawPurchaseDocument);
130
+ const validationResult = document.validate(dpp.protocolVersion);
131
+ const error = expectJsonSchemaError(validationResult);
132
+ expect(error.keyword).to.equal('additionalProperties');
133
+ expect(error.params.additionalProperties).to.deep.equal(['someOtherProperty']);
134
+ });
135
+ });
136
+
137
+ describe('priceUpdate', () => {
138
+ let rawPriceUpdateDocument;
139
+
140
+ beforeEach(() => {
141
+ rawPriceUpdateDocument = {
142
+ dataContractId: crypto.randomBytes(32),
143
+ documentTypeName: 'domain',
144
+ documentId: crypto.randomBytes(32),
145
+ price: 100000,
146
+ };
147
+ });
148
+
149
+ ['dataContractId', 'documentTypeName', 'documentId', 'price'].forEach((property) => {
150
+ describe(property, () => {
151
+ it('should be defined', async () => {
152
+ delete rawPriceUpdateDocument[property];
153
+ const document = dpp.document.create(dataContract, identityId, 'priceUpdate', rawPriceUpdateDocument);
154
+ const validationResult = document.validate(dpp.protocolVersion);
155
+ const error = expectJsonSchemaError(validationResult);
156
+ expect(error.keyword).to.equal('required');
157
+ expect(error.params.missingProperty).to.equal(property);
158
+ });
159
+ });
160
+ });
161
+
162
+ it('should not have additional properties', async () => {
163
+ rawPriceUpdateDocument.someOtherProperty = 42;
164
+ const document = dpp.document.create(dataContract, identityId, 'priceUpdate', rawPriceUpdateDocument);
165
+ const validationResult = document.validate(dpp.protocolVersion);
166
+ const error = expectJsonSchemaError(validationResult);
167
+ expect(error.keyword).to.equal('additionalProperties');
168
+ expect(error.params.additionalProperties).to.deep.equal(['someOtherProperty']);
169
+ });
170
+ });
171
+ });
172
+ });