@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 +2 -0
- package/Cargo.toml +13 -0
- package/LICENSE +20 -0
- package/README.md +39 -0
- package/eslint.config.mjs +10 -0
- package/lib/systemIds.js +4 -0
- package/package.json +27 -0
- package/schema/v1/document-history-contract-documents.json +304 -0
- package/src/error.rs +17 -0
- package/src/lib.rs +37 -0
- package/src/v1/mod.rs +45 -0
- package/test/bootstrap.js +30 -0
- package/test/unit/documentHistoryContract.spec.js +172 -0
package/.mocharc.yml
ADDED
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
|
+
[](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.
|
package/lib/systemIds.js
ADDED
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
|
+
});
|