@obolnetwork/obol-sdk 1.0.16 → 1.0.17

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 (48) hide show
  1. package/README.md +6 -3
  2. package/dist/cjs/package.json +11 -6
  3. package/dist/cjs/src/ajv.js +27 -0
  4. package/dist/cjs/src/constants.js +8 -4
  5. package/dist/cjs/src/index.js +10 -9
  6. package/dist/cjs/src/schema.js +8 -0
  7. package/dist/cjs/src/utils.js +3 -3
  8. package/dist/cjs/src/verification/common.js +19 -7
  9. package/dist/cjs/src/verification/termsAndConditions.js +1 -1
  10. package/dist/cjs/src/verification/v1.6.0.js +1 -1
  11. package/dist/cjs/src/verification/v1.7.0.js +3 -3
  12. package/dist/cjs/src/verification/v1.8.0.js +3 -3
  13. package/dist/cjs/test/fixtures.js +27 -33
  14. package/dist/cjs/test/methods.test.js +54 -27
  15. package/dist/esm/package.json +11 -6
  16. package/dist/esm/src/ajv.js +27 -0
  17. package/dist/esm/src/base.js +1 -1
  18. package/dist/esm/src/constants.js +7 -3
  19. package/dist/esm/src/index.js +11 -10
  20. package/dist/esm/src/schema.js +8 -0
  21. package/dist/esm/src/utils.js +3 -3
  22. package/dist/esm/src/verification/common.js +27 -15
  23. package/dist/esm/src/verification/sszTypes.js +1 -1
  24. package/dist/esm/src/verification/termsAndConditions.js +1 -1
  25. package/dist/esm/src/verification/v1.6.0.js +5 -5
  26. package/dist/esm/src/verification/v1.7.0.js +8 -8
  27. package/dist/esm/src/verification/v1.8.0.js +8 -8
  28. package/dist/esm/test/fixtures.js +27 -33
  29. package/dist/esm/test/methods.test.js +55 -28
  30. package/dist/types/src/constants.d.ts +3 -1
  31. package/dist/types/src/schema.d.ts +8 -0
  32. package/dist/types/src/types.d.ts +18 -18
  33. package/package.json +12 -7
  34. package/src/ajv.ts +38 -7
  35. package/src/base.ts +22 -18
  36. package/src/constants.ts +42 -36
  37. package/src/errors.ts +4 -4
  38. package/src/index.ts +85 -75
  39. package/src/schema.ts +10 -2
  40. package/src/services.ts +6 -6
  41. package/src/types.ts +65 -65
  42. package/src/utils.ts +17 -17
  43. package/src/verification/common.ts +374 -333
  44. package/src/verification/sszTypes.ts +60 -51
  45. package/src/verification/termsAndConditions.ts +16 -14
  46. package/src/verification/v1.6.0.ts +214 -184
  47. package/src/verification/v1.7.0.ts +268 -233
  48. package/src/verification/v1.8.0.ts +266 -225
