Skip to content
This repository was archived by the owner on Jan 4, 2022. It is now read-only.

Commit 390b63a

Browse files
authored
feat!: validate protocol version (#336)
1 parent 420b4d3 commit 390b63a

32 files changed

+675
-187
lines changed

lib/DashPlatformProtocol.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { default: getRE2Class } = require('@dashevo/re2-wasm');
22
const createAjv = require('./ajv/createAjv');
33

4-
const { protocolVersion } = require('./protocolVersion');
4+
const protocolVersion = require('./protocolVersion');
55

66
const JsonSchemaValidator = require('./validation/JsonSchemaValidator');
77

@@ -26,7 +26,7 @@ class DashPlatformProtocol {
2626

2727
this.protocolVersion = this.options.protocolVersion !== undefined
2828
? this.options.protocolVersion
29-
: protocolVersion;
29+
: protocolVersion.latestVersion;
3030

3131
this.stateRepository = undefined;
3232
this.jsonSchemaValidator = undefined;

lib/dataContract/DataContractFacade.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const validateDataContractFactory = require('./validation/validateDataContractFa
66
const enrichDataContractWithBaseSchema = require('./enrichDataContractWithBaseSchema');
77
const validateDataContractMaxDepthFactory = require('./validation/validateDataContractMaxDepthFactory');
88
const validateDataContractPatternsFactory = require('./validation/validateDataContractPatternsFactory');
9+
const decodeProtocolEntityFactory = require('../decodeProtocolEntityFactory');
10+
11+
const protocolVersion = require('../protocolVersion');
912

1013
class DataContractFacade {
1114
/**
@@ -25,9 +28,12 @@ class DataContractFacade {
2528
RE2,
2629
);
2730

31+
const decodeProtocolEntity = decodeProtocolEntityFactory(protocolVersion.compatibility);
32+
2833
this.factory = new DataContractFactory(
2934
dpp,
3035
this.validateDataContract,
36+
decodeProtocolEntity,
3137
);
3238
}
3339

lib/dataContract/DataContractFactory.js

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,19 @@ const generateDataContractId = require('./generateDataContractId');
55

66
const DataContractCreateTransition = require('./stateTransition/DataContractCreateTransition/DataContractCreateTransition');
77

8-
const SerializedObjectParsingError = require('../errors/SerializedObjectParsingError');
9-
108
const generateEntropy = require('../util/generateEntropy');
11-
12-
const { decode } = require('../util/serializer');
9+
const ConsensusError = require('../errors/ConsensusError');
1310

1411
class DataContractFactory {
1512
/**
1613
* @param {DashPlatformProtocol} dpp
1714
* @param {validateDataContract} validateDataContract
15+
* @param {decodeProtocolEntity} decodeProtocolEntity
1816
*/
19-
constructor(dpp, validateDataContract) {
17+
constructor(dpp, validateDataContract, decodeProtocolEntity) {
2018
this.dpp = dpp;
2119
this.validateDataContract = validateDataContract;
20+
this.decodeProtocolEntity = decodeProtocolEntity;
2221
}
2322

2423
/**
@@ -79,17 +78,21 @@ class DataContractFactory {
7978
*/
8079
async createFromBuffer(buffer, options = { }) {
8180
let rawDataContract;
81+
let protocolVersion;
82+
8283
try {
83-
// first 4 bytes are protocol version
84-
rawDataContract = decode(buffer.slice(4, buffer.length));
85-
rawDataContract.protocolVersion = buffer.slice(0, 4).readUInt32BE(0);
84+
[protocolVersion, rawDataContract] = this.decodeProtocolEntity(
85+
buffer,
86+
this.dpp.getProtocolVersion(),
87+
);
88+
89+
rawDataContract.protocolVersion = protocolVersion;
8690
} catch (error) {
87-
throw new InvalidDataContractError([
88-
new SerializedObjectParsingError(
89-
buffer,
90-
error,
91-
),
92-
]);
91+
if (error instanceof ConsensusError) {
92+
throw new InvalidDataContractError([error]);
93+
}
94+
95+
throw error;
9396
}
9497

9598
return this.createFromObject(rawDataContract, options);

lib/decodeProtocolEntityFactory.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const ProtocolVersionParsingError = require('./errors/ProtocolVersionParsingError');
2+
const SerializedObjectParsingError = require('./errors/SerializedObjectParsingError');
3+
const UnsupportedProtocolVersionError = require('./errors/UnsupportedProtocolVersionError');
4+
const IncompatibleProtocolVersionError = require('./errors/IncompatibleProtocolVersionError');
5+
const CompatibleProtocolVersionIsNotDefinedError = require('./errors/CompatibleProtocolVersionIsNotDefinedError');
6+
7+
const { decode } = require('./util/serializer');
8+
9+
function decodeProtocolEntityFactory(versionCompatibilityMap) {
10+
/**
11+
* @typedef {decodeProtocolEntity}
12+
* @param {Buffer} buffer
13+
* @param {number} currentProtocolVersion
14+
* @return {[number, Object]}
15+
*/
16+
function decodeProtocolEntity(buffer, currentProtocolVersion) {
17+
// Parse protocol version from the first 4 bytes
18+
let protocolVersion;
19+
try {
20+
protocolVersion = buffer.slice(0, 4).readUInt32BE(0);
21+
} catch (error) {
22+
throw new ProtocolVersionParsingError(
23+
buffer,
24+
error,
25+
);
26+
}
27+
28+
// Parsed protocol version must be equal or lower than current version
29+
if (protocolVersion > currentProtocolVersion) {
30+
throw new UnsupportedProtocolVersionError(
31+
buffer,
32+
protocolVersion,
33+
currentProtocolVersion,
34+
);
35+
}
36+
37+
if (!Object.prototype.hasOwnProperty.call(versionCompatibilityMap, currentProtocolVersion)) {
38+
throw new CompatibleProtocolVersionIsNotDefinedError(currentProtocolVersion);
39+
}
40+
41+
const minimalProtocolVersion = versionCompatibilityMap[currentProtocolVersion];
42+
43+
// Parsed protocol version must higher or equal to the minimum compatible version
44+
if (protocolVersion < minimalProtocolVersion) {
45+
throw new IncompatibleProtocolVersionError(
46+
buffer,
47+
protocolVersion,
48+
minimalProtocolVersion,
49+
);
50+
}
51+
52+
let rawEntity;
53+
try {
54+
rawEntity = decode(
55+
buffer.slice(4, buffer.length),
56+
);
57+
} catch (error) {
58+
throw new SerializedObjectParsingError(
59+
buffer,
60+
error,
61+
);
62+
}
63+
64+
return [protocolVersion, rawEntity];
65+
}
66+
67+
return decodeProtocolEntity;
68+
}
69+
70+
module.exports = decodeProtocolEntityFactory;

lib/document/DocumentFacade.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const Document = require('./Document');
66
const DocumentFactory = require('./DocumentFactory');
77

88
const MissingOptionError = require('../errors/MissingOptionError');
9+
const decodeProtocolEntityFactory = require('../decodeProtocolEntityFactory');
10+
11+
const protocolVersion = require('../protocolVersion');
912

1013
class DocumentFacade {
1114
/**
@@ -23,10 +26,13 @@ class DocumentFacade {
2326
this.stateRepository,
2427
);
2528

29+
const decodeProtocolEntity = decodeProtocolEntityFactory(protocolVersion.compatibility);
30+
2631
this.factory = new DocumentFactory(
2732
dpp,
2833
this.validateDocument,
2934
this.fetchAndValidateDataContract,
35+
decodeProtocolEntity,
3036
);
3137
}
3238

lib/document/DocumentFactory.js

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,38 @@
11
const Document = require('./Document');
22

3-
const { decode } = require('../util/serializer');
43
const generateEntropy = require('../util/generateEntropy');
4+
const generateDocumentId = require('./generateDocumentId');
55

66
const DocumentsBatchTransition = require('./stateTransition/DocumentsBatchTransition/DocumentsBatchTransition');
77

88
const AbstractDocumentTransition = require('./stateTransition/DocumentsBatchTransition/documentTransition/AbstractDocumentTransition');
99
const DocumentCreateTransition = require('./stateTransition/DocumentsBatchTransition/documentTransition/DocumentCreateTransition');
1010

11+
const ConsensusError = require('../errors/ConsensusError');
1112
const InvalidActionNameError = require('./errors/InvalidActionNameError');
1213
const NoDocumentsSuppliedError = require('./errors/NoDocumentsSuppliedError');
1314
const MismatchOwnerIdsError = require('./errors/MismatchOwnerIdsError');
1415
const InvalidInitialRevisionError = require('./errors/InvalidInitialRevisionError');
15-
1616
const InvalidDocumentError = require('./errors/InvalidDocumentError');
1717
const InvalidDocumentTypeError = require('../errors/InvalidDocumentTypeError');
18-
const SerializedObjectParsingError = require('../errors/SerializedObjectParsingError');
19-
20-
const generateDocumentId = require('./generateDocumentId');
2118

2219
class DocumentFactory {
2320
/**
2421
* @param {DashPlatformProtocol} dpp
2522
* @param {validateDocument} validateDocument
2623
* @param {fetchAndValidateDataContract} fetchAndValidateDataContract
24+
* @param {decodeProtocolEntity} decodeProtocolEntity
2725
*/
28-
constructor(dpp, validateDocument, fetchAndValidateDataContract) {
26+
constructor(
27+
dpp,
28+
validateDocument,
29+
fetchAndValidateDataContract,
30+
decodeProtocolEntity,
31+
) {
2932
this.dpp = dpp;
3033
this.validateDocument = validateDocument;
3134
this.fetchAndValidateDataContract = fetchAndValidateDataContract;
35+
this.decodeProtocolEntity = decodeProtocolEntity;
3236
}
3337

3438
/**
@@ -160,17 +164,21 @@ class DocumentFactory {
160164
*/
161165
async createFromBuffer(buffer, options = { }) {
162166
let rawDocument;
167+
let protocolVersion;
168+
163169
try {
164-
// first 4 bytes are protocol version
165-
rawDocument = decode(buffer.slice(4, buffer.length));
166-
rawDocument.$protocolVersion = buffer.slice(0, 4).readUInt32BE(0);
170+
[protocolVersion, rawDocument] = this.decodeProtocolEntity(
171+
buffer,
172+
this.dpp.getProtocolVersion(),
173+
);
174+
175+
rawDocument.$protocolVersion = protocolVersion;
167176
} catch (error) {
168-
throw new InvalidDocumentError([
169-
new SerializedObjectParsingError(
170-
buffer,
171-
error,
172-
),
173-
]);
177+
if (error instanceof ConsensusError) {
178+
throw new InvalidDocumentError([error]);
179+
}
180+
181+
throw error;
174182
}
175183

176184
return this.createFromObject(rawDocument, options);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class CompatibleProtocolVersionIsNotDefinedError extends Error {
2+
/**
3+
* @param {number} currentProtocolVersion
4+
*/
5+
constructor(currentProtocolVersion) {
6+
super();
7+
8+
this.name = this.constructor.name;
9+
this.message = `Compatible version is not defined for protocol version ${currentProtocolVersion}`;
10+
11+
this.currentProtocolVersion = currentProtocolVersion;
12+
13+
if (Error.captureStackTrace) {
14+
Error.captureStackTrace(this, this.constructor);
15+
}
16+
}
17+
18+
/**
19+
* @return {number}
20+
*/
21+
getCurrentProtocolVersion() {
22+
return this.currentProtocolVersion;
23+
}
24+
}
25+
26+
module.exports = CompatibleProtocolVersionIsNotDefinedError;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const ConsensusError = require('./ConsensusError');
2+
3+
class IncompatibleProtocolVersionError extends ConsensusError {
4+
/**
5+
* @param {Buffer} payload
6+
* @param {number} parsedProtocolVersion
7+
* @param {number} minimalProtocolVersion
8+
*/
9+
constructor(payload, parsedProtocolVersion, minimalProtocolVersion) {
10+
super(
11+
`Protocol version ${parsedProtocolVersion} is not supported. Minimal supported protocol version is ${minimalProtocolVersion}`,
12+
);
13+
14+
this.payload = payload;
15+
this.parsedProtocolVersion = parsedProtocolVersion;
16+
this.minimalProtocolVersion = minimalProtocolVersion;
17+
}
18+
19+
/**
20+
* Get object payload
21+
*
22+
* @return {Buffer}
23+
*/
24+
getPayload() {
25+
return this.payload;
26+
}
27+
28+
/**
29+
* @return {number}
30+
*/
31+
getParsedProtocolVersion() {
32+
return this.parsedProtocolVersion;
33+
}
34+
35+
/**
36+
* @return {number}
37+
*/
38+
getMinimalProtocolVersion() {
39+
return this.minimalProtocolVersion;
40+
}
41+
}
42+
43+
module.exports = IncompatibleProtocolVersionError;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const ConsensusError = require('./ConsensusError');
2+
3+
class ProtocolVersionParsingError extends ConsensusError {
4+
/**
5+
* @param {Buffer} payload
6+
* @param {Error} parsingError
7+
*/
8+
constructor(payload, parsingError) {
9+
super(
10+
`Can't read protocol version from serialized object: ${parsingError.message}`,
11+
);
12+
13+
this.payload = payload;
14+
this.parsingError = parsingError;
15+
}
16+
17+
/**
18+
* Get object payload
19+
*
20+
* @return {Buffer}
21+
*/
22+
getPayload() {
23+
return this.payload;
24+
}
25+
26+
/**
27+
* Get parsing error
28+
*
29+
* @return {Error}
30+
*/
31+
getParsingError() {
32+
return this.parsingError;
33+
}
34+
}
35+
36+
module.exports = ProtocolVersionParsingError;

lib/errors/SerializedObjectParsingError.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const ConsensusError = require('./ConsensusError');
22

33
class SerializedObjectParsingError extends ConsensusError {
44
/**
5-
* @param {Buffer|string} payload
5+
* @param {Buffer} payload
66
* @param {Error} parsingError
77
*/
88
constructor(payload, parsingError) {
@@ -15,9 +15,9 @@ class SerializedObjectParsingError extends ConsensusError {
1515
}
1616

1717
/**
18-
* Get object payload
18+
* Get payload
1919
*
20-
* @return {Buffer|string}
20+
* @return {Buffer}
2121
*/
2222
getPayload() {
2323
return this.payload;

0 commit comments

Comments
 (0)