@vocdoni/davinci-sdk 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.umd.js CHANGED
@@ -88,12 +88,12 @@
88
88
  super(baseURL);
89
89
  }
90
90
  /**
91
- * Constructs the URI for accessing a census by its root
92
- * @param censusRoot - The census root (hex-prefixed)
91
+ * Constructs the URI for accessing a census
92
+ * @param relativePath - The relative path
93
93
  * @returns The constructed URI for the census
94
94
  */
95
- getCensusUri(censusRoot) {
96
- return `${this.axios.defaults.baseURL}/censuses/${censusRoot}`;
95
+ getCensusUri(relativePath) {
96
+ return `${this.axios.defaults.baseURL}${relativePath}`;
97
97
  }
98
98
  createCensus() {
99
99
  return this.request({
@@ -162,10 +162,13 @@
162
162
  return this.request({
163
163
  method: "POST",
164
164
  url: `/censuses/${censusId}/publish`
165
- }).then((response) => ({
166
- ...response,
167
- uri: this.getCensusUri(response.root)
168
- }));
165
+ }).then((apiResponse) => {
166
+ const { censusUri, ...responseWithoutCensusUri } = apiResponse;
167
+ return {
168
+ ...responseWithoutCensusUri,
169
+ uri: this.getCensusUri(censusUri)
170
+ };
171
+ });
169
172
  }
170
173
  // BigQuery endpoints
171
174
  getSnapshots(params) {
@@ -195,13 +198,13 @@
195
198
  return CensusOrigin2;
196
199
  })(CensusOrigin || {});
197
200
  function isBaseCensusProof(proof) {
198
- return !!proof && typeof proof.root === "string" && typeof proof.address === "string" && typeof proof.weight === "string" && typeof proof.censusOrigin === "number" && Object.values(CensusOrigin).includes(proof.censusOrigin);
201
+ return !!proof && typeof proof.root === "string" && typeof proof.address === "string" && typeof proof.censusOrigin === "number" && Object.values(CensusOrigin).includes(proof.censusOrigin);
199
202
  }
200
203
  function isMerkleCensusProof(proof) {
201
- return isBaseCensusProof(proof) && proof.censusOrigin === 1 /* CensusOriginMerkleTree */ && typeof proof.value === "string" && typeof proof.siblings === "string";
204
+ return isBaseCensusProof(proof) && proof.censusOrigin === 1 /* CensusOriginMerkleTree */ && typeof proof.weight === "string" && typeof proof.value === "string" && typeof proof.siblings === "string";
202
205
  }
203
206
  function isCSPCensusProof(proof) {
204
- return isBaseCensusProof(proof) && proof.censusOrigin === 2 /* CensusOriginCSP */ && typeof proof.processId === "string" && typeof proof.publicKey === "string" && typeof proof.signature === "string";
207
+ return isBaseCensusProof(proof) && proof.censusOrigin === 2 /* CensusOriginCSP */ && typeof proof.weight === "string" && typeof proof.processId === "string" && typeof proof.publicKey === "string" && typeof proof.signature === "string";
205
208
  }
206
209
  function assertMerkleCensusProof(proof) {
207
210
  if (!isMerkleCensusProof(proof)) {
@@ -561,6 +564,27 @@
561
564
  throw error;
562
565
  }
563
566
  }
