@obolnetwork/obol-sdk 1.0.12 → 1.0.13
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/LICENCE +21 -0
- package/README.md +33 -3
- package/dist/cjs/package.json +128 -0
- package/dist/cjs/src/ajv.js +2 -1
- package/dist/cjs/src/base.js +2 -2
- package/dist/cjs/src/constants.js +38 -13
- package/dist/cjs/src/errors.js +1 -1
- package/dist/cjs/src/index.js +24 -21
- package/dist/cjs/src/schema.js +24 -36
- package/dist/cjs/src/services.js +2 -2
- package/dist/cjs/src/types.js +0 -1
- package/dist/cjs/src/utils.js +36 -1
- package/dist/cjs/src/{verify.js → verification/common.js} +128 -155
- package/dist/cjs/src/verification/sszTypes.js +69 -0
- package/dist/cjs/src/verification/v1.6.0.js +149 -0
- package/dist/cjs/src/verification/v1.7.0.js +173 -0
- package/dist/cjs/src/verification/v1.8.0.js +183 -0
- package/dist/cjs/test/fixtures.js +281 -63
- package/dist/cjs/test/methods.test.js +58 -35
- package/dist/esm/package.json +128 -0
- package/dist/esm/src/ajv.js +2 -1
- package/dist/esm/src/base.js +2 -2
- package/dist/esm/src/constants.js +14 -12
- package/dist/esm/src/errors.js +1 -1
- package/dist/esm/src/index.js +27 -24
- package/dist/esm/src/schema.js +24 -36
- package/dist/esm/src/services.js +1 -1
- package/dist/esm/src/types.js +0 -1
- package/dist/esm/src/utils.js +34 -0
- package/dist/esm/src/{verify.js → verification/common.js} +114 -146
- package/dist/esm/src/verification/sszTypes.js +64 -0
- package/dist/esm/src/verification/v1.6.0.js +142 -0
- package/dist/esm/src/verification/v1.7.0.js +166 -0
- package/dist/esm/src/verification/v1.8.0.js +176 -0
- package/dist/esm/test/fixtures.js +280 -62
- package/dist/esm/test/methods.test.js +59 -36
- package/dist/types/src/ajv.d.ts +1 -1
- package/dist/types/src/base.d.ts +3 -3
- package/dist/types/src/constants.d.ts +8 -10
- package/dist/types/src/index.d.ts +16 -16
- package/dist/types/src/services.d.ts +1 -1
- package/dist/types/src/types.d.ts +33 -27
- package/dist/types/src/utils.d.ts +3 -0
- package/dist/types/src/verification/common.d.ts +32 -0
- package/dist/types/src/verification/sszTypes.d.ts +63 -0
- package/dist/types/src/verification/v1.6.0.d.ts +34 -0
- package/dist/types/src/verification/v1.7.0.d.ts +34 -0
- package/dist/types/src/verification/v1.8.0.d.ts +35 -0
- package/dist/types/test/fixtures.d.ts +104 -2
- package/package.json +42 -8
- package/src/ajv.ts +15 -10
- package/src/base.ts +25 -21
- package/src/constants.ts +80 -78
- package/src/errors.ts +6 -9
- package/src/index.ts +118 -81
- package/src/schema.ts +24 -36
- package/src/services.ts +15 -13
- package/src/types.ts +72 -74
- package/src/utils.ts +53 -4
- package/src/verification/common.ts +420 -0
- package/src/verification/sszTypes.ts +79 -0
- package/src/verification/v1.6.0.ts +226 -0
- package/src/verification/v1.7.0.ts +274 -0
- package/src/verification/v1.8.0.ts +281 -0
- package/dist/cjs/src/hash.js +0 -172
- package/dist/esm/src/hash.js +0 -165
- package/dist/types/src/hash.d.ts +0 -56
- package/dist/types/src/verify.d.ts +0 -4
- package/src/hash.ts +0 -250
- package/src/verify.ts +0 -479
|
@@ -35,73 +35,69 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
35
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
36
|
};
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
exports.isValidClusterLock = exports.signingRoot = void 0;
|
|
39
|
-
const semver = __importStar(require("semver"));
|
|
40
|
-
const bls_1 = require("@chainsafe/bls");
|
|
41
|
-
const discv5_1 = require("@chainsafe/discv5");
|
|
38
|
+
exports.isValidClusterLock = exports.signingRoot = exports.verifyNodeSignatures = exports.verifyBuilderRegistration = exports.verifyDepositData = exports.clusterLockHash = exports.clusterConfigOrDefinitionHash = void 0;
|
|
42
39
|
const ssz_1 = require("@chainsafe/ssz");
|
|
43
|
-
const uint_js_1 = require("@chainsafe/ssz/lib/type/uint.js");
|
|
44
|
-
const eth_sig_util_1 = require("@metamask/eth-sig-util");
|
|
45
40
|
const elliptic_1 = __importDefault(require("elliptic"));
|
|
41
|
+
const bls_1 = require("@chainsafe/bls");
|
|
42
|
+
const types_js_1 = require("../types.js");
|
|
43
|
+
const semver = __importStar(require("semver"));
|
|
44
|
+
const v1_6_0_js_1 = require("./v1.6.0.js");
|
|
45
|
+
const v1_7_0_js_1 = require("./v1.7.0.js");
|
|
46
46
|
const ethers_1 = require("ethers");
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const utils_js_1 = require("
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return (operator.address &&
|
|
76
|
-
operator.config_signature &&
|
|
77
|
-
operator.enr &&
|
|
78
|
-
operator.enr_signature);
|
|
79
|
-
})) {
|
|
80
|
-
return constants_js_1.DefinitionFlow.Group;
|
|
47
|
+
const constants_js_1 = require("../constants.js");
|
|
48
|
+
const eth_sig_util_1 = require("@metamask/eth-sig-util");
|
|
49
|
+
const sszTypes_js_1 = require("./sszTypes.js");
|
|
50
|
+
const utils_js_1 = require("../utils.js");
|
|
51
|
+
const discv5_1 = require("@chainsafe/discv5");
|
|
52
|
+
const v1_8_0_js_1 = require("./v1.8.0.js");
|
|
53
|
+
// cluster-definition hash
|
|
54
|
+
/**
|
|
55
|
+
* @param cluster The cluster configuration or the cluster definition
|
|
56
|
+
* @param configOnly a boolean to indicate config hash or definition hash
|
|
57
|
+
* @returns The config hash or the definition hash in of the corresponding cluster
|
|
58
|
+
*/
|
|
59
|
+
const clusterConfigOrDefinitionHash = (cluster, configOnly) => {
|
|
60
|
+
let definitionType, val;
|
|
61
|
+
if (semver.eq(cluster.version, 'v1.6.0')) {
|
|
62
|
+
definitionType = (0, v1_6_0_js_1.clusterDefinitionContainerTypeV1X6)(configOnly);
|
|
63
|
+
val = (0, v1_6_0_js_1.hashClusterDefinitionV1X6)(cluster, configOnly);
|
|
64
|
+
return ('0x' + Buffer.from(definitionType.hashTreeRoot(val).buffer).toString('hex'));
|
|
65
|
+
}
|
|
66
|
+
if (semver.eq(cluster.version, 'v1.7.0')) {
|
|
67
|
+
definitionType = (0, v1_7_0_js_1.clusterDefinitionContainerTypeV1X7)(configOnly);
|
|
68
|
+
val = (0, v1_7_0_js_1.hashClusterDefinitionV1X7)(cluster, configOnly);
|
|
69
|
+
return ('0x' + Buffer.from(definitionType.hashTreeRoot(val).buffer).toString('hex'));
|
|
70
|
+
}
|
|
71
|
+
if (semver.eq(cluster.version, 'v1.8.0')) {
|
|
72
|
+
definitionType = (0, v1_8_0_js_1.clusterDefinitionContainerTypeV1X8)(configOnly);
|
|
73
|
+
val = (0, v1_8_0_js_1.hashClusterDefinitionV1X8)(cluster, configOnly);
|
|
74
|
+
return ('0x' + Buffer.from(definitionType.hashTreeRoot(val).buffer).toString('hex'));
|
|
81
75
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
76
|
+
throw new Error('unsupported version');
|
|
77
|
+
};
|
|
78
|
+
exports.clusterConfigOrDefinitionHash = clusterConfigOrDefinitionHash;
|
|
79
|
+
// cluster-lock hash
|
|
80
|
+
/**
|
|
81
|
+
* Returns the SSZ cluster lock hash of the given cluster lock object
|
|
82
|
+
* @param cluster The cluster lock whose lock hash needs to be calculated
|
|
83
|
+
* @returns The cluster lock hash in of the corresponding cluster lock
|
|
84
|
+
*/
|
|
85
|
+
const clusterLockHash = (clusterLock) => {
|
|
86
|
+
if (semver.eq(clusterLock.cluster_definition.version, 'v1.6.0')) {
|
|
87
|
+
return (0, v1_6_0_js_1.hashClusterLockV1X6)(clusterLock);
|
|
88
|
+
}
|
|
89
|
+
if (semver.eq(clusterLock.cluster_definition.version, 'v1.7.0')) {
|
|
90
|
+
return (0, v1_7_0_js_1.hashClusterLockV1X7)(clusterLock);
|
|
91
91
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
clusterDefinition.operators.every(operator => {
|
|
95
|
-
return (!operator.address &&
|
|
96
|
-
!operator.config_signature &&
|
|
97
|
-
operator.enr &&
|
|
98
|
-
!operator.enr_signature);
|
|
99
|
-
})) {
|
|
100
|
-
return constants_js_1.DefinitionFlow.Charon;
|
|
92
|
+
if (semver.eq(clusterLock.cluster_definition.version, 'v1.8.0')) {
|
|
93
|
+
return (0, v1_8_0_js_1.hashClusterLockV1X8)(clusterLock);
|
|
101
94
|
}
|
|
102
|
-
|
|
95
|
+
// other versions
|
|
96
|
+
throw new Error('unsupported version');
|
|
103
97
|
};
|
|
104
|
-
|
|
98
|
+
exports.clusterLockHash = clusterLockHash;
|
|
99
|
+
// Lock verification
|
|
100
|
+
// cluster-definition signatures verification
|
|
105
101
|
const getPOSTConfigHashSigner = (signature, configHash, chainId) => {
|
|
106
102
|
try {
|
|
107
103
|
const sig = ethers_1.ethers.Signature.from(signature);
|
|
@@ -136,7 +132,7 @@ const getEnrSigner = (signature, payload, chainId) => {
|
|
|
136
132
|
}
|
|
137
133
|
};
|
|
138
134
|
const verifyDefinitionSignatures = (clusterDefinition, definitionType) => {
|
|
139
|
-
if (definitionType ===
|
|
135
|
+
if (definitionType === constants_js_1.DefinitionFlow.Charon) {
|
|
140
136
|
return true;
|
|
141
137
|
}
|
|
142
138
|
else {
|
|
@@ -144,10 +140,10 @@ const verifyDefinitionSignatures = (clusterDefinition, definitionType) => {
|
|
|
144
140
|
if (configSigner !== clusterDefinition.creator.address.toLowerCase()) {
|
|
145
141
|
return false;
|
|
146
142
|
}
|
|
147
|
-
if (definitionType ===
|
|
143
|
+
if (definitionType === constants_js_1.DefinitionFlow.Solo) {
|
|
148
144
|
return true;
|
|
149
145
|
}
|
|
150
|
-
return clusterDefinition.operators.every(operator => {
|
|
146
|
+
return clusterDefinition.operators.every((operator) => {
|
|
151
147
|
const configSigner = getPUTConfigHashSigner(operator.config_signature, clusterDefinition.config_hash, types_js_1.FORK_MAPPING[clusterDefinition.fork_version]);
|
|
152
148
|
const enrSigner = getEnrSigner(operator.enr_signature, operator.enr, types_js_1.FORK_MAPPING[clusterDefinition.fork_version]);
|
|
153
149
|
if (configSigner !== operator.address.toLowerCase() ||
|
|
@@ -158,36 +154,35 @@ const verifyDefinitionSignatures = (clusterDefinition, definitionType) => {
|
|
|
158
154
|
});
|
|
159
155
|
}
|
|
160
156
|
};
|
|
161
|
-
//cluster-lock data verification
|
|
157
|
+
// cluster-lock data verification
|
|
162
158
|
const computeSigningRoot = (sszObjectRoot, domain) => {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return Buffer.from(signingRootType.hashTreeRoot(
|
|
159
|
+
const signingRootDefaultValue = sszTypes_js_1.signingRootType.defaultValue();
|
|
160
|
+
signingRootDefaultValue.objectRoot = sszObjectRoot;
|
|
161
|
+
signingRootDefaultValue.domain = domain;
|
|
162
|
+
return Buffer.from(sszTypes_js_1.signingRootType.hashTreeRoot(signingRootDefaultValue).buffer);
|
|
167
163
|
};
|
|
168
164
|
const computeDepositMsgRoot = (msg) => {
|
|
169
|
-
const depositMsgVal = depositMessageType.defaultValue();
|
|
165
|
+
const depositMsgVal = sszTypes_js_1.depositMessageType.defaultValue();
|
|
170
166
|
depositMsgVal.pubkey = (0, ssz_1.fromHexString)(msg.pubkey);
|
|
171
167
|
depositMsgVal.withdrawal_credentials = (0, ssz_1.fromHexString)(msg.withdrawal_credentials);
|
|
172
168
|
depositMsgVal.amount = parseInt(msg.amount);
|
|
173
|
-
return Buffer.from(depositMessageType.hashTreeRoot(depositMsgVal).buffer);
|
|
169
|
+
return Buffer.from(sszTypes_js_1.depositMessageType.hashTreeRoot(depositMsgVal).buffer);
|
|
174
170
|
};
|
|
175
171
|
const computeForkDataRoot = (currentVersion, genesisValidatorsRoot) => {
|
|
176
|
-
const forkDataVal = forkDataType.defaultValue();
|
|
172
|
+
const forkDataVal = sszTypes_js_1.forkDataType.defaultValue();
|
|
177
173
|
forkDataVal.currentVersion = currentVersion;
|
|
178
174
|
forkDataVal.genesisValidatorsRoot = genesisValidatorsRoot;
|
|
179
|
-
return Buffer.from(forkDataType.hashTreeRoot(forkDataVal).buffer);
|
|
175
|
+
return Buffer.from(sszTypes_js_1.forkDataType.hashTreeRoot(forkDataVal).buffer);
|
|
180
176
|
};
|
|
181
177
|
const computebuilderRegistrationMsgRoot = (msg) => {
|
|
182
|
-
const builderRegistrationMsgVal = builderRegistrationMessageType.defaultValue();
|
|
178
|
+
const builderRegistrationMsgVal = sszTypes_js_1.builderRegistrationMessageType.defaultValue();
|
|
183
179
|
builderRegistrationMsgVal.fee_recipient = (0, ssz_1.fromHexString)(msg.fee_recipient);
|
|
184
180
|
builderRegistrationMsgVal.gas_limit = msg.gas_limit;
|
|
185
181
|
builderRegistrationMsgVal.timestamp = msg.timestamp;
|
|
186
182
|
builderRegistrationMsgVal.pubkey = (0, ssz_1.fromHexString)(msg.pubkey);
|
|
187
|
-
return Buffer.from(builderRegistrationMessageType.hashTreeRoot(builderRegistrationMsgVal)
|
|
183
|
+
return Buffer.from(sszTypes_js_1.builderRegistrationMessageType.hashTreeRoot(builderRegistrationMsgVal)
|
|
188
184
|
.buffer);
|
|
189
185
|
};
|
|
190
|
-
// DV data verification
|
|
191
186
|
const computeDomain = (domainType, lockForkVersion, genesisValidatorsRoot = (0, ssz_1.fromHexString)(constants_js_1.GENESIS_VALIDATOR_ROOT)) => {
|
|
192
187
|
const forkVersion = (0, ssz_1.fromHexString)(lockForkVersion.substring(2, lockForkVersion.length));
|
|
193
188
|
const forkDataRoot = computeForkDataRoot(forkVersion, genesisValidatorsRoot);
|
|
@@ -196,121 +191,99 @@ const computeDomain = (domainType, lockForkVersion, genesisValidatorsRoot = (0,
|
|
|
196
191
|
domain.set(forkDataRoot.subarray(0, 28), 4);
|
|
197
192
|
return domain;
|
|
198
193
|
};
|
|
199
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Verify deposit data withdrawal credintials and signature
|
|
196
|
+
* @param {string} forkVersion - fork version in definition file.
|
|
197
|
+
* @param {DistributedValidatorDto} validator - distributed validator.
|
|
198
|
+
* @param {string} withdrawalAddress - withdrawal address in definition file.
|
|
199
|
+
* @returns {boolean} - return if deposit data is valid.
|
|
200
|
+
*/
|
|
201
|
+
const verifyDepositData = (distributedPublicKey, depositData, withdrawalAddress, forkVersion) => {
|
|
202
|
+
const depositDomain = computeDomain((0, ssz_1.fromHexString)(constants_js_1.DOMAIN_DEPOSIT), forkVersion);
|
|
200
203
|
const eth1AddressWithdrawalPrefix = '0x01';
|
|
201
204
|
if (eth1AddressWithdrawalPrefix +
|
|
202
205
|
'0'.repeat(22) +
|
|
203
206
|
withdrawalAddress.toLowerCase().slice(2) !==
|
|
204
|
-
|
|
205
|
-
return false;
|
|
207
|
+
depositData.withdrawal_credentials) {
|
|
208
|
+
return { isValidDepositData: false, depositDataMsg: new Uint8Array(0) };
|
|
206
209
|
}
|
|
207
|
-
if (
|
|
208
|
-
return false;
|
|
210
|
+
if (distributedPublicKey !== depositData.pubkey) {
|
|
211
|
+
return { isValidDepositData: false, depositDataMsg: new Uint8Array(0) };
|
|
209
212
|
}
|
|
210
|
-
|
|
213
|
+
const depositMessageBuffer = computeDepositMsgRoot(depositData);
|
|
214
|
+
const depositDataMessage = (0, exports.signingRoot)(depositDomain, depositMessageBuffer);
|
|
215
|
+
return { isValidDepositData: true, depositDataMsg: depositDataMessage };
|
|
211
216
|
};
|
|
212
|
-
|
|
217
|
+
exports.verifyDepositData = verifyDepositData;
|
|
218
|
+
const verifyBuilderRegistration = (validator, feeRecipientAddress, forkVersion) => {
|
|
219
|
+
var _a;
|
|
220
|
+
const builderDomain = computeDomain((0, ssz_1.fromHexString)(constants_js_1.DOMAIN_APPLICATION_BUILDER), forkVersion);
|
|
213
221
|
if (validator.distributed_public_key !==
|
|
214
|
-
validator.builder_registration.message.pubkey) {
|
|
215
|
-
return false;
|
|
222
|
+
((_a = validator.builder_registration) === null || _a === void 0 ? void 0 : _a.message.pubkey)) {
|
|
223
|
+
return { isValidBuilderRegistration: false, builderRegistrationMsg: new Uint8Array(0) };
|
|
216
224
|
}
|
|
217
225
|
if (feeRecipientAddress.toLowerCase() !==
|
|
218
226
|
validator.builder_registration.message.fee_recipient.toLowerCase()) {
|
|
219
|
-
return false;
|
|
227
|
+
return { isValidBuilderRegistration: false, builderRegistrationMsg: new Uint8Array(0) };
|
|
228
|
+
}
|
|
229
|
+
const builderRegistrationMessageBuffer = computebuilderRegistrationMsgRoot(validator.builder_registration.message);
|
|
230
|
+
const builderRegistrationMessage = (0, exports.signingRoot)(builderDomain, builderRegistrationMessageBuffer);
|
|
231
|
+
return { isValidBuilderRegistration: true, builderRegistrationMsg: builderRegistrationMessage };
|
|
232
|
+
};
|
|
233
|
+
exports.verifyBuilderRegistration = verifyBuilderRegistration;
|
|
234
|
+
const verifyNodeSignatures = (clusterLock) => {
|
|
235
|
+
const ec = new elliptic_1.default.ec('secp256k1');
|
|
236
|
+
const nodeSignatures = clusterLock.node_signatures;
|
|
237
|
+
const lockHashWithout0x = (0, utils_js_1.hexWithout0x)(clusterLock.lock_hash);
|
|
238
|
+
// node(ENR) signatures
|
|
239
|
+
for (let i = 0; i < nodeSignatures.length; i++) {
|
|
240
|
+
const pubkey = discv5_1.ENR.decodeTxt(clusterLock.cluster_definition.operators[i].enr).publicKey.toString('hex');
|
|
241
|
+
const ENRsignature = {
|
|
242
|
+
r: nodeSignatures[i].slice(2, 66),
|
|
243
|
+
s: nodeSignatures[i].slice(66, 130),
|
|
244
|
+
};
|
|
245
|
+
const nodeSignatureVerification = ec
|
|
246
|
+
.keyFromPublic(pubkey, 'hex')
|
|
247
|
+
.verify(lockHashWithout0x, ENRsignature);
|
|
248
|
+
if (!nodeSignatureVerification) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
220
251
|
}
|
|
221
252
|
return true;
|
|
222
253
|
};
|
|
254
|
+
exports.verifyNodeSignatures = verifyNodeSignatures;
|
|
223
255
|
const signingRoot = (domain, messageBuffer) => {
|
|
224
256
|
return computeSigningRoot(messageBuffer, domain);
|
|
225
257
|
};
|
|
226
258
|
exports.signingRoot = signingRoot;
|
|
227
259
|
const verifyLockData = (clusterLock) => __awaiter(void 0, void 0, void 0, function* () {
|
|
228
|
-
const ec = new elliptic_1.default.ec('secp256k1');
|
|
229
|
-
const validators = clusterLock.distributed_validators;
|
|
230
|
-
const nodeSignatures = clusterLock.node_signatures;
|
|
231
|
-
const depositDomain = computeDomain((0, ssz_1.fromHexString)(constants_js_1.DOMAIN_DEPOSIT), clusterLock.cluster_definition.fork_version);
|
|
232
|
-
const builderDomain = computeDomain((0, ssz_1.fromHexString)(constants_js_1.DOMAIN_APPLICATION_BUILDER), clusterLock.cluster_definition.fork_version);
|
|
233
|
-
const pubShares = [];
|
|
234
|
-
const pubKeys = [];
|
|
235
|
-
const builderRegistrationAndDepositDataMessages = [];
|
|
236
|
-
const blsSignatures = [];
|
|
237
260
|
yield (0, bls_1.init)('herumi');
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const validatorPublicShares = validator['public_shares'];
|
|
241
|
-
for (const element of validatorPublicShares) {
|
|
242
|
-
pubShares.push((0, ssz_1.fromHexString)(element));
|
|
243
|
-
}
|
|
244
|
-
//Deposit data signature
|
|
245
|
-
if (semver.gte(clusterLock.cluster_definition.version, 'v1.6.0')) {
|
|
246
|
-
if (!verifyDepositData(validator, clusterLock.cluster_definition.validators[i].withdrawal_address)) {
|
|
247
|
-
return false;
|
|
248
|
-
}
|
|
249
|
-
const depositMessageBuffer = computeDepositMsgRoot(validator.deposit_data);
|
|
250
|
-
const depositDataMessage = (0, exports.signingRoot)(depositDomain, depositMessageBuffer);
|
|
251
|
-
pubKeys.push((0, ssz_1.fromHexString)(validator.distributed_public_key));
|
|
252
|
-
builderRegistrationAndDepositDataMessages.push(depositDataMessage);
|
|
253
|
-
blsSignatures.push((0, ssz_1.fromHexString)(validator.deposit_data.signature));
|
|
254
|
-
}
|
|
255
|
-
//Builder registration signature
|
|
256
|
-
if (semver.gte(clusterLock.cluster_definition.version, 'v1.7.0')) {
|
|
257
|
-
if (!verifyBuilderRegistration(validator, clusterLock.cluster_definition.validators[i].fee_recipient_address)) {
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
const builderRegistrationMessageBuffer = computebuilderRegistrationMsgRoot(validator.builder_registration.message);
|
|
261
|
-
const builderRegistrationMessage = (0, exports.signingRoot)(builderDomain, builderRegistrationMessageBuffer);
|
|
262
|
-
pubKeys.push((0, ssz_1.fromHexString)(validator.distributed_public_key));
|
|
263
|
-
builderRegistrationAndDepositDataMessages.push(builderRegistrationMessage);
|
|
264
|
-
blsSignatures.push((0, ssz_1.fromHexString)(validator.builder_registration.signature));
|
|
265
|
-
}
|
|
261
|
+
if (semver.eq(clusterLock.cluster_definition.version, 'v1.6.0')) {
|
|
262
|
+
return (0, v1_6_0_js_1.verifyDVV1X6)(clusterLock);
|
|
266
263
|
}
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
builderRegistrationAndDepositDataMessages.length) {
|
|
270
|
-
// verify all deposit data and builder registration bls signatures
|
|
271
|
-
const aggregateBLSSignature = (0, bls_1.aggregateSignatures)(blsSignatures);
|
|
272
|
-
if (!(0, bls_1.verifyMultiple)(pubKeys, builderRegistrationAndDepositDataMessages, aggregateBLSSignature)) {
|
|
273
|
-
return false;
|
|
274
|
-
}
|
|
264
|
+
if (semver.eq(clusterLock.cluster_definition.version, 'v1.7.0')) {
|
|
265
|
+
return (0, v1_7_0_js_1.verifyDVV1X7)(clusterLock);
|
|
275
266
|
}
|
|
276
|
-
if (semver.
|
|
277
|
-
|
|
278
|
-
//node(ENR) signatures
|
|
279
|
-
for (let i = 0; i < nodeSignatures.length; i++) {
|
|
280
|
-
const pubkey = discv5_1.ENR.decodeTxt(clusterLock.cluster_definition.operators[i].enr).publicKey.toString('hex');
|
|
281
|
-
const ENRsignature = {
|
|
282
|
-
r: nodeSignatures[i].slice(2, 66),
|
|
283
|
-
s: nodeSignatures[i].slice(66, 130),
|
|
284
|
-
};
|
|
285
|
-
const nodeSignatureVerification = ec
|
|
286
|
-
.keyFromPublic(pubkey, 'hex')
|
|
287
|
-
.verify(lockHashWithout0x, ENRsignature);
|
|
288
|
-
if (!nodeSignatureVerification) {
|
|
289
|
-
return false;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
267
|
+
if (semver.eq(clusterLock.cluster_definition.version, 'v1.8.0')) {
|
|
268
|
+
return (0, v1_8_0_js_1.verifyDVV1X8)(clusterLock);
|
|
292
269
|
}
|
|
293
|
-
|
|
294
|
-
if (!(0, bls_1.verifyAggregate)(pubShares, (0, ssz_1.fromHexString)(clusterLock.lock_hash), (0, ssz_1.fromHexString)(clusterLock.signature_aggregate))) {
|
|
295
|
-
return false;
|
|
296
|
-
}
|
|
297
|
-
return true;
|
|
270
|
+
return false;
|
|
298
271
|
});
|
|
299
272
|
const isValidClusterLock = (clusterLock) => __awaiter(void 0, void 0, void 0, function* () {
|
|
300
273
|
try {
|
|
301
|
-
const definitionType = definitionFlow(clusterLock.cluster_definition);
|
|
302
|
-
if (
|
|
274
|
+
const definitionType = (0, utils_js_1.definitionFlow)(clusterLock.cluster_definition);
|
|
275
|
+
if (definitionType == null) {
|
|
303
276
|
return false;
|
|
304
277
|
}
|
|
305
278
|
const isValidDefinitionData = verifyDefinitionSignatures(clusterLock.cluster_definition, definitionType);
|
|
306
279
|
if (!isValidDefinitionData) {
|
|
307
280
|
return false;
|
|
308
281
|
}
|
|
309
|
-
if ((0,
|
|
282
|
+
if ((0, exports.clusterConfigOrDefinitionHash)(clusterLock.cluster_definition, false) !==
|
|
310
283
|
clusterLock.cluster_definition.definition_hash) {
|
|
311
284
|
return false;
|
|
312
285
|
}
|
|
313
|
-
if ((0,
|
|
286
|
+
if ((0, exports.clusterLockHash)(clusterLock) !== clusterLock.lock_hash) {
|
|
314
287
|
return false;
|
|
315
288
|
}
|
|
316
289
|
const isValidLockData = yield verifyLockData(clusterLock);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.signingRootType = exports.depositMessageType = exports.forkDataType = exports.builderRegistrationMessageType = exports.builderRegistrationContainer = exports.builderRegistrationMessageContainer = exports.depositDataContainer = exports.newOperatorContainerType = exports.newCreatorContainerType = exports.validatorsContainerType = exports.creatorContainerType = exports.operatorContainerType = exports.creatorAddressWrapperType = exports.operatorAddressWrapperType = void 0;
|
|
4
|
+
const ssz_1 = require("@chainsafe/ssz");
|
|
5
|
+
exports.operatorAddressWrapperType = new ssz_1.ContainerType({
|
|
6
|
+
address: new ssz_1.ByteVectorType(20),
|
|
7
|
+
});
|
|
8
|
+
exports.creatorAddressWrapperType = new ssz_1.ContainerType({
|
|
9
|
+
address: new ssz_1.ByteVectorType(20),
|
|
10
|
+
});
|
|
11
|
+
exports.operatorContainerType = new ssz_1.ContainerType({
|
|
12
|
+
address: new ssz_1.ByteVectorType(20),
|
|
13
|
+
enr: new ssz_1.ByteListType(1024), // This needs to be dynamic, since ENRs do not have a fixed length.
|
|
14
|
+
config_signature: new ssz_1.ByteVectorType(65),
|
|
15
|
+
enr_signature: new ssz_1.ByteVectorType(65),
|
|
16
|
+
});
|
|
17
|
+
exports.creatorContainerType = new ssz_1.ContainerType({
|
|
18
|
+
address: new ssz_1.ByteVectorType(20),
|
|
19
|
+
config_signature: new ssz_1.ByteVectorType(65),
|
|
20
|
+
});
|
|
21
|
+
exports.validatorsContainerType = new ssz_1.ContainerType({
|
|
22
|
+
fee_recipient_address: new ssz_1.ByteVectorType(20),
|
|
23
|
+
withdrawal_address: new ssz_1.ByteVectorType(20),
|
|
24
|
+
});
|
|
25
|
+
const newCreatorContainerType = (configOnly) => {
|
|
26
|
+
return configOnly ? exports.creatorAddressWrapperType : exports.creatorContainerType;
|
|
27
|
+
};
|
|
28
|
+
exports.newCreatorContainerType = newCreatorContainerType;
|
|
29
|
+
const newOperatorContainerType = (configOnly) => {
|
|
30
|
+
return configOnly ? exports.operatorAddressWrapperType : exports.operatorContainerType;
|
|
31
|
+
};
|
|
32
|
+
exports.newOperatorContainerType = newOperatorContainerType;
|
|
33
|
+
// Lock
|
|
34
|
+
exports.depositDataContainer = new ssz_1.ContainerType({
|
|
35
|
+
pubkey: new ssz_1.ByteVectorType(48),
|
|
36
|
+
withdrawal_credentials: new ssz_1.ByteVectorType(32),
|
|
37
|
+
amount: new ssz_1.UintNumberType(8),
|
|
38
|
+
signature: new ssz_1.ByteVectorType(96),
|
|
39
|
+
});
|
|
40
|
+
exports.builderRegistrationMessageContainer = new ssz_1.ContainerType({
|
|
41
|
+
fee_recipient: new ssz_1.ByteVectorType(20),
|
|
42
|
+
gas_limit: new ssz_1.UintNumberType(8),
|
|
43
|
+
timestamp: new ssz_1.UintNumberType(8),
|
|
44
|
+
pubkey: new ssz_1.ByteVectorType(48),
|
|
45
|
+
});
|
|
46
|
+
exports.builderRegistrationContainer = new ssz_1.ContainerType({
|
|
47
|
+
message: exports.builderRegistrationMessageContainer,
|
|
48
|
+
signature: new ssz_1.ByteVectorType(96),
|
|
49
|
+
});
|
|
50
|
+
exports.builderRegistrationMessageType = new ssz_1.ContainerType({
|
|
51
|
+
fee_recipient: new ssz_1.ByteVectorType(20),
|
|
52
|
+
gas_limit: new ssz_1.UintNumberType(8),
|
|
53
|
+
timestamp: new ssz_1.UintNumberType(8),
|
|
54
|
+
pubkey: new ssz_1.ByteVectorType(48),
|
|
55
|
+
});
|
|
56
|
+
// For domain computation that is used in deposit data and builder registration verification for dv
|
|
57
|
+
exports.forkDataType = new ssz_1.ContainerType({
|
|
58
|
+
currentVersion: new ssz_1.ByteVectorType(4),
|
|
59
|
+
genesisValidatorsRoot: new ssz_1.ByteVectorType(32),
|
|
60
|
+
});
|
|
61
|
+
exports.depositMessageType = new ssz_1.ContainerType({
|
|
62
|
+
pubkey: new ssz_1.ByteVectorType(48),
|
|
63
|
+
withdrawal_credentials: new ssz_1.ByteVectorType(32),
|
|
64
|
+
amount: new ssz_1.UintNumberType(8),
|
|
65
|
+
});
|
|
66
|
+
exports.signingRootType = new ssz_1.ContainerType({
|
|
67
|
+
objectRoot: new ssz_1.ByteVectorType(32),
|
|
68
|
+
domain: new ssz_1.ByteVectorType(32),
|
|
69
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyDVV1X6 = exports.hashClusterLockV1X6 = exports.hashClusterDefinitionV1X6 = exports.clusterDefinitionContainerTypeV1X6 = void 0;
|
|
4
|
+
const uint_1 = require("@chainsafe/ssz/lib/type/uint");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
const sszTypes_1 = require("./sszTypes");
|
|
7
|
+
const ssz_1 = require("@chainsafe/ssz");
|
|
8
|
+
const common_1 = require("./common");
|
|
9
|
+
const bls_1 = require("@chainsafe/bls");
|
|
10
|
+
/**
|
|
11
|
+
* Returns the containerized cluster definition
|
|
12
|
+
* @param cluster ClusterDefinition to calculate the type from
|
|
13
|
+
* @returns SSZ Containerized type of cluster input
|
|
14
|
+
*/
|
|
15
|
+
const clusterDefinitionContainerTypeV1X6 = (configOnly) => {
|
|
16
|
+
let returnedContainerType = {
|
|
17
|
+
uuid: new ssz_1.ByteListType(64),
|
|
18
|
+
name: new ssz_1.ByteListType(256),
|
|
19
|
+
version: new ssz_1.ByteListType(16),
|
|
20
|
+
timestamp: new ssz_1.ByteListType(32),
|
|
21
|
+
num_validators: new uint_1.UintNumberType(8),
|
|
22
|
+
threshold: new uint_1.UintNumberType(8),
|
|
23
|
+
dkg_algorithm: new ssz_1.ByteListType(32),
|
|
24
|
+
fork_version: new ssz_1.ByteVectorType(4),
|
|
25
|
+
operators: new ssz_1.ListCompositeType((0, sszTypes_1.newOperatorContainerType)(configOnly), 256),
|
|
26
|
+
creator: (0, sszTypes_1.newCreatorContainerType)(configOnly),
|
|
27
|
+
validators: new ssz_1.ListCompositeType(sszTypes_1.validatorsContainerType, 65536),
|
|
28
|
+
};
|
|
29
|
+
if (!configOnly) {
|
|
30
|
+
returnedContainerType = Object.assign(Object.assign({}, returnedContainerType), { config_hash: new ssz_1.ByteVectorType(32) });
|
|
31
|
+
}
|
|
32
|
+
return new ssz_1.ContainerType(returnedContainerType);
|
|
33
|
+
};
|
|
34
|
+
exports.clusterDefinitionContainerTypeV1X6 = clusterDefinitionContainerTypeV1X6;
|
|
35
|
+
const hashClusterDefinitionV1X6 = (cluster, configOnly) => {
|
|
36
|
+
const definitionType = (0, exports.clusterDefinitionContainerTypeV1X6)(configOnly);
|
|
37
|
+
const val = definitionType.defaultValue();
|
|
38
|
+
// order should be same as charon https://github.com/ObolNetwork/charon/blob/main/cluster/ssz.go#L276
|
|
39
|
+
val.uuid = (0, utils_1.strToUint8Array)(cluster.uuid);
|
|
40
|
+
val.name = (0, utils_1.strToUint8Array)(cluster.name);
|
|
41
|
+
val.version = (0, utils_1.strToUint8Array)(cluster.version);
|
|
42
|
+
val.timestamp = (0, utils_1.strToUint8Array)(cluster.timestamp);
|
|
43
|
+
val.num_validators = cluster.num_validators;
|
|
44
|
+
val.threshold = cluster.threshold;
|
|
45
|
+
val.dkg_algorithm = (0, utils_1.strToUint8Array)(cluster.dkg_algorithm);
|
|
46
|
+
val.fork_version = (0, ssz_1.fromHexString)(cluster.fork_version);
|
|
47
|
+
val.operators = cluster.operators.map(operator => {
|
|
48
|
+
return configOnly
|
|
49
|
+
? { address: (0, ssz_1.fromHexString)(operator.address) }
|
|
50
|
+
: {
|
|
51
|
+
address: (0, ssz_1.fromHexString)(operator.address),
|
|
52
|
+
enr: (0, utils_1.strToUint8Array)(operator.enr),
|
|
53
|
+
config_signature: (0, ssz_1.fromHexString)(operator.config_signature),
|
|
54
|
+
enr_signature: (0, ssz_1.fromHexString)(operator.enr_signature),
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
val.creator = configOnly
|
|
58
|
+
? { address: (0, ssz_1.fromHexString)(cluster.creator.address) }
|
|
59
|
+
: {
|
|
60
|
+
address: (0, ssz_1.fromHexString)(cluster.creator.address),
|
|
61
|
+
config_signature: (0, ssz_1.fromHexString)(cluster.creator.config_signature),
|
|
62
|
+
};
|
|
63
|
+
val.validators = cluster.validators.map((validator) => {
|
|
64
|
+
return {
|
|
65
|
+
fee_recipient_address: (0, ssz_1.fromHexString)(validator.fee_recipient_address),
|
|
66
|
+
withdrawal_address: (0, ssz_1.fromHexString)(validator.withdrawal_address),
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
if (!configOnly) {
|
|
70
|
+
val.config_hash = (0, ssz_1.fromHexString)(cluster.config_hash);
|
|
71
|
+
}
|
|
72
|
+
return val;
|
|
73
|
+
};
|
|
74
|
+
exports.hashClusterDefinitionV1X6 = hashClusterDefinitionV1X6;
|
|
75
|
+
// cluster lock
|
|
76
|
+
const dvContainerTypeV1X6 = new ssz_1.ContainerType({
|
|
77
|
+
distributed_public_key: new ssz_1.ByteVectorType(48),
|
|
78
|
+
public_shares: new ssz_1.ListCompositeType(new ssz_1.ByteVectorType(48), 256),
|
|
79
|
+
pubkey: new ssz_1.ByteVectorType(48),
|
|
80
|
+
withdrawal_credentials: new ssz_1.ByteVectorType(32),
|
|
81
|
+
amount: new uint_1.UintNumberType(8),
|
|
82
|
+
signature: new ssz_1.ByteVectorType(96),
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* @returns SSZ Containerized type of cluster lock
|
|
86
|
+
*/
|
|
87
|
+
const clusterLockContainerTypeV1X6 = () => {
|
|
88
|
+
return new ssz_1.ContainerType({
|
|
89
|
+
cluster_definition: (0, exports.clusterDefinitionContainerTypeV1X6)(false),
|
|
90
|
+
distributed_validators: new ssz_1.ListCompositeType(dvContainerTypeV1X6, 65536),
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* @param cluster The published cluster lock
|
|
95
|
+
* @returns The lock hash in of the corresponding cluster
|
|
96
|
+
*/
|
|
97
|
+
const hashClusterLockV1X6 = (cluster) => {
|
|
98
|
+
const lockType = clusterLockContainerTypeV1X6();
|
|
99
|
+
const val = lockType.defaultValue();
|
|
100
|
+
// Check if we can replace with definition_hash
|
|
101
|
+
val.cluster_definition = (0, exports.hashClusterDefinitionV1X6)(cluster.cluster_definition, false);
|
|
102
|
+
val.distributed_validators = cluster.distributed_validators.map(dValidator => {
|
|
103
|
+
var _a, _b, _c, _d;
|
|
104
|
+
return {
|
|
105
|
+
distributed_public_key: (0, ssz_1.fromHexString)(dValidator.distributed_public_key),
|
|
106
|
+
public_shares: dValidator.public_shares.map(publicShare => (0, ssz_1.fromHexString)(publicShare)),
|
|
107
|
+
pubkey: (0, ssz_1.fromHexString)((_a = dValidator.deposit_data) === null || _a === void 0 ? void 0 : _a.pubkey),
|
|
108
|
+
withdrawal_credentials: (0, ssz_1.fromHexString)((_b = dValidator.deposit_data) === null || _b === void 0 ? void 0 : _b.withdrawal_credentials),
|
|
109
|
+
amount: parseInt((_c = dValidator.deposit_data) === null || _c === void 0 ? void 0 : _c.amount),
|
|
110
|
+
signature: (0, ssz_1.fromHexString)((_d = dValidator.deposit_data) === null || _d === void 0 ? void 0 : _d.signature),
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
return '0x' + Buffer.from(lockType.hashTreeRoot(val).buffer).toString('hex');
|
|
114
|
+
};
|
|
115
|
+
exports.hashClusterLockV1X6 = hashClusterLockV1X6;
|
|
116
|
+
// DV verification
|
|
117
|
+
const verifyDVV1X6 = (clusterLock) => {
|
|
118
|
+
var _a;
|
|
119
|
+
const validators = clusterLock.distributed_validators;
|
|
120
|
+
const pubShares = [];
|
|
121
|
+
const pubKeys = [];
|
|
122
|
+
const builderRegistrationAndDepositDataMessages = [];
|
|
123
|
+
const blsSignatures = [];
|
|
124
|
+
for (let i = 0; i < validators.length; i++) {
|
|
125
|
+
const validator = validators[i];
|
|
126
|
+
const validatorPublicShares = validator.public_shares;
|
|
127
|
+
const distributedPublicKey = validator.distributed_public_key;
|
|
128
|
+
// Needed in signature_aggregate verification
|
|
129
|
+
for (const element of validatorPublicShares) {
|
|
130
|
+
pubShares.push((0, ssz_1.fromHexString)(element));
|
|
131
|
+
}
|
|
132
|
+
const { isValidDepositData, depositDataMsg } = (0, common_1.verifyDepositData)(distributedPublicKey, validator.deposit_data, clusterLock.cluster_definition.validators[i].withdrawal_address, clusterLock.cluster_definition.fork_version);
|
|
133
|
+
if (!isValidDepositData) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
pubKeys.push((0, ssz_1.fromHexString)(validator.distributed_public_key));
|
|
137
|
+
builderRegistrationAndDepositDataMessages.push(depositDataMsg);
|
|
138
|
+
blsSignatures.push((0, ssz_1.fromHexString)((_a = validator.deposit_data) === null || _a === void 0 ? void 0 : _a.signature));
|
|
139
|
+
}
|
|
140
|
+
const aggregateBLSSignature = (0, bls_1.aggregateSignatures)(blsSignatures);
|
|
141
|
+
if (!(0, bls_1.verifyMultiple)(pubKeys, builderRegistrationAndDepositDataMessages, aggregateBLSSignature)) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
if (!(0, bls_1.verifyAggregate)(pubShares, (0, ssz_1.fromHexString)(clusterLock.lock_hash), (0, ssz_1.fromHexString)(clusterLock.signature_aggregate))) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
};
|
|
149
|
+
exports.verifyDVV1X6 = verifyDVV1X6;
|