@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/README.md +21 -0
- package/dist/contracts.d.ts +5 -6
- package/dist/index.d.ts +112 -60
- package/dist/index.js +126 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +126 -38
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +126 -38
- package/dist/sequencer.d.ts +24 -39
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -87,12 +87,12 @@ class VocdoniCensusService extends BaseService {
|
|
|
87
87
|
super(baseURL);
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* Constructs the URI for accessing a census
|
|
91
|
-
* @param
|
|
90
|
+
* Constructs the URI for accessing a census
|
|
91
|
+
* @param relativePath - The relative path
|
|
92
92
|
* @returns The constructed URI for the census
|
|
93
93
|
*/
|
|
94
|
-
getCensusUri(
|
|
95
|
-
return `${this.axios.defaults.baseURL}
|
|
94
|
+
getCensusUri(relativePath) {
|
|
95
|
+
return `${this.axios.defaults.baseURL}${relativePath}`;
|
|
96
96
|
}
|
|
97
97
|
createCensus() {
|
|
98
98
|
return this.request({
|
|
@@ -161,10 +161,13 @@ class VocdoniCensusService extends BaseService {
|
|
|
161
161
|
return this.request({
|
|
162
162
|
method: "POST",
|
|
163
163
|
url: `/censuses/${censusId}/publish`
|
|
164
|
-
}).then((
|
|
165
|
-
...
|
|
166
|
-
|
|
167
|
-
|
|
164
|
+
}).then((apiResponse) => {
|
|
165
|
+
const { censusUri, ...responseWithoutCensusUri } = apiResponse;
|
|
166
|
+
return {
|
|
167
|
+
...responseWithoutCensusUri,
|
|
168
|
+
uri: this.getCensusUri(censusUri)
|
|
169
|
+
};
|
|
170
|
+
});
|
|
168
171
|
}
|
|
169
172
|
// BigQuery endpoints
|
|
170
173
|
getSnapshots(params) {
|
|
@@ -194,13 +197,13 @@ var CensusOrigin = /* @__PURE__ */ ((CensusOrigin2) => {
|
|
|
194
197
|
return CensusOrigin2;
|
|
195
198
|
})(CensusOrigin || {});
|
|
196
199
|
function isBaseCensusProof(proof) {
|
|
197
|
-
return !!proof && typeof proof.root === "string" && typeof proof.address === "string" && typeof proof.
|
|
200
|
+
return !!proof && typeof proof.root === "string" && typeof proof.address === "string" && typeof proof.censusOrigin === "number" && Object.values(CensusOrigin).includes(proof.censusOrigin);
|
|
198
201
|
}
|
|
199
202
|
function isMerkleCensusProof(proof) {
|
|
200
|
-
return isBaseCensusProof(proof) && proof.censusOrigin === 1 /* CensusOriginMerkleTree */ && typeof proof.value === "string" && typeof proof.siblings === "string";
|
|
203
|
+
return isBaseCensusProof(proof) && proof.censusOrigin === 1 /* CensusOriginMerkleTree */ && typeof proof.weight === "string" && typeof proof.value === "string" && typeof proof.siblings === "string";
|
|
201
204
|
}
|
|
202
205
|
function isCSPCensusProof(proof) {
|
|
203
|
-
return isBaseCensusProof(proof) && proof.censusOrigin === 2 /* CensusOriginCSP */ && typeof proof.processId === "string" && typeof proof.publicKey === "string" && typeof proof.signature === "string";
|
|
206
|
+
return isBaseCensusProof(proof) && proof.censusOrigin === 2 /* CensusOriginCSP */ && typeof proof.weight === "string" && typeof proof.processId === "string" && typeof proof.publicKey === "string" && typeof proof.signature === "string";
|
|
204
207
|
}
|
|
205
208
|
function assertMerkleCensusProof(proof) {
|
|
206
209
|
if (!isMerkleCensusProof(proof)) {
|
|
@@ -560,6 +563,27 @@ class VocdoniSequencerService extends BaseService {
|
|
|
560
563
|
throw error;
|
|
561
564
|
}
|
|
562
565
|
}
|
|
566
|
+
async getAddressWeight(processId, address) {
|
|
567
|
+
const participant = await this.request({
|
|
568
|
+
method: "GET",
|
|
569
|
+
url: `/processes/${processId}/participants/${address}`
|
|
570
|
+
});
|
|
571
|
+
return participant.weight;
|
|
572
|
+
}
|
|
573
|
+
async isAddressAbleToVote(processId, address) {
|
|
574
|
+
try {
|
|
575
|
+
await this.request({
|
|
576
|
+
method: "GET",
|
|
577
|
+
url: `/processes/${processId}/participants/${address}`
|
|
578
|
+
});
|
|
579
|
+
return true;
|
|
580
|
+
} catch (error) {
|
|
581
|
+
if (error?.code === 40001) {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
throw error;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
563
587
|
getInfo() {
|
|
564
588
|
return this.request({
|
|
565
589
|
method: "GET",
|
|
@@ -983,7 +1007,6 @@ class ProcessRegistryService extends SmartContractService {
|
|
|
983
1007
|
newProcess(status, startTime, duration, ballotMode, census, metadata, encryptionKey, initStateRoot) {
|
|
984
1008
|
const contractCensus = {
|
|
985
1009
|
censusOrigin: BigInt(census.censusOrigin),
|
|
986
|
-
maxVotes: BigInt(census.maxVotes),
|
|
987
1010
|
censusRoot: census.censusRoot,
|
|
988
1011
|
censusURI: census.censusURI
|
|
989
1012
|
};
|
|
@@ -1014,7 +1037,6 @@ class ProcessRegistryService extends SmartContractService {
|
|
|
1014
1037
|
setProcessCensus(processID, census) {
|
|
1015
1038
|
const contractCensus = {
|
|
1016
1039
|
censusOrigin: BigInt(census.censusOrigin),
|
|
1017
|
-
maxVotes: BigInt(census.maxVotes),
|
|
1018
1040
|
censusRoot: census.censusRoot,
|
|
1019
1041
|
censusURI: census.censusURI
|
|
1020
1042
|
};
|
|
@@ -1183,7 +1205,6 @@ class ProcessOrchestrationService {
|
|
|
1183
1205
|
const census = {
|
|
1184
1206
|
type: Number(rawProcess.census.censusOrigin),
|
|
1185
1207
|
root: rawProcess.census.censusRoot,
|
|
1186
|
-
size: Number(rawProcess.census.maxVotes),
|
|
1187
1208
|
uri: rawProcess.census.censusURI || ""
|
|
1188
1209
|
};
|
|
1189
1210
|
const ballot = {
|
|
@@ -1198,11 +1219,11 @@ class ProcessOrchestrationService {
|
|
|
1198
1219
|
};
|
|
1199
1220
|
return {
|
|
1200
1221
|
processId,
|
|
1201
|
-
title,
|
|
1222
|
+
title: title || "",
|
|
1202
1223
|
description,
|
|
1203
1224
|
census,
|
|
1204
1225
|
ballot,
|
|
1205
|
-
questions,
|
|
1226
|
+
questions: questions || [],
|
|
1206
1227
|
status: Number(rawProcess.status),
|
|
1207
1228
|
creator: rawProcess.organizationId,
|
|
1208
1229
|
startDate: new Date(startTime * 1e3),
|
|
@@ -1210,8 +1231,8 @@ class ProcessOrchestrationService {
|
|
|
1210
1231
|
duration,
|
|
1211
1232
|
timeRemaining,
|
|
1212
1233
|
result: rawProcess.result,
|
|
1213
|
-
|
|
1214
|
-
|
|
1234
|
+
votersCount: Number(rawProcess.votersCount),
|
|
1235
|
+
overwrittenVotesCount: Number(rawProcess.overwrittenVotesCount),
|
|
1215
1236
|
metadataURI: rawProcess.metadataURI,
|
|
1216
1237
|
raw: rawProcess
|
|
1217
1238
|
};
|
|
@@ -1331,20 +1352,27 @@ class ProcessOrchestrationService {
|
|
|
1331
1352
|
const censusConfig = await this.handleCensus(config.census);
|
|
1332
1353
|
const censusRoot = censusConfig.root;
|
|
1333
1354
|
const ballotMode = config.ballot;
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1355
|
+
let metadataUri;
|
|
1356
|
+
if ("metadataUri" in config) {
|
|
1357
|
+
metadataUri = config.metadataUri;
|
|
1358
|
+
} else {
|
|
1359
|
+
const metadata = this.createMetadata(config);
|
|
1360
|
+
const metadataHash = await this.apiService.sequencer.pushMetadata(metadata);
|
|
1361
|
+
metadataUri = this.apiService.sequencer.getMetadataUrl(metadataHash);
|
|
1362
|
+
}
|
|
1337
1363
|
const signature = await signProcessCreation(processId, this.signer);
|
|
1338
1364
|
const sequencerResult = await this.apiService.sequencer.createProcess({
|
|
1339
1365
|
processId,
|
|
1340
|
-
|
|
1366
|
+
census: {
|
|
1367
|
+
censusOrigin: censusConfig.type,
|
|
1368
|
+
censusRoot,
|
|
1369
|
+
censusURI: censusConfig.uri
|
|
1370
|
+
},
|
|
1341
1371
|
ballotMode,
|
|
1342
|
-
signature
|
|
1343
|
-
censusOrigin: censusConfig.type
|
|
1372
|
+
signature
|
|
1344
1373
|
});
|
|
1345
1374
|
const census = {
|
|
1346
1375
|
censusOrigin: censusConfig.type,
|
|
1347
|
-
maxVotes: censusConfig.size.toString(),
|
|
1348
1376
|
censusRoot,
|
|
1349
1377
|
censusURI: censusConfig.uri
|
|
1350
1378
|
};
|
|
@@ -1413,15 +1441,13 @@ class ProcessOrchestrationService {
|
|
|
1413
1441
|
throw new Error("Invalid date format. Use Date object, ISO string, or Unix timestamp.");
|
|
1414
1442
|
}
|
|
1415
1443
|
/**
|
|
1416
|
-
* Creates metadata from the
|
|
1444
|
+
* Creates metadata from the configuration with metadata fields
|
|
1445
|
+
* This method should only be called with ProcessConfigWithMetadata
|
|
1417
1446
|
*/
|
|
1418
1447
|
createMetadata(config) {
|
|
1419
1448
|
const metadata = getElectionMetadataTemplate();
|
|
1420
1449
|
metadata.title.default = config.title;
|
|
1421
1450
|
metadata.description.default = config.description || "";
|
|
1422
|
-
if (!config.questions || config.questions.length === 0) {
|
|
1423
|
-
throw new Error("Questions are required. Please provide at least one question with choices.");
|
|
1424
|
-
}
|
|
1425
1451
|
metadata.questions = config.questions.map((q) => ({
|
|
1426
1452
|
title: { default: q.title },
|
|
1427
1453
|
description: { default: q.description || "" },
|
|
@@ -1886,16 +1912,19 @@ class VoteOrchestrationService {
|
|
|
1886
1912
|
);
|
|
1887
1913
|
const { proof } = await this.generateZkProof(circomInputs);
|
|
1888
1914
|
const signature = await this.signVote(voteId);
|
|
1889
|
-
|
|
1915
|
+
const voteRequest = {
|
|
1890
1916
|
processId: config.processId,
|
|
1891
|
-
censusProof,
|
|
1892
1917
|
ballot: cryptoOutput.ballot,
|
|
1893
1918
|
ballotProof: proof,
|
|
1894
1919
|
ballotInputsHash: cryptoOutput.ballotInputsHash,
|
|
1895
1920
|
address: voterAddress,
|
|
1896
1921
|
signature,
|
|
1897
1922
|
voteId
|
|
1898
|
-
}
|
|
1923
|
+
};
|
|
1924
|
+
if (process.census.censusOrigin === CensusOrigin.CensusOriginCSP) {
|
|
1925
|
+
voteRequest.censusProof = censusProof;
|
|
1926
|
+
}
|
|
1927
|
+
await this.submitVoteRequest(voteRequest);
|
|
1899
1928
|
const status = await this.apiService.sequencer.getVoteStatus(config.processId, voteId);
|
|
1900
1929
|
return {
|
|
1901
1930
|
voteId,
|
|
@@ -2017,9 +2046,15 @@ class VoteOrchestrationService {
|
|
|
2017
2046
|
assertMerkleCensusProof(proof);
|
|
2018
2047
|
return proof;
|
|
2019
2048
|
} else {
|
|
2020
|
-
const
|
|
2021
|
-
|
|
2022
|
-
|
|
2049
|
+
const weight = await this.apiService.sequencer.getAddressWeight(processId, voterAddress);
|
|
2050
|
+
return {
|
|
2051
|
+
root: censusRoot,
|
|
2052
|
+
address: voterAddress,
|
|
2053
|
+
weight,
|
|
2054
|
+
censusOrigin: CensusOrigin.CensusOriginMerkleTree,
|
|
2055
|
+
value: "",
|
|
2056
|
+
siblings: ""
|
|
2057
|
+
};
|
|
2023
2058
|
}
|
|
2024
2059
|
}
|
|
2025
2060
|
if (censusOrigin === CensusOrigin.CensusOriginCSP) {
|
|
@@ -2339,14 +2374,15 @@ class DavinciCrypto {
|
|
|
2339
2374
|
* @param privKey - The private key in hex format
|
|
2340
2375
|
* @param processId - The process ID in hex format
|
|
2341
2376
|
* @param address - The address in hex format
|
|
2377
|
+
* @param weight - The vote weight as a decimal string
|
|
2342
2378
|
* @returns The CSP proof as a parsed JSON object
|
|
2343
2379
|
* @throws if called before `await init()`, or if Go returns an error
|
|
2344
2380
|
*/
|
|
2345
|
-
async cspSign(censusOrigin, privKey, processId, address) {
|
|
2381
|
+
async cspSign(censusOrigin, privKey, processId, address, weight) {
|
|
2346
2382
|
if (!this.initialized) {
|
|
2347
2383
|
throw new Error("DavinciCrypto not initialized \u2014 call `await init()` first");
|
|
2348
2384
|
}
|
|
2349
|
-
const raw = globalThis.DavinciCrypto.cspSign(censusOrigin, privKey, processId, address);
|
|
2385
|
+
const raw = globalThis.DavinciCrypto.cspSign(censusOrigin, privKey, processId, address, weight);
|
|
2350
2386
|
if (raw.error) {
|
|
2351
2387
|
throw new Error(`Go/WASM cspSign error: ${raw.error}`);
|
|
2352
2388
|
}
|
|
@@ -2360,13 +2396,14 @@ class DavinciCrypto {
|
|
|
2360
2396
|
* @param censusOrigin - The census origin type (e.g., CensusOrigin.CensusOriginCSP)
|
|
2361
2397
|
* @param root - The census root
|
|
2362
2398
|
* @param address - The address
|
|
2399
|
+
* @param weight - The vote weight as a decimal string
|
|
2363
2400
|
* @param processId - The process ID
|
|
2364
2401
|
* @param publicKey - The public key
|
|
2365
2402
|
* @param signature - The signature
|
|
2366
2403
|
* @returns The verification result
|
|
2367
2404
|
* @throws if called before `await init()`, or if Go returns an error
|
|
2368
2405
|
*/
|
|
2369
|
-
async cspVerify(censusOrigin, root, address, processId, publicKey, signature) {
|
|
2406
|
+
async cspVerify(censusOrigin, root, address, weight, processId, publicKey, signature) {
|
|
2370
2407
|
if (!this.initialized) {
|
|
2371
2408
|
throw new Error("DavinciCrypto not initialized \u2014 call `await init()` first");
|
|
2372
2409
|
}
|
|
@@ -2374,6 +2411,7 @@ class DavinciCrypto {
|
|
|
2374
2411
|
censusOrigin,
|
|
2375
2412
|
root,
|
|
2376
2413
|
address,
|
|
2414
|
+
weight,
|
|
2377
2415
|
processId,
|
|
2378
2416
|
publicKey,
|
|
2379
2417
|
signature
|
|
@@ -2825,6 +2863,56 @@ class DavinciSDK {
|
|
|
2825
2863
|
}
|
|
2826
2864
|
return this.voteOrchestrator.hasAddressVoted(processId, address);
|
|
2827
2865
|
}
|
|
2866
|
+
/**
|
|
2867
|
+
* Check if an address is able to vote in a process (i.e., is in the census).
|
|
2868
|
+
*
|
|
2869
|
+
* Does NOT require a provider - uses API calls only.
|
|
2870
|
+
*
|
|
2871
|
+
* @param processId - The process ID
|
|
2872
|
+
* @param address - The voter's address
|
|
2873
|
+
* @returns Promise resolving to boolean indicating if the address can vote
|
|
2874
|
+
*
|
|
2875
|
+
* @example
|
|
2876
|
+
* ```typescript
|
|
2877
|
+
* const canVote = await sdk.isAddressAbleToVote(processId, "0x1234567890abcdef...");
|
|
2878
|
+
* if (canVote) {
|
|
2879
|
+
* console.log("This address can vote");
|
|
2880
|
+
* } else {
|
|
2881
|
+
* console.log("This address is not in the census");
|
|
2882
|
+
* }
|
|
2883
|
+
* ```
|
|
2884
|
+
*/
|
|
2885
|
+
async isAddressAbleToVote(processId, address) {
|
|
2886
|
+
if (!this.initialized) {
|
|
2887
|
+
throw new Error(
|
|
2888
|
+
"SDK must be initialized before checking if address can vote. Call sdk.init() first."
|
|
2889
|
+
);
|
|
2890
|
+
}
|
|
2891
|
+
return this.apiService.sequencer.isAddressAbleToVote(processId, address);
|
|
2892
|
+
}
|
|
2893
|
+
/**
|
|
2894
|
+
* Get the voting weight for an address in a process.
|
|
2895
|
+
*
|
|
2896
|
+
* Does NOT require a provider - uses API calls only.
|
|
2897
|
+
*
|
|
2898
|
+
* @param processId - The process ID
|
|
2899
|
+
* @param address - The voter's address
|
|
2900
|
+
* @returns Promise resolving to the address weight as a string
|
|
2901
|
+
*
|
|
2902
|
+
* @example
|
|
2903
|
+
* ```typescript
|
|
2904
|
+
* const weight = await sdk.getAddressWeight(processId, "0x1234567890abcdef...");
|
|
2905
|
+
* console.log("Address weight:", weight);
|
|
2906
|
+
* ```
|
|
2907
|
+
*/
|
|
2908
|
+
async getAddressWeight(processId, address) {
|
|
2909
|
+
if (!this.initialized) {
|
|
2910
|
+
throw new Error(
|
|
2911
|
+
"SDK must be initialized before getting address weight. Call sdk.init() first."
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
return this.apiService.sequencer.getAddressWeight(processId, address);
|
|
2915
|
+
}
|
|
2828
2916
|
/**
|
|
2829
2917
|
* Watch vote status changes in real-time using an async generator.
|
|
2830
2918
|
* This method yields each status change as it happens, perfect for showing
|