@@ -1,22 +1,53 @@
1
+ import { fromHexString } from '@chainsafe/ssz';
2
+ import elliptic from 'elliptic';
3
+ import { init } from '@chainsafe/bls';
4
+
5
+ import {
6
+ FORK_MAPPING,
7
+ type ClusterDefinition,
8
+ type ClusterLock,
9
+ type DepositData,
10
+ type BuilderRegistrationMessage,
11
+ type DistributedValidator,
12
+ } from '../types.js';
13
+ import * as semver from 'semver';
14
+ import {
15
+ clusterDefinitionContainerTypeV1X6,
16
+ hashClusterDefinitionV1X6,
17
+ hashClusterLockV1X6,
18
+ verifyDVV1X6,
19
+ } from './v1.6.0.js';
1
20
  import {
2
- fromHexString,
3
- } from '@chainsafe/ssz'
4
- import elliptic from 'elliptic'
21
+ clusterDefinitionContainerTypeV1X7,
22
+ hashClusterDefinitionV1X7,
23
+ hashClusterLockV1X7,
24
+ verifyDVV1X7,
25
+ } from './v1.7.0.js';
26
+ import { ethers } from 'ethers';
5
27
  import {
6
- init,
7
- } from '@chainsafe/bls'
8
-
9
- import { FORK_MAPPING, type ClusterDefinition, type ClusterLock, type DepositData, type BuilderRegistrationMessage, type DistributedValidator } from '../types.js'
10
- import * as semver from 'semver'
11
- import { clusterDefinitionContainerTypeV1X6, hashClusterDefinitionV1X6, hashClusterLockV1X6, verifyDVV1X6 } from './v1.6.0.js'
12
- import { clusterDefinitionContainerTypeV1X7, hashClusterDefinitionV1X7, hashClusterLockV1X7, verifyDVV1X7 } from './v1.7.0.js'
13
- import { ethers } from 'ethers'
14
- import { DOMAIN_APPLICATION_BUILDER, DOMAIN_DEPOSIT, DefinitionFlow, GENESIS_VALIDATOR_ROOT, signCreatorConfigHashPayload, signEnrPayload, signOperatorConfigHashPayload } from '../constants.js'
15
- import { SignTypedDataVersion, TypedDataUtils } from '@metamask/eth-sig-util'
16
- import { builderRegistrationMessageType, depositMessageType, forkDataType, signingRootType } from './sszTypes.js'
17
- import { definitionFlow, hexWithout0x } from '../utils.js'
18
- import { ENR } from '@chainsafe/discv5'
19
- import { clusterDefinitionContainerTypeV1X8, hashClusterDefinitionV1X8, hashClusterLockV1X8, verifyDVV1X8 } from './v1.8.0.js'
28
+ DOMAIN_APPLICATION_BUILDER,
29
+ DOMAIN_DEPOSIT,
30
+ DefinitionFlow,
31
+ GENESIS_VALIDATOR_ROOT,
32
+ signCreatorConfigHashPayload,
33
+ signEnrPayload,
34
+ signOperatorConfigHashPayload,
35
+ } from '../constants.js';
36
+ import { SignTypedDataVersion, TypedDataUtils } from '@metamask/eth-sig-util';
37
+ import {
38
+ builderRegistrationMessageType,
39
+ depositMessageType,
40
+ forkDataType,
41
+ signingRootType,
42
+ } from './sszTypes.js';
43
+ import { definitionFlow, hexWithout0x } from '../utils.js';
44
+ import { ENR } from '@chainsafe/discv5';
45
+ import {
46
+ clusterDefinitionContainerTypeV1X8,
47
+ hashClusterDefinitionV1X8,
48
+ hashClusterLockV1X8,
49
+ verifyDVV1X8,
50
+ } from './v1.8.0.js';
20
51
 
21
52
  // cluster-definition hash
22
53
 
@@ -26,37 +57,40 @@ import { clusterDefinitionContainerTypeV1X8, hashClusterDefinitionV1X8, hashClus
26
57
  * @returns The config hash or the definition hash in of the corresponding cluster
27
58
  */
28
59
  export const clusterConfigOrDefinitionHash = (
29
- cluster: ClusterDefinition,
30
- configOnly: boolean,
60
+ cluster: ClusterDefinition,
61
+ configOnly: boolean,
31
62
  ): string => {
32
- let definitionType, val
33
-
34
- if (semver.eq(cluster.version, 'v1.6.0')) {
35
- definitionType = clusterDefinitionContainerTypeV1X6(configOnly)
36
- val = hashClusterDefinitionV1X6(cluster, configOnly)
37
- return (
38
- '0x' + Buffer.from(definitionType.hashTreeRoot(val).buffer).toString('hex')
39
- )
40
- }
41
-
42
- if (semver.eq(cluster.version, 'v1.7.0')) {
43
- definitionType = clusterDefinitionContainerTypeV1X7(configOnly)
44
- val = hashClusterDefinitionV1X7(cluster, configOnly)
45
- return (
46
- '0x' + Buffer.from(definitionType.hashTreeRoot(val).buffer).toString('hex')
47
- )
48
- }
49
-
50
- if (semver.eq(cluster.version, 'v1.8.0')) {
51
- definitionType = clusterDefinitionContainerTypeV1X8(configOnly)
52
- val = hashClusterDefinitionV1X8(cluster, configOnly)
53
- return (
54
- '0x' + Buffer.from(definitionType.hashTreeRoot(val).buffer).toString('hex')
55
- )
56
- }
57
-
58
- throw new Error('unsupported version')
59
- }
63
+ let definitionType, val;
64
+
65
+ if (semver.eq(cluster.version, 'v1.6.0')) {
66
+ definitionType = clusterDefinitionContainerTypeV1X6(configOnly);
67
+ val = hashClusterDefinitionV1X6(cluster, configOnly);
68
+ return (
69
+ '0x' +
70
+ Buffer.from(definitionType.hashTreeRoot(val).buffer).toString('hex')
71
+ );
72
+ }
73
+
74
+ if (semver.eq(cluster.version, 'v1.7.0')) {
75
+ definitionType = clusterDefinitionContainerTypeV1X7(configOnly);
76
+ val = hashClusterDefinitionV1X7(cluster, configOnly);
77
+ return (
78
+ '0x' +
79
+ Buffer.from(definitionType.hashTreeRoot(val).buffer).toString('hex')
80
+ );
81
+ }
82
+
83
+ if (semver.eq(cluster.version, 'v1.8.0')) {
84
+ definitionType = clusterDefinitionContainerTypeV1X8(configOnly);
85
+ val = hashClusterDefinitionV1X8(cluster, configOnly);
86
+ return (
87
+ '0x' +
88
+ Buffer.from(definitionType.hashTreeRoot(val).buffer).toString('hex')
89
+ );
90
+ }
91
+
92
+ throw new Error('unsupported version');
93
+ };
60
94
 