567
+ async getAddressWeight(processId, address) {
568
+ const participant = await this.request({
569
+ method: "GET",
570
+ url: `/processes/${processId}/participants/${address}`
571
+ });
572
+ return participant.weight;
573
+ }
574
+ async isAddressAbleToVote(processId, address) {
575
+ try {
576
+ await this.request({
577
+ method: "GET",
578
+ url: `/processes/${processId}/participants/${address}`
579
+ });
580
+ return true;
581
+ } catch (error) {
582
+ if (error?.code === 40001) {
583
+ return false;
584
+ }
585
+ throw error;
586
+ }
587
+ }
564
588
  getInfo() {
565
589
  return this.request({
566
590
  method: "GET",
@@ -984,7 +1008,6 @@
984
1008
  newProcess(status, startTime, duration, ballotMode, census, metadata, encryptionKey, initStateRoot) {
985
1009
  const contractCensus = {
986
1010
  censusOrigin: BigInt(census.censusOrigin),
987
- maxVotes: BigInt(census.maxVotes),
988
1011
  censusRoot: census.censusRoot,
989
1012
  censusURI: census.censusURI
990
1013
  };
@@ -1015,7 +1038,6 @@
1015
1038
  setProcessCensus(processID, census) {
1016
1039
  const contractCensus = {
1017
1040
  censusOrigin: BigInt(census.censusOrigin),
1018
- maxVotes: BigInt(census.maxVotes),
1019
1041
  censusRoot: census.censusRoot,
1020
1042
  censusURI: census.censusURI
1021
1043
  };
@@ -1184,7 +1206,6 @@
1184
1206
  const census = {
1185
1207
  type: Number(rawProcess.census.censusOrigin),
1186
1208
  root: rawProcess.census.censusRoot,
1187
- size: Number(rawProcess.census.maxVotes),
1188
1209
  uri: rawProcess.census.censusURI || ""
1189
1210
  };
1190
1211
  const ballot = {
@@ -1199,11 +1220,11 @@
1199
1220
  };
1200
1221
  return {
1201
1222
  processId,
1202
- title,
1223
+ title: title || "",
1203
1224
  description,
1204
1225
  census,
1205
1226
  ballot,
1206
- questions,
1227
+ questions: questions || [],
1207
1228
  status: Number(rawProcess.status),
1208
1229
  creator: rawProcess.organizationId,
1209
1230
  startDate: new Date(startTime * 1e3),
@@ -1211,8 +1232,8 @@
1211
1232
  duration,
1212
1233
  timeRemaining,
1213
1234
  result: rawProcess.result,
1214
- voteCount: Number(rawProcess.voteCount),
1215
- voteOverwriteCount: Number(rawProcess.voteOverwriteCount),
1235
+ votersCount: Number(rawProcess.votersCount),
1236
+ overwrittenVotesCount: Number(rawProcess.overwrittenVotesCount),
1216
1237
  metadataURI: rawProcess.metadataURI,
1217
1238
  raw: rawProcess
1218
1239
  };
@@ -1332,20 +1353,27 @@
1332
1353
  const censusConfig = await this.handleCensus(config.census);
1333
1354
  const censusRoot = censusConfig.root;
1334
1355
  const ballotMode = config.ballot;
1335
- const metadata = this.createMetadata(config);
1336
- const metadataHash = await this.apiService.sequencer.pushMetadata(metadata);
1337
- const metadataUri = this.apiService.sequencer.getMetadataUrl(metadataHash);
1356
+ let metadataUri;
1357
+ if ("metadataUri" in config) {
1358
+ metadataUri = config.metadataUri;
1359
+ } else {
1360
+ const metadata = this.createMetadata(config);
1361
+ const metadataHash = await this.apiService.sequencer.pushMetadata(metadata);
1362
+ metadataUri = this.apiService.sequencer.getMetadataUrl(metadataHash);
1363
+ }
1338
1364
  const signature = await signProcessCreation(processId, this.signer);
1339
1365
  const sequencerResult = await this.apiService.sequencer.createProcess({
1340
1366
  processId,
1341
- censusRoot,
1367
+ census: {
1368
+ censusOrigin: censusConfig.type,
1369
+ censusRoot,
1370
+ censusURI: censusConfig.uri
1371
+ },
1342
1372
  ballotMode,
1343
- signature,
1344
- censusOrigin: censusConfig.type
1373
+ signature
1345
1374
  });
1346
1375
  const census = {
1347
1376
  censusOrigin: censusConfig.type,
1348
- maxVotes: censusConfig.size.toString(),
1349
1377
  censusRoot,
1350
1378
  censusURI: censusConfig.uri
1351
1379
  };
@@ -1414,15 +1442,13 @@
1414
1442
  throw new Error("Invalid date format. Use Date object, ISO string, or Unix timestamp.");
1415
1443
  }
1416
1444
  /**
1417
- * Creates metadata from the simplified configuration
1445
+ * Creates metadata from the configuration with metadata fields
1446
+ * This method should only be called with ProcessConfigWithMetadata
1418
1447
  */
1419
1448
  createMetadata(config) {
1420
1449
  const metadata = getElectionMetadataTemplate();
1421
1450
  metadata.title.default = config.title;
1422
1451
  metadata.description.default = config.description || "";
1423
- if (!config.questions || config.questions.length === 0) {
1424
- throw new Error("Questions are required. Please provide at least one question with choices.");
1425
- }
1426
1452
  metadata.questions = config.questions.map((q) => ({
1427
1453
  title: { default: q.title },
1428
1454
  description: { default: q.description || "" },
@@ -1887,16 +1913,19 @@
1887
1913
  );
1888
1914
  const { proof } = await this.generateZkProof(circomInputs);
1889
1915
  const signature = await this.signVote(voteId);
1890
- await this.submitVoteRequest({
1916
+ const voteRequest = {
1891
1917
  processId: config.processId,
1892
- censusProof,
1893
1918
  ballot: cryptoOutput.ballot,
1894
1919
  ballotProof: proof,
1895
1920
  ballotInputsHash: cryptoOutput.ballotInputsHash,
1896
1921
  address: voterAddress,
1897
1922
  signature,
1898
1923
  voteId
1899
- });
1924
+ };
1925
+ if (process.census.censusOrigin === CensusOrigin.CensusOriginCSP) {
1926
+ voteRequest.censusProof = censusProof;
1927
+ }
1928
+ await this.submitVoteRequest(voteRequest);
1900
1929
  const status = await this.apiService.sequencer.getVoteStatus(config.processId, voteId);
1901
1930
  return {
1902
1931
  voteId,
@@ -2018,9 +2047,15 @@
2018
2047
  assertMerkleCensusProof(proof);
2019
2048
  return proof;
2020
2049
  } else {
2021
- const proof = await this.apiService.census.getCensusProof(censusRoot, voterAddress);
2022
- assertMerkleCensusProof(proof);
2023
- return proof;
2050
+ const weight = await this.apiService.sequencer.getAddressWeight(processId, voterAddress);
2051
+ return {
2052
+ root: censusRoot,
2053
+ address: voterAddress,
2054
+ weight,
2055
+ censusOrigin: CensusOrigin.CensusOriginMerkleTree,
2056
+ value: "",
2057
+ siblings: ""
2058
+ };
2024
2059
  }
2025
2060
  }
2026
2061
  if (censusOrigin === CensusOrigin.CensusOriginCSP) {
@@ -2340,14 +2375,15 @@
2340
2375
  * @param privKey - The private key in hex format
2341
2376
  * @param processId - The process ID in hex format
2342
2377
  * @param address - The address in hex format
2378
+ * @param weight - The vote weight as a decimal string
2343
2379
  * @returns The CSP proof as a parsed JSON object
2344
2380
  * @throws if called before `await init()`, or if Go returns an error
2345
2381
  */
2346
- async cspSign(censusOrigin, privKey, processId, address) {
2382
+ async cspSign(censusOrigin, privKey, processId, address, weight) {
2347
2383
  if (!this.initialized) {
2348
2384
  throw new Error("DavinciCrypto not initialized \u2014 call `await init()` first");
2349
2385
  }
2350
- const raw = globalThis.DavinciCrypto.cspSign(censusOrigin, privKey, processId, address);
2386
+ const raw = globalThis.DavinciCrypto.cspSign(censusOrigin, privKey, processId, address, weight);
2351
2387
  if (raw.error) {
2352
2388
  throw new Error(`Go/WASM cspSign error: ${raw.error}`);
2353
2389
  }
@@ -2361,13 +2397,14 @@
2361
2397
  * @param censusOrigin - The census origin type (e.g., CensusOrigin.CensusOriginCSP)
2362
2398
  * @param root - The census root
2363
2399
  * @param address - The address
2400
+ * @param weight - The vote weight as a decimal string
2364
2401
  * @param processId - The process ID
2365
2402
  * @param publicKey - The public key
2366
2403
  * @param signature - The signature
2367
2404
  * @returns The verification result
2368
2405
  * @throws if called before `await init()`, or if Go returns an error
2369
2406
  */
2370
- async cspVerify(censusOrigin, root, address, processId, publicKey, signature) {
2407
+ async cspVerify(censusOrigin, root, address, weight, processId, publicKey, signature) {
2371
2408
  if (!this.initialized) {
2372
2409
  throw new Error("DavinciCrypto not initialized \u2014 call `await init()` first");
2373
2410
  }
@@ -2375,6 +2412,7 @@
2375
2412
  censusOrigin,
2376
2413
  root,
2377
2414
  address,
2415
+ weight,
2378
2416
  processId,
2379
2417
  publicKey,
2380
2418
  signature
@@ -2826,6 +2864,56 @@
2826
2864
  }
2827
2865
  return this.voteOrchestrator.hasAddressVoted(processId, address);
2828
2866
  }
2867
+ /**
2868
+ * Check if an address is able to vote in a process (i.e., is in the census).
2869
+ *
2870
+ * Does NOT require a provider - uses API calls only.
2871
+ *
2872
+ * @param processId - The process ID
2873
+ * @param address - The voter's address
2874
+ * @returns Promise resolving to boolean indicating if the address can vote
2875
+ *
2876
+ * @example
2877
+ * ```typescript
2878
+ * const canVote = await sdk.isAddressAbleToVote(processId, "0x1234567890abcdef...");
2879
+ * if (canVote) {
2880
+ * console.log("This address can vote");
2881
+ * } else {
2882
+ * console.log("This address is not in the census");
2883
+ * }
2884
+ * ```
2885
+ */
2886
+ async isAddressAbleToVote(processId, address) {
2887
+ if (!this.initialized) {
2888
+ throw new Error(
2889
+ "SDK must be initialized before checking if address can vote. Call sdk.init() first."
2890
+ );
2891
+ }
2892
+ return this.apiService.sequencer.isAddressAbleToVote(processId, address);
2893
+ }
2894
+ /**
2895
+ * Get the voting weight for an address in a process.
2896
+ *
2897
+ * Does NOT require a provider - uses API calls only.
2898
+ *
2899
+ * @param processId - The process ID
2900
+ * @param address - The voter's address
2901
+ * @returns Promise resolving to the address weight as a string
2902
+ *
2903
+ * @example
2904
+ * ```typescript
2905
+ * const weight = await sdk.getAddressWeight(processId, "0x1234567890abcdef...");
2906
+ * console.log("Address weight:", weight);
2907
+ * ```
2908
+ */
2909
+ async getAddressWeight(processId, address) {
2910
+ if (!this.initialized) {
2911
+ throw new Error(
2912
+ "SDK must be initialized before getting address weight. Call sdk.init() first."
2913
+ );
2914
+ }
2915
+ return this.apiService.sequencer.getAddressWeight(processId, address);
2916
+ }
2829
2917
  /**
2830
2918
  * Watch vote status changes in real-time using an async generator.
2831
2919
  * This method yields each status change as it happens, perfect for showing
@@ -144,7 +144,6 @@ interface BallotMode {
144
144
  }
145
145
  interface CensusData {
146
146
  censusOrigin: CensusOrigin;
147
- maxVotes: string;
148
147
  censusRoot: string;
149
148
  censusURI: string;
150
149
  }
@@ -155,14 +154,13 @@ interface EncryptionKey {
155
154
 
156
155
  interface CreateProcessRequest {
157
156
  processId: string;
158
- censusRoot: string;
157
+ census: {
158
+ censusOrigin: CensusOrigin;
159
+ censusRoot: string;
160
+ censusURI: string;
161
+ };
159
162
  ballotMode: BallotMode;
160
163
  signature: string;
161
- /**
162
- * The censusOrigin specifies the origin type of the census used in the request.
163
- * This attribute allows the API to determine how the census data should be processed or verified.
164
- */
165
- censusOrigin: CensusOrigin;
166
164
  }
167
165
  interface CreateProcessResponse {
168
166
  processId: string;
@@ -176,36 +174,14 @@ interface GetProcessResponse {
176
174
  organizationId: string;
177
175
  encryptionKey: EncryptionKey;
178
176
  stateRoot: string;
179
- result: string[];
180
- startTime: number;
177
+ result: string[] | null;
178
+ startTime: string;
181
179
  duration: number;
182
180
  metadataURI: string;
183
181
  ballotMode: BallotMode;
184
182
  census: CensusData;
185
- metadata: {
186
- title: Record<string, string>;
187
- description: Record<string, string>;
188
- media: {
189
- header: string;
190
- logo: string;
191
- };
192
- questions: {
193
- title: Record<string, string>;
194
- description: Record<string, string>;
195
- choices: {
196
- title: Record<string, string>;
197
- value: number;
198
- meta: Record<string, string>;
199
- }[];
200
- meta: Record<string, string>;
201
- }[];
202
- processType: {
203
- name: string;
204
- properties: Record<string, string>;
205
- };
206
- };
207
- voteCount: string;
208
- voteOverwrittenCount: string;
183
+ votersCount: string;
184
+ overwrittenVotesCount: string;
209
185
  isAcceptingVotes: boolean;
210
186
  sequencerStats: {
211
187
  stateTransitionCount: number;
@@ -235,8 +211,8 @@ interface VoteProof {
235
211
  interface VoteRequest {
236
212
  /** The `processId` you obtained when creating the process. */
237
213
  processId: string;
238
- /** Your Merkle‐proof that you're in the census. */
239
- censusProof: CensusProof;
214
+ /** Your census proof (only required for CSP, not for MerkleTree). */
215
+ censusProof?: CensusProof;
240
216
  /** Your encrypted ballot. */
241
217
  ballot: VoteBallot;
242
218
  /** The zkSNARK proof that the ballot is well‐formed. */
@@ -302,6 +278,10 @@ interface WorkerStats {
302
278
  interface WorkersResponse {
303
279
  workers: WorkerStats[];
304
280
  }
281
+ interface ParticipantInfoResponse {
282
+ key: string;
283
+ weight: string;
284
+ }
305
285
 
306
286
  /**
307
287
  * Creates the signature message for process creation.
@@ -421,6 +401,7 @@ interface CSPSignOutput {
421
401
  censusOrigin: CensusOrigin;
422
402
  root: string;
423
403
  address: string;
404
+ weight: string;
424
405
  processId: string;
425
406
  publicKey: string;
426
407
  signature: string;
@@ -431,7 +412,7 @@ interface RawResult<T = any> {
431
412
  }
432
413
  interface GoDavinciCryptoWasm {
433
414
  proofInputs(inputJson: string): RawResult<DavinciCryptoOutput>;
434
- cspSign(censusOrigin: number, privKey: string, processId: string, address: string): RawResult<CSPSignOutput>;
415
+ cspSign(censusOrigin: number, privKey: string, processId: string, address: string, weight: string): RawResult<CSPSignOutput>;
435
416
  cspVerify(cspProof: string): RawResult<boolean>;
436
417
  cspCensusRoot(censusOrigin: number, privKey: string): RawResult<{
437
418
  root: string;
@@ -491,22 +472,24 @@ declare class DavinciCrypto {
491
472
  * @param privKey - The private key in hex format
492
473
  * @param processId - The process ID in hex format
493
474
  * @param address - The address in hex format
475
+ * @param weight - The vote weight as a decimal string
494
476
  * @returns The CSP proof as a parsed JSON object
495
477
  * @throws if called before `await init()`, or if Go returns an error
496
478
  */
497
- cspSign(censusOrigin: CensusOrigin, privKey: string, processId: string, address: string): Promise<CSPSignOutput>;
479
+ cspSign(censusOrigin: CensusOrigin, privKey: string, processId: string, address: string, weight: string): Promise<CSPSignOutput>;
498
480
  /**
499
481
  * Verify a CSP (Credential Service Provider) proof.
500
482
  * @param censusOrigin - The census origin type (e.g., CensusOrigin.CensusOriginCSP)
501
483
  * @param root - The census root
502
484
  * @param address - The address
485
+ * @param weight - The vote weight as a decimal string
503
486
  * @param processId - The process ID
504
487
  * @param publicKey - The public key
505
488
  * @param signature - The signature
506
489
  * @returns The verification result
507
490
  * @throws if called before `await init()`, or if Go returns an error
508
491
  */
509
- cspVerify(censusOrigin: CensusOrigin, root: string, address: string, processId: string, publicKey: string, signature: string): Promise<boolean>;
492
+ cspVerify(censusOrigin: CensusOrigin, root: string, address: string, weight: string, processId: string, publicKey: string, signature: string): Promise<boolean>;
510
493
  /**
511
494
  * Generate a CSP (Credential Service Provider) census root.
512
495
  * @param censusOrigin - The census origin type (e.g., CensusOrigin.CensusOriginCSP)
@@ -526,6 +509,8 @@ declare class VocdoniSequencerService extends BaseService {
526
509
  submitVote(vote: VoteRequest): Promise<void>;
527
510
  getVoteStatus(processId: string, voteId: string): Promise<VoteStatusResponse>;
528
511
  hasAddressVoted(processId: string, address: string): Promise<boolean>;
512
+ getAddressWeight(processId: string, address: string): Promise<string>;
513
+ isAddressAbleToVote(processId: string, address: string): Promise<boolean>;
529
514
  getInfo(): Promise<InfoResponse>;
530
515
  pushMetadata(metadata: ElectionMetadata): Promise<string>;
531
516
  getMetadata(hashOrUrl: string): Promise<ElectionMetadata>;
@@ -535,4 +520,4 @@ declare class VocdoniSequencerService extends BaseService {
535
520
  }
536
521
 
537
522
  export { CircomProof, DavinciCrypto, VocdoniSequencerService, VoteStatus, createProcessSignatureMessage, signProcessCreation, validateProcessId };
538
- export type { CSPSignOutput, CircomProofOptions, CreateProcessRequest, CreateProcessResponse, DavinciCryptoCiphertext, DavinciCryptoInputs, DavinciCryptoOptions, DavinciCryptoOutput, GetProcessResponse, Groth16Proof, InfoResponse, ListProcessesResponse, ProofInputs, SequencerStats, VoteBallot, VoteCiphertext, VoteProof, VoteRequest, VoteStatusResponse, WorkerStats, WorkersResponse };
523
+ export type { CSPSignOutput, CircomProofOptions, CreateProcessRequest, CreateProcessResponse, DavinciCryptoCiphertext, DavinciCryptoInputs, DavinciCryptoOptions, DavinciCryptoOutput, GetProcessResponse, Groth16Proof, InfoResponse, ListProcessesResponse, ParticipantInfoResponse, ProofInputs, SequencerStats, VoteBallot, VoteCiphertext, VoteProof, VoteRequest, VoteStatusResponse, WorkerStats, WorkersResponse };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vocdoni/davinci-sdk",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -72,7 +72,7 @@
72
72
  },
73
73
  "dependencies": {
74
74
  "@ethereumjs/common": "^4.4.0",
75
- "@vocdoni/davinci-contracts": "0.0.21",
75
+ "@vocdoni/davinci-contracts": "0.0.28",
76
76
  "axios": "^1.8.4",
77
77
  "ethers": "^6.7.1",
78
78
  "snarkjs": "^0.7.5"