@obolnetwork/obol-sdk 1.0.9 → 1.0.12

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 (66) hide show
  1. package/README.md +2 -1
  2. package/dist/cjs/src/ajv.js +17 -0
  3. package/dist/cjs/src/base.js +39 -0
  4. package/dist/cjs/src/constants.js +86 -0
  5. package/dist/cjs/src/errors.js +11 -0
  6. package/dist/cjs/src/hash.js +172 -0
  7. package/dist/cjs/src/index.js +158 -0
  8. package/dist/cjs/src/schema.js +72 -0
  9. package/dist/cjs/src/services.js +32 -0
  10. package/dist/cjs/src/types.js +18 -0
  11. package/dist/cjs/src/utils.js +11 -0
  12. package/dist/cjs/src/verify.js +326 -0
  13. package/dist/cjs/test/fixtures.js +101 -0
  14. package/dist/cjs/test/methods.test.js +128 -0
  15. package/dist/esm/src/ajv.js +10 -0
  16. package/dist/esm/src/base.js +35 -0
  17. package/dist/esm/src/constants.js +79 -0
  18. package/dist/esm/src/errors.js +7 -0
  19. package/dist/esm/src/hash.js +165 -0
  20. package/dist/esm/src/index.js +140 -0
  21. package/dist/esm/src/schema.js +69 -0
  22. package/dist/esm/src/services.js +28 -0
  23. package/dist/esm/src/types.js +15 -0
  24. package/dist/esm/src/utils.js +6 -0
  25. package/dist/esm/src/verify.js +295 -0
  26. package/dist/esm/test/fixtures.js +98 -0
  27. package/dist/esm/test/methods.test.js +126 -0
  28. package/dist/{ajv.d.ts → types/src/ajv.d.ts} +2 -3
  29. package/dist/{base.d.ts → types/src/base.d.ts} +13 -14
  30. package/dist/types/src/constants.d.ts +79 -0
  31. package/dist/{errors.d.ts → types/src/errors.d.ts} +4 -5
  32. package/dist/{hash.d.ts → types/src/hash.d.ts} +56 -64
  33. package/dist/{index.d.ts → types/src/index.d.ts} +64 -60
  34. package/dist/{schema.d.ts → types/src/schema.d.ts} +57 -58
  35. package/dist/types/src/services.d.ts +11 -0
  36. package/dist/{types.d.ts → types/src/types.d.ts} +153 -154
  37. package/dist/types/src/utils.d.ts +2 -0
  38. package/dist/types/src/verify.d.ts +4 -0
  39. package/dist/{fixtures.d.ts → types/test/fixtures.d.ts} +61 -62
  40. package/dist/types/test/methods.test.d.ts +1 -0
  41. package/package.json +55 -20
  42. package/src/ajv.ts +11 -0
  43. package/src/base.ts +44 -0
  44. package/src/constants.ts +118 -0
  45. package/src/errors.ts +11 -0
  46. package/src/hash.ts +250 -0
  47. package/src/index.ts +160 -0
  48. package/src/schema.ts +70 -0
  49. package/src/services.ts +20 -0
  50. package/src/types.ts +211 -0
  51. package/src/utils.ts +7 -0
  52. package/src/verify.ts +479 -0
  53. package/dist/ajv.d.ts.map +0 -1
  54. package/dist/base.d.ts.map +0 -1
  55. package/dist/cluster.test.d.ts +0 -2
  56. package/dist/cluster.test.d.ts.map +0 -1
  57. package/dist/constants.d.ts +0 -30
  58. package/dist/constants.d.ts.map +0 -1
  59. package/dist/errors.d.ts.map +0 -1
  60. package/dist/fixtures.d.ts.map +0 -1
  61. package/dist/hash.d.ts.map +0 -1
  62. package/dist/index.d.ts.map +0 -1
  63. package/dist/index.es.js +0 -15582
  64. package/dist/index.js +0 -15586
  65. package/dist/schema.d.ts.map +0 -1
  66. package/dist/types.d.ts.map +0 -1