61
95
  // cluster-lock hash
62
96
 
@@ -66,191 +100,193 @@ export const clusterConfigOrDefinitionHash = (
66
100
  * @returns The cluster lock hash in of the corresponding cluster lock
67
101
  */
68
102
  export const clusterLockHash = (clusterLock: ClusterLock): string => {
69
- if (semver.eq(clusterLock.cluster_definition.version, 'v1.6.0')) {
70
- return hashClusterLockV1X6(clusterLock)
71
- }
103
+ if (semver.eq(clusterLock.cluster_definition.version, 'v1.6.0')) {
104
+ return hashClusterLockV1X6(clusterLock);
105
+ }
72
106
 
73
- if (semver.eq(clusterLock.cluster_definition.version, 'v1.7.0')) {
74
- return hashClusterLockV1X7(clusterLock)
75
- }
107
+ if (semver.eq(clusterLock.cluster_definition.version, 'v1.7.0')) {
108
+ return hashClusterLockV1X7(clusterLock);
109
+ }
76
110
 
77
- if (semver.eq(clusterLock.cluster_definition.version, 'v1.8.0')) {
78
- return hashClusterLockV1X8(clusterLock)
79
- }
111
+ if (semver.eq(clusterLock.cluster_definition.version, 'v1.8.0')) {
112
+ return hashClusterLockV1X8(clusterLock);
113
+ }
80
114
 
81
- // other versions
82
- throw new Error('unsupported version')
83
- }
115
+ // other versions
116
+ throw new Error('unsupported version');
117
+ };
84
118
 
85
119
  // Lock verification
86
120
 
87
121
  // cluster-definition signatures verification
88
122
 
89
123
  const getPOSTConfigHashSigner = (
90
- signature: string,
91
- configHash: string,
92
- chainId: FORK_MAPPING,
124
+ signature: string,
125
+ configHash: string,
126
+ chainId: FORK_MAPPING,
93
127
  ): string => {
94
- try {
95
- const sig = ethers.Signature.from(signature)
96
- const data = signCreatorConfigHashPayload(
97
- { creator_config_hash: configHash },
98
- chainId,
99
- )
100
- const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4)
101
-
102
- return ethers.recoverAddress(digest, sig).toLowerCase()
103
- } catch (err) {
104
- throw err
105
- }
106
- }
128
+ try {
129
+ const sig = ethers.Signature.from(signature);
130
+ const data = signCreatorConfigHashPayload(
131
+ { creator_config_hash: configHash },
132
+ chainId,
133
+ );
134
+ const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);
135
+
136
+ return ethers.recoverAddress(digest, sig).toLowerCase();
137
+ } catch (err) {
138
+ throw err;
139
+ }
140
+ };
107
141
 
108
142
  const getPUTConfigHashSigner = (
109
- signature: string,
110
- configHash: string,
111
- chainId: number,
143
+ signature: string,
144
+ configHash: string,
145
+ chainId: number,
112
146
  ): string => {
113
- try {
114
- const sig = ethers.Signature.from(signature)
115
- const data = signOperatorConfigHashPayload(
116
- { operator_config_hash: configHash },
117
- chainId,
118
- )
119
- const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4)
120
-
121
- return ethers.recoverAddress(digest, sig).toLowerCase()
122
- } catch (err) {
123
- throw err
124
- }
125
- }
147
+ try {
148
+ const sig = ethers.Signature.from(signature);
149
+ const data = signOperatorConfigHashPayload(
150
+ { operator_config_hash: configHash },
151
+ chainId,
152
+ );
153
+ const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);
154
+
155
+ return ethers.recoverAddress(digest, sig).toLowerCase();
156
+ } catch (err) {
157
+ throw err;
158
+ }
159
+ };
126
160
 
