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