@@ -0,0 +1,295 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
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';
16
+ import elliptic from 'elliptic';
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;
52
+ }
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;
62
+ }
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;
72
+ }
73
+ return null;
74
+ };
75
+ //cluster-definition signatures verificatin
76
+ const getPOSTConfigHashSigner = (signature, configHash, chainId) => {
77
+ try {
78
+ const sig = ethers.Signature.from(signature);
79
+ const data = signCreatorConfigHashPayload({ creator_config_hash: configHash }, chainId);
80
+ const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);
81
+ return ethers.recoverAddress(digest, sig).toLowerCase();
82
+ }
83
+ catch (err) {
84
+ throw err;
85
+ }
86
+ };
87
+ const getPUTConfigHashSigner = (signature, configHash, chainId) => {
88
+ try {
89
+ const sig = ethers.Signature.from(signature);
90
+ const data = signOperatorConfigHashPayload({ operator_config_hash: configHash }, chainId);
91
+ const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);
92
+ return ethers.recoverAddress(digest, sig).toLowerCase();
93
+ }
94
+ catch (err) {
95
+ throw err;
96
+ }
97
+ };
98
+ const getEnrSigner = (signature, payload, chainId) => {
99
+ try {
100
+ const sig = ethers.Signature.from(signature);
101
+ const data = signEnrPayload({ enr: payload }, chainId);
102
+ const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);
103
+ return ethers.recoverAddress(digest, sig).toLowerCase();
104
+ }
105
+ catch (err) {
106
+ throw err;
107
+ }
108
+ };
109
+ const verifyDefinitionSignatures = (clusterDefinition, definitionType) => {
110
+ if (definitionType === 'Charon-Command') {
111
+ return true;
112
+ }
113
+ else {
114
+ const configSigner = getPOSTConfigHashSigner(clusterDefinition.creator.config_signature, clusterDefinition.config_hash, FORK_MAPPING[clusterDefinition.fork_version]);
115
+ if (configSigner !== clusterDefinition.creator.address.toLowerCase()) {
116
+ return false;
117
+ }
118
+ if (definitionType === 'LP-Solo') {
119
+ return true;
120
+ }
121
+ return clusterDefinition.operators.every(operator => {
122
+ const configSigner = getPUTConfigHashSigner(operator.config_signature, clusterDefinition.config_hash, FORK_MAPPING[clusterDefinition.fork_version]);
123
+ const enrSigner = getEnrSigner(operator.enr_signature, operator.enr, FORK_MAPPING[clusterDefinition.fork_version]);
124
+ if (configSigner !== operator.address.toLowerCase() ||
125
+ enrSigner !== operator.address.toLowerCase()) {
126
+ return false;
127
+ }
128
+ return true;
129
+ });
130
+ }
131
+ };
132
+ //cluster-lock data verification
133
+ 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);
138
+ };
139
+ const computeDepositMsgRoot = (msg) => {
140
+ const depositMsgVal = depositMessageType.defaultValue();
141
+ depositMsgVal.pubkey = fromHexString(msg.pubkey);
142
+ depositMsgVal.withdrawal_credentials = fromHexString(msg.withdrawal_credentials);
143
+ depositMsgVal.amount = parseInt(msg.amount);
144
+ return Buffer.from(depositMessageType.hashTreeRoot(depositMsgVal).buffer);
145
+ };
146
+ const computeForkDataRoot = (currentVersion, genesisValidatorsRoot) => {
147
+ const forkDataVal = forkDataType.defaultValue();
148
+ forkDataVal.currentVersion = currentVersion;
149
+ forkDataVal.genesisValidatorsRoot = genesisValidatorsRoot;
150
+ return Buffer.from(forkDataType.hashTreeRoot(forkDataVal).buffer);
151
+ };
152
+ const computebuilderRegistrationMsgRoot = (msg) => {
153
+ const builderRegistrationMsgVal = builderRegistrationMessageType.defaultValue();
154
+ builderRegistrationMsgVal.fee_recipient = fromHexString(msg.fee_recipient);
155
+ builderRegistrationMsgVal.gas_limit = msg.gas_limit;
156
+ builderRegistrationMsgVal.timestamp = msg.timestamp;
157
+ builderRegistrationMsgVal.pubkey = fromHexString(msg.pubkey);
158
+ return Buffer.from(builderRegistrationMessageType.hashTreeRoot(builderRegistrationMsgVal)
159
+ .buffer);
160
+ };
161
+ // DV data verification
162
+ const computeDomain = (domainType, lockForkVersion, genesisValidatorsRoot = fromHexString(GENESIS_VALIDATOR_ROOT)) => {
163
+ const forkVersion = fromHexString(lockForkVersion.substring(2, lockForkVersion.length));
164
+ const forkDataRoot = computeForkDataRoot(forkVersion, genesisValidatorsRoot);
165
+ const domain = new Uint8Array(32);
166
+ domain.set(domainType);
167
+ domain.set(forkDataRoot.subarray(0, 28), 4);
168
+ return domain;
169
+ };
170
+ const verifyDepositData = (validator, withdrawalAddress) => {
171
+ const eth1AddressWithdrawalPrefix = '0x01';
172
+ if (eth1AddressWithdrawalPrefix +
173
+ '0'.repeat(22) +
174
+ withdrawalAddress.toLowerCase().slice(2) !==
175
+ validator.deposit_data.withdrawal_credentials) {
176
+ return false;
177
+ }
178
+ if (validator.distributed_public_key !== validator.deposit_data.pubkey) {
179
+ return false;
180
+ }
181
+ return true;
182
+ };
183
+ const verifyBuilderRegistration = (validator, feeRecipientAddress) => {
184
+ if (validator.distributed_public_key !==
185
+ validator.builder_registration.message.pubkey) {
186
+ return false;
187
+ }
188
+ if (feeRecipientAddress.toLowerCase() !==
189
+ validator.builder_registration.message.fee_recipient.toLowerCase()) {
190
+ return false;
191
+ }
192
+ return true;
193
+ };
194
+ export const signingRoot = (domain, messageBuffer) => {
195
+ return computeSigningRoot(messageBuffer, domain);
196
+ };
197
+ 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
+ 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
+ }
245
+ }
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
+ }
262
+ }
263
+ //signature aggregate
264
+ if (!verifyAggregate(pubShares, fromHexString(clusterLock.lock_hash), fromHexString(clusterLock.signature_aggregate))) {
265
+ return false;
266
+ }
267
+ return true;
268
+ });
269
+ export const isValidClusterLock = (clusterLock) => __awaiter(void 0, void 0, void 0, function* () {
270
+ try {
271
+ const definitionType = definitionFlow(clusterLock.cluster_definition);
272
+ if (!definitionType) {
273
+ return false;
274
+ }
275
+ const isValidDefinitionData = verifyDefinitionSignatures(clusterLock.cluster_definition, definitionType);
276
+ if (!isValidDefinitionData) {
277
+ return false;
278
+ }
279
+ if (clusterConfigOrDefinitionHash(clusterLock.cluster_definition, false) !==
280
+ clusterLock.cluster_definition.definition_hash) {
281
+ return false;
282
+ }
283
+ if (clusterLockHash(clusterLock) !== clusterLock.lock_hash) {
284
+ return false;
285
+ }
286
+ const isValidLockData = yield verifyLockData(clusterLock);
287
+ if (!isValidLockData) {
288
+ return false;
289
+ }
290
+ return true;
291
+ }
292
+ catch (err) {
293
+ return false;
294
+ }
295
+ });
@@ -0,0 +1,98 @@
1
+ export const clusterLockV1X7 = {
2
+ cluster_definition: {
3
+ name: "v1.7.0 group test",
4
+ creator: {
5
+ address: "0x86B8145c98e5BD25BA722645b15eD65f024a87EC",
6
+ config_signature: "0x269aefc773de5fbfe41001cbd160b62747a27d76a92c8a6a7eae19d0f172c4b44d445ee0992e1ba6e271740a59051bf9cd8fda4042572bde75e40e8d28f7e6561b"
7
+ },
8
+ operators: [
9
+ {
10
+ address: "0x86B8145c98e5BD25BA722645b15eD65f024a87EC",
11
+ enr: "enr:-Iu4QCdreXm-iHrd2Y-QgLAHeGAkQSrZNvfPL-l6IuGEuKoMHnZsg93EgIWWOtcBzKs_-skexEKvY3CmcJ8SOzMIGMiAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQL4r6dX8heIruf3OW_hfcctLtsaX8roj8E-8s0-IpH9rYN0Y3CCDhqDdWRwgg4u",
12
+ config_signature: "0x73bc8d23dee307a02791339300b6405573e686c22222d6e6c8fe349f52bb8c0620ea1ed476fe569ed8c8d21751893216b248eb28dd4de7aa263a234efe3129181b",
13
+ enr_signature: "0x1dc963f01eb794170e35a972ec8b487bdea2e3c1acbbe2fc2ab7936e7fe99e407583489a7dff3fbb08f4902e77867d795a7196a83d91b0a0eb8e50b8d96d35ba1c"
14
+ },
15
+ {
16
+ address: "0xC35CfCd67b9C27345a54EDEcC1033F2284148c81",
17
+ enr: "enr:-Iu4QNbiUUUwT18LynBbVPJhNxvzQsaSpUr40mQTWscnZaqKb6vAlvV8j-eDDR3E0wjMQumGRbGm2IAb5_k4bVWJiVGAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPOiodUji0ohgJb5sNK1hgv8g6xO5_znZz3NkkBkyYyKIN0Y3CCDhqDdWRwgg4u",
18
+ config_signature: "0x0ab6f58c612cd0c6af395769132fccc5c0804fd40cc395ae7ab2069a7aedb2884f2fa07a91eee57e13c3407dbeeb81b2256312d14006bdb1c19d4206b66899a41c",
19
+ enr_signature: "0xb580f1380a62d299587357181561c8db8d67f4cc92d094b2ca3c84eddde3759567cfe60669b30ef55308a749c3c6a32f52148ac6b667cc6554f63a04c55350451b"
20
+ },
21
+ {
22
+ address: "0x33807D6F1DCe44b9C599fFE03640762A6F08C496",
23
+ enr: "enr:-Iu4QJyserRukhG0Vgi2csu7GjpHYUGufNEbZ8Q7ZBrcZUb0KqpL5QzHonkh1xxHlxatTxrIcX_IS5J3SEWR_sa0ptGAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQMAUgEqczOjevyculnUIofhCj0DkgJudErM7qCYIvIkzIN0Y3CCDhqDdWRwgg4u",
24
+ config_signature: "0x6f4f030aedba854cc4fa626171038e0125a93b3c3017af932d5b2a159076d0e17c1641f28cb58c29039e2c33074dd729c3198253ea17cc38074274f5e13676441b",
25
+ enr_signature: "0xf63195e4238eac6d9deae5ec56626ac16a7a861294587c3ee144853ace76f8a2089980a2cbf17c72a8c5ac0651c6935d023db34fc0be58a91f0c70c26017597a1b"
26
+ },
27
+ {
28
+ address: "0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f",
29
+ enr: "enr:-HW4QKJTwXC6Chw6zbnA3HFZi6Jo0DkIgjKy4eUBpsSOGnAeWE6ChEjEyk_6R6Qrm7jI-iqfs3_HYxiKde8vFgvHHrCAgmlkgnY0iXNlY3AyNTZrMaECfFKQH4spdZCHqrKVz1Q02xYla6J_RQECDNNYBRWdzv8",
30
+ config_signature: "0x8c09f7163c20cd0a2d28cb4fbfda22e93bc53bc537f6143407065922a25e40ab39f52b232ecb9241b3e1674f87a16d263181a17a80de55fb92b9659211428b581b",
31
+ enr_signature: "0x05d801705908933b7d1566f48f5726d36874423b1bbdd3f582e08c754220d211492b46ee434f1d1f5bd1507f80f9ca1df569354892e7d2eda87861bc2279be6e1c"
32
+ }
33
+ ],
34
+ uuid: "a1d642aa-07ed-4d48-9eda-0e70cb4bb03f",
35
+ version: "v1.7.0",
36
+ timestamp: "2023-07-18T10:36:04.505Z",
37
+ num_validators: 1,
38
+ threshold: 3,
39
+ validators: [
40
+ {
41
+ fee_recipient_address: "0x86B8145c98e5BD25BA722645b15eD65f024a87EC",
42
+ withdrawal_address: "0x86B8145c98e5BD25BA722645b15eD65f024a87EC"
43
+ }
44
+ ],
45
+ dkg_algorithm: "default",
46
+ fork_version: "0x00001020",
47
+ config_hash: "0x4c3e37cbdb2fb850150f6e112c4ea03cdc388bff059081182659b3407183b431",
48
+ definition_hash: "0xe7bf484f38a45c96e9a3fac60d3e39b88eeb9979fad983e06b0ec90e90aa20b3"
49
+ },
50
+ distributed_validators: [
51
+ {
52
+ distributed_public_key: "0xb61b17f3b6425eb2636bc6378de0ed8aab918c3346b59d297828e7c74643d51e80edc2b41290c568614cfb80d2b0be34",
53
+ public_shares: [
54
+ "0xab6c1448221f664c145a4a574302fd4419ccfe1178487b2fa4629ee9fc2999c43188c96a52ad795074124a7eb0f63841",
55
+ "0x8a2a265fbe9854b1532be335272717a5650e19ecc4d6ea54ed8d8d239200f5b860e5710f7a3b9f5d8a100782cbaf4ede",
56
+ "0xa8a0aaf1119afe1c2b21d7dbf18bbe107586dc49028d285886c2e7f2bcf3f1c6817fff1250f4a4a370229a0266ea9bff",
57
+ "0x93f62a042d304bbb316761712f5a39a9d549c71275e5d891afa3688b9224ebfa3bfd22f8b76f31d8ad88f65812b27dc3"
58
+ ],
59
+ deposit_data: {
60
+ pubkey: "0xb61b17f3b6425eb2636bc6378de0ed8aab918c3346b59d297828e7c74643d51e80edc2b41290c568614cfb80d2b0be34",
61
+ withdrawal_credentials: "0x01000000000000000000000086b8145c98e5bd25ba722645b15ed65f024a87ec",
62
+ amount: "32000000000",
63
+ signature: "0xae016c6c42daa8d1517d0e10370cf7882b28149635f734c5f3d83f781cd61f5df6d5203ec11bd0ca9d9e51c6030b66330476a93a65de6151d110e0f6482591f53678d863e55392e9cfb55fecac537edac51979790bcde38250a141348f116d46",
64
+ deposit_data_root: 'fbada6c7c4cba867780eead34c1462339795fe9b417db466ce2f9d4822e8a4bb'
65
+ },
66
+ builder_registration: {
67
+ message: {
68
+ fee_recipient: "0x86B8145c98e5BD25BA722645b15eD65f024a87EC",
69
+ gas_limit: 30000000,
70
+ timestamp: 1616508000,
71
+ pubkey: "0xb61b17f3b6425eb2636bc6378de0ed8aab918c3346b59d297828e7c74643d51e80edc2b41290c568614cfb80d2b0be34"
72
+ },
73
+ signature: "0xb1de0eeb44c3b9d598d50c8d48a676e45dd63a5a3040caa34e34ab8c60d0b2f079419d8699c97e76b641326845c99ee309096de1878547e370d4b82e2e4e580650fee2e46dc7b9b3bd805b7fe6f95ba6a9e2734c3ddd23bf6b1f5597e9a3f834"
74
+ }
75
+ }
76
+ ],
77
+ signature_aggregate: "0x90c32196e4dda81b39faaaec283e3ec5620b7c25c1a35949d54eb53a7c1c5a3963614c0d147085d82f9c52f550556ba406c0c5c8a4cec3fec825e6663a8792a9205f1a36fdde3e8c810cfd8861963190dd9b9eadd9a803bd5765c0b93d146542",
78
+ lock_hash: "0xdb19ef9c0677713f55502448b7c63d58fb25c81d34a5a8ce07a19bcfb3a9c0a0",
79
+ node_signatures: [
80
+ "0xa5230ba0586b21d49e790bc99304dc2245f8222c17d456ef60b848e3b0c5541d61825b2f9c2bda87ba3952979f28f6ba2338026603de74f925d9603aa823d42f01",
81
+ "0xc22ac5dc3a6f98471f2f25d11fd48600f955f68cb940e9da6789ab8fc50ca1bf40c8fb3200132ad6b2c66be7e256979fe182f8f924506ad8ba2c827401038ec600",
82
+ "0xebd08c9ece21abd78cfe82bc32eae68ccd77fc1e96e56ce93679918c5adcb2c92564a4fa609d7649587fbfb0c581403da8553e9e62c910e5ae589625704aa02b00",
83
+ "0xa07fc8f89f32a5b2551cc2173b0486f86142ee2a67400d2d4a956a2b948da96b58adddf558db6ac84de62242b5e56b1a1e95d26451be3a922fe3929f2a367a7400"
84
+ ]
85
+ };
86
+ export const clusterConfig = {
87
+ name: "testSDK",
88
+ operators: [
89
+ { address: "0xC35CfCd67b9C27345a54EDEcC1033F2284148c81" },
90
+ { address: "0x33807D6F1DCe44b9C599fFE03640762A6F08C496" },
91
+ { address: "0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f" },
92
+ { address: "0x86B8145c98e5BD25BA722645b15eD65f024a87EC" }
93
+ ],
94
+ validators: [{
95
+ fee_recipient_address: "0x3CD4958e76C317abcEA19faDd076348808424F99",
96
+ withdrawal_address: "0xE0C5ceA4D3869F156717C66E188Ae81C80914a6e"
97
+ }],
98
+ };
@@ -0,0 +1,126 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { ethers } from 'ethers';
11
+ import { Client, validateClusterLock } from '../src/index';
12
+ import { clusterConfig, clusterLockV1X7 } from './fixtures.js';
13
+ import { SDK_VERSION } from '../src/constants';
14
+ import { Base } from '../src/base';
15
+ import { validatePayload } from '../src/ajv';
16
+ import { HttpResponse, http } from 'msw';
17
+ import { setupServer } from 'msw/node';
18
+ describe('Cluster Client', () => {
19
+ var _a;
20
+ const mockConfigHash = "0x1f6c94e6c070393a68c1aa6073a21cb1fd57f0e14d2a475a2958990ab728c2fd";
21
+ const mnemonic = ((_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) || "";
22
+ const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
23
+ const wallet = new ethers.Wallet(privateKey);
24
+ const mockSigner = wallet.connect(null);
25
+ const clientInstance = new Client({ baseUrl: "https://obol-api-dev.gcp.obol.tech", chainId: 5 }, mockSigner);
26
+ test('throws invalid ChainId when it is equal to 1', () => __awaiter(void 0, void 0, void 0, function* () {
27
+ try {
28
+ new Client({ chainId: 1 }, mockSigner);
29
+ }
30
+ catch (error) {
31
+ expect(error.message).toBe("Invalid ChainId");
32
+ }
33
+ }));
34
+ test('createClusterDefinition should return config_hash', () => __awaiter(void 0, void 0, void 0, function* () {
35
+ clientInstance['request'] = jest.fn().mockReturnValue(Promise.resolve({ config_hash: mockConfigHash }));
36
+ const config_hash = yield clientInstance.createClusterDefinition(clusterConfig);
37
+ expect(config_hash).toEqual(mockConfigHash);
38
+ }));
39
+ test('acceptClusterDefinition should return cluster definition', () => __awaiter(void 0, void 0, void 0, function* () {
40
+ clientInstance['request'] = jest.fn().mockReturnValue(Promise.resolve(clusterLockV1X7.cluster_definition));
41
+ const clusterDefinition = yield clientInstance.acceptClusterDefinition({ enr: clusterLockV1X7.cluster_definition.operators[0].enr, version: clusterLockV1X7.cluster_definition.version }, clusterLockV1X7.cluster_definition.config_hash);
42
+ expect(clusterDefinition).toEqual(clusterLockV1X7.cluster_definition);
43
+ }));
44
+ test('createClusterDefinition should throw an error on invalid operators', () => __awaiter(void 0, void 0, void 0, function* () {
45
+ clientInstance['request'] = jest.fn().mockReturnValue(Promise.resolve({ config_hash: mockConfigHash }));
46
+ try {
47
+ yield clientInstance.createClusterDefinition(Object.assign(Object.assign({}, clusterConfig), { operators: [] }));
48
+ }
49
+ catch (error) {
50
+ expect(error.message).toEqual("Schema compilation errors', must NOT have fewer than 4 items");
51
+ }
52
+ }));
53
+ test('validatePayload should throw an error on empty schema', () => __awaiter(void 0, void 0, void 0, function* () {
54
+ try {
55
+ validatePayload(Object.assign(Object.assign({}, clusterConfig), { operators: [] }), "");
56
+ }
57
+ catch (error) {
58
+ expect(error.message).toEqual("schema must be object or boolean");
59
+ }
60
+ }));
61
+ test('getClusterdefinition should return cluster definition if config hash exist', () => __awaiter(void 0, void 0, void 0, function* () {
62
+ clientInstance['request'] = jest.fn().mockReturnValue(Promise.resolve(clusterLockV1X7.cluster_definition));
63
+ const clusterDefinition = yield clientInstance.getClusterDefinition(clusterLockV1X7.cluster_definition.config_hash);
64
+ expect(clusterDefinition.config_hash).toEqual(clusterLockV1X7.cluster_definition.config_hash);
65
+ }));
66
+ test('getClusterLock should return lockFile if exist', () => __awaiter(void 0, void 0, void 0, function* () {
67
+ clientInstance['request'] = jest.fn().mockReturnValue(Promise.resolve(clusterLockV1X7));
68
+ const clusterLock = yield clientInstance.getClusterLock(clusterLockV1X7.cluster_definition.config_hash);
69
+ expect(clusterLock.lock_hash).toEqual(clusterLockV1X7.lock_hash);
70
+ }));
71
+ test('request method should set user agent header', () => __awaiter(void 0, void 0, void 0, function* () {
72
+ const server = setupServer(http.get('http://testexample.com/test', ({ request }) => {
73
+ // Check if the request contains specific headers
74
+ if (request.headers.get('User-Agent') === `Obol-SDK/${SDK_VERSION}`) {
75
+ return HttpResponse.json({ message: 'user-agent header exist' });
76
+ }
77
+ }));
78
+ server.listen();
79
+ class TestBase extends Base {
80
+ callProtectedRequest(endpoint, options) {
81
+ return __awaiter(this, void 0, void 0, function* () {
82
+ return (yield this.request(endpoint, options));
83
+ });
84
+ }
85
+ }
86
+ const testBaseInstance = new TestBase({ baseUrl: "http://testExample.com" });
87
+ const result = yield testBaseInstance.callProtectedRequest('/test', {
88
+ method: 'GET',
89
+ });
90
+ expect(result === null || result === void 0 ? void 0 : result.message).toEqual("user-agent header exist");
91
+ server.close();
92
+ }));
93
+ });
94
+ describe('Cluster Client without a signer', () => {
95
+ const clientInstance = new Client({ baseUrl: "https://obol-api-dev.gcp.obol.tech", chainId: 5 });
96
+ test('createClusterDefinition should throw an error without signer', () => __awaiter(void 0, void 0, void 0, function* () {
97
+ try {
98
+ yield clientInstance.createClusterDefinition(clusterConfig);
99
+ }
100
+ catch (err) {
101
+ expect(err).toEqual("Signer is required in createClusterDefinition");
102
+ }
103
+ }));
104
+ test('acceptClusterDefinition should throw an error without signer', () => __awaiter(void 0, void 0, void 0, function* () {
105
+ try {
106
+ yield clientInstance.acceptClusterDefinition({ enr: clusterLockV1X7.cluster_definition.operators[0].enr, version: clusterLockV1X7.cluster_definition.version }, clusterLockV1X7.cluster_definition.config_hash);
107
+ }
108
+ catch (err) {
109
+ expect(err).toEqual("Signer is required in acceptClusterDefinition");
110
+ }
111
+ }));
112
+ test('getClusterdefinition should return cluster definition if config hash exist', () => __awaiter(void 0, void 0, void 0, function* () {
113
+ clientInstance['request'] = jest.fn().mockReturnValue(Promise.resolve(clusterLockV1X7.cluster_definition));
114
+ const clusterDefinition = yield clientInstance.getClusterDefinition(clusterLockV1X7.cluster_definition.config_hash);
115
+ expect(clusterDefinition.config_hash).toEqual(clusterLockV1X7.cluster_definition.config_hash);
116
+ }));
117
+ test('getClusterLock should return lockFile if exist', () => __awaiter(void 0, void 0, void 0, function* () {
118
+ clientInstance['request'] = jest.fn().mockReturnValue(Promise.resolve(clusterLockV1X7));
119
+ const clusterLock = yield clientInstance.getClusterLock(clusterLockV1X7.cluster_definition.config_hash);
120
+ expect(clusterLock.lock_hash).toEqual(clusterLockV1X7.lock_hash);
121
+ }));
122
+ it('should return true on verified the cluster lock', () => __awaiter(void 0, void 0, void 0, function* () {
123
+ const isValidLock = yield validateClusterLock(clusterLockV1X7);
124
+ expect(isValidLock).toEqual(true);
125
+ }));
126
+ });
@@ -1,3 +1,2 @@
1
- import { ErrorObject } from 'ajv';
2
- export declare function validatePayload(data: any, schema: any): ErrorObject[] | undefined | null | boolean;
3
- //# sourceMappingURL=ajv.d.ts.map
1
+ import { ErrorObject } from 'ajv';
2
+ export declare function validatePayload(data: any, schema: any): ErrorObject[] | undefined | null | boolean;
@@ -1,14 +1,13 @@
1
- import { FORK_MAPPING } from './types';
2
- declare type Config = {
3
- baseUrl?: string;
4
- chainId?: FORK_MAPPING;
5
- };
6
- export declare abstract class Base {
7
- baseUrl: string;
8
- chainId: number;
9
- fork_version: string;
10
- constructor({ baseUrl, chainId }: Config);
11
- protected request<T>(endpoint: string, options?: RequestInit): Promise<T>;
12
- }
13
- export {};
14
- //# sourceMappingURL=base.d.ts.map
1
+ import { FORK_MAPPING } from './types.js';
2
+ type Config = {
3
+ baseUrl?: string;
4
+ chainId?: FORK_MAPPING;
5
+ };
6
+ export declare abstract class Base {
7
+ baseUrl: string;
8
+ chainId: number;
9
+ fork_version: string;
10
+ constructor({ baseUrl, chainId }: Config);
11
+ protected request<T>(endpoint: string, options?: RequestInit): Promise<T>;
12
+ }
13
+ export {};
@@ -0,0 +1,79 @@
1
+ import { TypedMessage } from "@metamask/eth-sig-util";
2
+ export declare const CONFLICT_ERROR_MSG = "Conflict";
3
+ export declare const EIP712_DOMAIN_NAME = "Obol";
4
+ export declare const EIP712_DOMAIN_VERSION = "1";
5
+ export declare const CreatorConfigHashSigningTypes: {
6
+ CreatorConfigHash: {
7
+ name: string;
8
+ type: string;
9
+ }[];
10
+ };
11
+ export declare const Domain: (chainId: number) => {
12
+ name: string;
13
+ version: string;
14
+ chainId: number;
15
+ };
16
+ export declare const CreatorTypedMessage: {
17
+ CreatorConfigHash: {
18
+ name: string;
19
+ type: string;
20
+ }[];
21
+ EIP712Domain: {
22
+ name: string;
23
+ type: string;
24
+ }[];
25
+ };
26
+ export declare const EnrSigningTypes: {
27
+ ENR: {
28
+ name: string;
29
+ type: string;
30
+ }[];
31
+ };
32
+ export declare const OperatorConfigHashSigningTypes: {
33
+ OperatorConfigHash: {
34
+ name: string;
35
+ type: string;
36
+ }[];
37
+ };
38
+ export declare const OperatorTypedMessage: {
39
+ OperatorConfigHash: {
40
+ name: string;
41
+ type: string;
42
+ }[];
43
+ EIP712Domain: {
44
+ name: string;
45
+ type: string;
46
+ }[];
47
+ };
48
+ export declare const ENRTypedMessage: {
49
+ ENR: {
50
+ name: string;
51
+ type: string;
52
+ }[];
53
+ EIP712Domain: {
54
+ name: string;
55
+ type: string;
56
+ }[];
57
+ };
58
+ export declare const signCreatorConfigHashPayload: (payload: {
59
+ creator_config_hash: string;
60
+ }, chainId: number) => TypedMessage<typeof CreatorTypedMessage>;
61
+ export declare const signOperatorConfigHashPayload: (payload: {
62
+ operator_config_hash: string;
63
+ }, chainId: number) => TypedMessage<typeof OperatorTypedMessage>;
64
+ export declare const signEnrPayload: (payload: {
65
+ enr: string;
66
+ }, chainId: number) => TypedMessage<typeof ENRTypedMessage>;
67
+ export declare const dkg_algorithm = "default";
68
+ export declare const config_version = "v1.7.0";
69
+ export declare const SDK_VERSION = "1.0.7";
70
+ export declare const DOMAIN_APPLICATION_BUILDER = "00000001";
71
+ export declare const DOMAIN_DEPOSIT = "03000000";
72
+ export declare const GENESIS_VALIDATOR_ROOT = "0000000000000000000000000000000000000000000000000000000000000000";
73
+ export declare enum DefinitionFlow {
74
+ Group = "LP-Group",
75
+ Solo = "LP-Solo",
76
+ Charon = "Charon-Command"
77
+ }
78
+ export declare const DEFAULT_BASE_URL = "https://api.obol.tech";
79
+ export declare const DEFAULT_CHAIN_ID = 1;
@@ -1,5 +1,4 @@
1
- export declare class ConflictError extends Error {
2
- name: string;
3
- constructor();
4
- }
5
- //# sourceMappingURL=errors.d.ts.map
1
+ export declare class ConflictError extends Error {
2
+ name: string;
3
+ constructor();
4
+ }