127
161
  const getEnrSigner = (
128
- signature: string,
129
- payload: string,
130
- chainId: number,
162
+ signature: string,
163
+ payload: string,
164
+ chainId: number,
131
165
  ): string => {
132
- try {
133
- const sig = ethers.Signature.from(signature)
166
+ try {
167
+ const sig = ethers.Signature.from(signature);
134
168
 
135
- const data = signEnrPayload({ enr: payload }, chainId)
136
- const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4)
169
+ const data = signEnrPayload({ enr: payload }, chainId);
170
+ const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);
137
171
 
138
- return ethers.recoverAddress(digest, sig).toLowerCase()
139
- } catch (err) {
140
- throw err
141
- }
142
- }
172
+ return ethers.recoverAddress(digest, sig).toLowerCase();
173
+ } catch (err) {
174
+ throw err;
175
+ }
176
+ };
143
177
 
144
178
  const verifyDefinitionSignatures = (
145
- clusterDefinition: ClusterDefinition,
146
- definitionType: DefinitionFlow,
179
+ clusterDefinition: ClusterDefinition,
180
+ definitionType: DefinitionFlow,
147
181
  ): boolean => {
148
- if (definitionType === DefinitionFlow.Charon) {
149
- return true
150
- } else {
151
- const configSigner = getPOSTConfigHashSigner(
152
- clusterDefinition.creator.config_signature as string,
153
- clusterDefinition.config_hash,
154
- FORK_MAPPING[clusterDefinition.fork_version as keyof typeof FORK_MAPPING],
155
- )
156
-
157
- if (configSigner !== clusterDefinition.creator.address.toLowerCase()) {
158
- return false
159
- }
160
- if (definitionType === DefinitionFlow.Solo) {
161
- return true
162
- }
163
- return clusterDefinition.operators.every((operator) => {
164
- const configSigner = getPUTConfigHashSigner(
165
- operator.config_signature as string,
166
- clusterDefinition.config_hash,
167
- FORK_MAPPING[
168
- clusterDefinition.fork_version as keyof typeof FORK_MAPPING
169
- ],
170
- )
171
-
172
- const enrSigner = getEnrSigner(
173
- operator.enr_signature as string,
174
- operator.enr as string,
175
- FORK_MAPPING[
176
- clusterDefinition.fork_version as keyof typeof FORK_MAPPING
177
- ],
178
- )
179
-
180
- if (
181
- configSigner !== operator.address.toLowerCase() ||
182
- enrSigner !== operator.address.toLowerCase()
183
- ) {
184
- return false
185
- }
186
- return true
187
- })
182
+ if (definitionType === DefinitionFlow.Charon) {
183
+ return true;
184
+ } else {
185
+ const configSigner = getPOSTConfigHashSigner(
186
+ clusterDefinition.creator.config_signature as string,
187
+ clusterDefinition.config_hash,
188
+ FORK_MAPPING[clusterDefinition.fork_version as keyof typeof FORK_MAPPING],
189
+ );
190
+
191
+ if (configSigner !== clusterDefinition.creator.address.toLowerCase()) {
192
+ return false;
193
+ }
194
+ if (definitionType === DefinitionFlow.Solo) {
195
+ return true;
188
196
  }
189
- }
197
+ return clusterDefinition.operators.every(operator => {
198
+ const configSigner = getPUTConfigHashSigner(
199
+ operator.config_signature as string,
200
+ clusterDefinition.config_hash,
201
+ FORK_MAPPING[
202
+ clusterDefinition.fork_version as keyof typeof FORK_MAPPING
203
+ ],
204
+ );
205
+
206
+ const enrSigner = getEnrSigner(
207
+ operator.enr_signature as string,
208
+ operator.enr as string,
209
+ FORK_MAPPING[
210
+ clusterDefinition.fork_version as keyof typeof FORK_MAPPING
211
+ ],
212
+ );
213
+
214
+ if (
215
+ configSigner !== operator.address.toLowerCase() ||
216
+ enrSigner !== operator.address.toLowerCase()
217
+ ) {
218
+ return false;
219
+ }
220
+ return true;
221
+ });
222
+ }
223
+ };
190
224
 
191
225
  // cluster-lock data verification
192
226
  const computeSigningRoot = (
193
- sszObjectRoot: Uint8Array,
194
- domain: Uint8Array,
227
+ sszObjectRoot: Uint8Array,
228
+ domain: Uint8Array,
195
229
  ): Uint8Array => {
196
- const signingRootDefaultValue = signingRootType.defaultValue()
197
- signingRootDefaultValue.objectRoot = sszObjectRoot
198
- signingRootDefaultValue.domain = domain
199
- return Buffer.from(signingRootType.hashTreeRoot(signingRootDefaultValue).buffer)
200
- }
230
+ const signingRootDefaultValue = signingRootType.defaultValue();
231
+ signingRootDefaultValue.objectRoot = sszObjectRoot;
232
+ signingRootDefaultValue.domain = domain;
233
+ return Buffer.from(
234
+ signingRootType.hashTreeRoot(signingRootDefaultValue).buffer,
235
+ );
236
+ };
201
237
 
202
238
  const computeDepositMsgRoot = (msg: Partial<DepositData>): Buffer => {
203
- const depositMsgVal = depositMessageType.defaultValue()
239
+ const depositMsgVal = depositMessageType.defaultValue();
204
240
 
205
- depositMsgVal.pubkey = fromHexString(msg.pubkey as string)
206
- depositMsgVal.withdrawal_credentials = fromHexString(
207
- msg.withdrawal_credentials as string,
208
- )
209
- depositMsgVal.amount = parseInt(msg.amount as string)
210
- return Buffer.from(depositMessageType.hashTreeRoot(depositMsgVal).buffer)
211
- }
241
+ depositMsgVal.pubkey = fromHexString(msg.pubkey as string);
242
+ depositMsgVal.withdrawal_credentials = fromHexString(
243
+ msg.withdrawal_credentials as string,
244
+ );
245
+ depositMsgVal.amount = parseInt(msg.amount as string);
246
+ return Buffer.from(depositMessageType.hashTreeRoot(depositMsgVal).buffer);
247
+ };
212
248
 
213
249
  const computeForkDataRoot = (
214
- currentVersion: Uint8Array,
215
- genesisValidatorsRoot: Uint8Array,
250
+ currentVersion: Uint8Array,
251
+ genesisValidatorsRoot: Uint8Array,
216
252
  ): Uint8Array => {
217
- const forkDataVal = forkDataType.defaultValue()
218
- forkDataVal.currentVersion = currentVersion
219
- forkDataVal.genesisValidatorsRoot = genesisValidatorsRoot
220
- return Buffer.from(forkDataType.hashTreeRoot(forkDataVal).buffer)
221
- }
253
+ const forkDataVal = forkDataType.defaultValue();
254
+ forkDataVal.currentVersion = currentVersion;
255
+ forkDataVal.genesisValidatorsRoot = genesisValidatorsRoot;
256
+ return Buffer.from(forkDataType.hashTreeRoot(forkDataVal).buffer);
257
+ };
222
258
 
223
259
  const computebuilderRegistrationMsgRoot = (
224
- msg: BuilderRegistrationMessage,
260
+ msg: BuilderRegistrationMessage,
225
261
  ): Buffer => {
226
- const builderRegistrationMsgVal =
227
- builderRegistrationMessageType.defaultValue()
228
-
229
- builderRegistrationMsgVal.fee_recipient = fromHexString(msg.fee_recipient)
230
- builderRegistrationMsgVal.gas_limit = msg.gas_limit
231
- builderRegistrationMsgVal.timestamp = msg.timestamp
232
- builderRegistrationMsgVal.pubkey = fromHexString(msg.pubkey)
233
- return Buffer.from(
234
- builderRegistrationMessageType.hashTreeRoot(builderRegistrationMsgVal)
235
- .buffer,
236
- )
237
- }
262
+ const builderRegistrationMsgVal =
263
+ builderRegistrationMessageType.defaultValue();
264
+
265
+ builderRegistrationMsgVal.fee_recipient = fromHexString(msg.fee_recipient);
266
+ builderRegistrationMsgVal.gas_limit = msg.gas_limit;
267
+ builderRegistrationMsgVal.timestamp = msg.timestamp;
268
+ builderRegistrationMsgVal.pubkey = fromHexString(msg.pubkey);
269
+ return Buffer.from(
270
+ builderRegistrationMessageType.hashTreeRoot(builderRegistrationMsgVal)
271
+ .buffer,
272
+ );
273
+ };
238
274
 
239
275
  const computeDomain = (
240
- domainType: Uint8Array,
241
- lockForkVersion: string,
242
- genesisValidatorsRoot: Uint8Array = fromHexString(GENESIS_VALIDATOR_ROOT),
276
+ domainType: Uint8Array,
277
+ lockForkVersion: string,
278
+ genesisValidatorsRoot: Uint8Array = fromHexString(GENESIS_VALIDATOR_ROOT),
243
279
  ): Uint8Array => {
244
- const forkVersion = fromHexString(
245
- lockForkVersion.substring(2, lockForkVersion.length),
246
- )
280
+ const forkVersion = fromHexString(
281
+ lockForkVersion.substring(2, lockForkVersion.length),
282
+ );
247
283
 
248
- const forkDataRoot = computeForkDataRoot(forkVersion, genesisValidatorsRoot)
249
- const domain = new Uint8Array(32)
250
- domain.set(domainType)
251
- domain.set(forkDataRoot.subarray(0, 28), 4)
252
- return domain
253
- }
284
+ const forkDataRoot = computeForkDataRoot(forkVersion, genesisValidatorsRoot);
285
+ const domain = new Uint8Array(32);
286
+ domain.set(domainType);
287
+ domain.set(forkDataRoot.subarray(0, 28), 4);
288
+ return domain;
289
+ };
254
290
 
255
291
  /**
256
292
  * Verify deposit data withdrawal credintials and signature
@@ -260,161 +296,166 @@ const computeDomain = (
260
296
  * @returns {boolean} - return if deposit data is valid.
261
297
  */
262
298
  export const verifyDepositData = (
263
- distributedPublicKey: string,
264
- depositData: Partial<DepositData>,
265
- withdrawalAddress: string,
266
- forkVersion: string,
267
- ): { isValidDepositData: boolean, depositDataMsg: Uint8Array } => {
268
- const depositDomain = computeDomain(
269
- fromHexString(DOMAIN_DEPOSIT),
270
- forkVersion,
271
- )
272
- const eth1AddressWithdrawalPrefix = '0x01'
273
- if (
274
- eth1AddressWithdrawalPrefix +
275
- '0'.repeat(22) +
276
- withdrawalAddress.toLowerCase().slice(2) !==
277
- depositData.withdrawal_credentials
278
- ) {
279
- return { isValidDepositData: false, depositDataMsg: new Uint8Array(0) }
280
- }
281
-
282
- if (distributedPublicKey !== depositData.pubkey) {
283
- return { isValidDepositData: false, depositDataMsg: new Uint8Array(0) }
284
- }
285
-
286
- const depositMessageBuffer = computeDepositMsgRoot(
287
- depositData
288
- )
289
- const depositDataMessage = signingRoot(
290
- depositDomain,
291
- depositMessageBuffer,
292
- )
293
-
294
- return { isValidDepositData: true, depositDataMsg: depositDataMessage }
295
- }
299
+ distributedPublicKey: string,
300
+ depositData: Partial<DepositData>,
301
+ withdrawalAddress: string,
302
+ forkVersion: string,
303
+ ): { isValidDepositData: boolean; depositDataMsg: Uint8Array } => {
304
+ const depositDomain = computeDomain(
305
+ fromHexString(DOMAIN_DEPOSIT),
306
+ forkVersion,
307
+ );
308
+ const eth1AddressWithdrawalPrefix = '0x01';
309
+ if (
310
+ eth1AddressWithdrawalPrefix +
311
+ '0'.repeat(22) +
312
+ withdrawalAddress.toLowerCase().slice(2) !==
313
+ depositData.withdrawal_credentials
314
+ ) {
315
+ return { isValidDepositData: false, depositDataMsg: new Uint8Array(0) };
316
+ }
317
+
318
+ if (distributedPublicKey !== depositData.pubkey) {
319
+ return { isValidDepositData: false, depositDataMsg: new Uint8Array(0) };
320
+ }
321
+
322
+ const depositMessageBuffer = computeDepositMsgRoot(depositData);
323
+ const depositDataMessage = signingRoot(depositDomain, depositMessageBuffer);
324
+
325
+ return { isValidDepositData: true, depositDataMsg: depositDataMessage };
326
+ };
296
327
 
297
328
  export const verifyBuilderRegistration = (
298
- validator: DistributedValidator,
299
- feeRecipientAddress: string,
300
- forkVersion: string,
301
- ): { isValidBuilderRegistration: boolean, builderRegistrationMsg: Uint8Array } => {
302
- const builderDomain = computeDomain(
303
- fromHexString(DOMAIN_APPLICATION_BUILDER),
304
- forkVersion)
305
-
306
- if (
307
- validator.distributed_public_key !==
308
- validator.builder_registration?.message.pubkey
309
- ) {
310
- return { isValidBuilderRegistration: false, builderRegistrationMsg: new Uint8Array(0) }
311
- }
312
- if (
313
- feeRecipientAddress.toLowerCase() !==
314
- validator.builder_registration.message.fee_recipient.toLowerCase()
315
- ) {
316
- return { isValidBuilderRegistration: false, builderRegistrationMsg: new Uint8Array(0) }
317
- }
318
-
319
- const builderRegistrationMessageBuffer =
320
- computebuilderRegistrationMsgRoot(
321
- validator.builder_registration.message,
322
- )
323
-
324
- const builderRegistrationMessage = signingRoot(
325
- builderDomain,
326
- builderRegistrationMessageBuffer,
327
- )
328
-
329
- return { isValidBuilderRegistration: true, builderRegistrationMsg: builderRegistrationMessage }
330
- }
329
+ validator: DistributedValidator,
330
+ feeRecipientAddress: string,
331
+ forkVersion: string,
332
+ ): {
333
+ isValidBuilderRegistration: boolean;
334
+ builderRegistrationMsg: Uint8Array;
335
+ } => {
336
+ const builderDomain = computeDomain(
337
+ fromHexString(DOMAIN_APPLICATION_BUILDER),
338
+ forkVersion,
339
+ );
340
+
341
+ if (
342
+ validator.distributed_public_key !==
343
+ validator.builder_registration?.message.pubkey
344
+ ) {
345
+ return {
346
+ isValidBuilderRegistration: false,
347
+ builderRegistrationMsg: new Uint8Array(0),
348
+ };
349
+ }
350
+ if (
351
+ feeRecipientAddress.toLowerCase() !==
352
+ validator.builder_registration.message.fee_recipient.toLowerCase()
353
+ ) {
354
+ return {
355
+ isValidBuilderRegistration: false,
356
+ builderRegistrationMsg: new Uint8Array(0),
357
+ };
358
+ }
359
+
360
+ const builderRegistrationMessageBuffer = computebuilderRegistrationMsgRoot(
361
+ validator.builder_registration.message,
362
+ );
363
+
364
+ const builderRegistrationMessage = signingRoot(
365
+ builderDomain,
366
+ builderRegistrationMessageBuffer,
367
+ );
368
+
369
+ return {
370
+ isValidBuilderRegistration: true,
371
+ builderRegistrationMsg: builderRegistrationMessage,
372
+ };
373
+ };
331
374
 
332
375
  export const verifyNodeSignatures = (clusterLock: ClusterLock): boolean => {
333
- const ec = new elliptic.ec('secp256k1')
334
- const nodeSignatures = clusterLock.node_signatures
335
-
336
- const lockHashWithout0x = hexWithout0x(clusterLock.lock_hash)
337
- // node(ENR) signatures
338
- for (let i = 0; i < (nodeSignatures as string[]).length; i++) {
339
- const pubkey = ENR.decodeTxt(
340
- clusterLock.cluster_definition.operators[i].enr as string,
341
- ).publicKey.toString('hex')
342
-
343
- const ENRsignature = {
344
- r: (nodeSignatures as string[])[i].slice(2, 66),
345
- s: (nodeSignatures as string[])[i].slice(66, 130),
346
- }
347
-
348
- const nodeSignatureVerification = ec
349
- .keyFromPublic(pubkey, 'hex')
350
- .verify(lockHashWithout0x, ENRsignature)
351
-
352
- if (!nodeSignatureVerification) {
353
- return false
354
- }
376
+ const ec = new elliptic.ec('secp256k1');
377
+ const nodeSignatures = clusterLock.node_signatures;
378
+
379
+ const lockHashWithout0x = hexWithout0x(clusterLock.lock_hash);
380
+ // node(ENR) signatures
381
+ for (let i = 0; i < (nodeSignatures as string[]).length; i++) {
382
+ const pubkey = ENR.decodeTxt(
383
+ clusterLock.cluster_definition.operators[i].enr as string,
384
+ ).publicKey.toString('hex');
385
+
386
+ const ENRsignature = {
387
+ r: (nodeSignatures as string[])[i].slice(2, 66),
388
+ s: (nodeSignatures as string[])[i].slice(66, 130),
389
+ };
390
+
391
+ const nodeSignatureVerification = ec
392
+ .keyFromPublic(pubkey, 'hex')
393
+ .verify(lockHashWithout0x, ENRsignature);
394
+
395
+ if (!nodeSignatureVerification) {
396
+ return false;
355
397
  }
398
+ }
356
399
 
357
- return true
358
- }
400
+ return true;
401
+ };
359
402
 
360
403
  export const signingRoot = (
361
- domain: Uint8Array,
362
- messageBuffer: Buffer,
404
+ domain: Uint8Array,
405
+ messageBuffer: Buffer,
363
406
  ): Uint8Array => {
364
- return computeSigningRoot(messageBuffer, domain)
365
- }
407
+ return computeSigningRoot(messageBuffer, domain);
408
+ };
366
409
 
367
410
  const verifyLockData = async (clusterLock: ClusterLock): Promise<boolean> => {
368
- await init('herumi')
411
+ await init('herumi');
369
412
 
370
- if (semver.eq(clusterLock.cluster_definition.version, 'v1.6.0')) {
371
- return verifyDVV1X6(clusterLock)
372
- }
413
+ if (semver.eq(clusterLock.cluster_definition.version, 'v1.6.0')) {
414
+ return verifyDVV1X6(clusterLock);
415
+ }
373
416
 
374
- if (semver.eq(clusterLock.cluster_definition.version, 'v1.7.0')) {
375
- return verifyDVV1X7(clusterLock)
376
- }
417
+ if (semver.eq(clusterLock.cluster_definition.version, 'v1.7.0')) {
418
+ return verifyDVV1X7(clusterLock);
419
+ }
377
420
 
378
- if (semver.eq(clusterLock.cluster_definition.version, 'v1.8.0')) {
379
- return verifyDVV1X8(clusterLock)
380
- }
381
- return false
382
- }
421
+ if (semver.eq(clusterLock.cluster_definition.version, 'v1.8.0')) {
422
+ return verifyDVV1X8(clusterLock);
423
+ }
424
+ return false;
425
+ };
383
426
 
384
427
  export const isValidClusterLock = async (
385
- clusterLock: ClusterLock,
428
+ clusterLock: ClusterLock,
386
429
  ): Promise<boolean> => {
387
- try {
388
- const definitionType = definitionFlow(clusterLock.cluster_definition)
389
- if (definitionType == null) {
390
- return false
391
- }
392
-
393
- const isValidDefinitionData = verifyDefinitionSignatures(
394
- clusterLock.cluster_definition,
395
- definitionType,
396
- )
397
- if (!isValidDefinitionData) {
398
- return false
399
- }
400
-
401
- if (
402
- clusterConfigOrDefinitionHash(clusterLock.cluster_definition, false) !==
403
- clusterLock.cluster_definition.definition_hash
404
- ) {
405
- return false
406
- }
407
-
408
- if (clusterLockHash(clusterLock) !== clusterLock.lock_hash) {
409
- return false
410
- }
411
-
412
- const isValidLockData = await verifyLockData(clusterLock)
413
- if (!isValidLockData) {
414
- return false
415
- }
416
- return true
417
- } catch (err) {
418
- return false
430
+ try {
431
+ const definitionType = definitionFlow(clusterLock.cluster_definition);
432
+ if (definitionType == null) {
433
+ return false;
434
+ }
435
+ const isValidDefinitionData = verifyDefinitionSignatures(
436
+ clusterLock.cluster_definition,
437
+ definitionType,
438
+ );
439
+ if (!isValidDefinitionData) {
440
+ return false;
441
+ }
442
+
443
+ if (
444
+ clusterConfigOrDefinitionHash(clusterLock.cluster_definition, false) !==
445
+ clusterLock.cluster_definition.definition_hash
446
+ ) {
447
+ return false;
448
+ }
449
+ if (clusterLockHash(clusterLock) !== clusterLock.lock_hash) {
450
+ return false;
451
+ }
452
+
453
+ const isValidLockData = await verifyLockData(clusterLock);
454
+ if (!isValidLockData) {
455
+ return false;
419
456
  }
420
- }
457
+ return true;
458
+ } catch (err) {
459
+ return false;
460
+ }
461
+ };