@peeramid-labs/sdk 3.7.3 → 3.8.0
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/cli/abis/MAODistribution.js +10 -0
- package/cli/abis/MAODistribution.js.map +1 -1
- package/cli/abis/RankToken.js +84 -0
- package/cli/abis/RankToken.js.map +1 -1
- package/cli/abis/superinterface.js +9 -3
- package/cli/abis/superinterface.js.map +1 -1
- package/cli/cli/commands/blockchain/index.js +9 -0
- package/cli/cli/commands/blockchain/index.js.map +1 -0
- package/cli/cli/commands/blockchain/mine.js +45 -0
- package/cli/cli/commands/blockchain/mine.js.map +1 -0
- package/cli/cli/commands/distributions/add.js +57 -31
- package/cli/cli/commands/distributions/add.js.map +1 -1
- package/cli/cli/commands/distributions/index.js +3 -1
- package/cli/cli/commands/distributions/index.js.map +1 -1
- package/cli/cli/commands/distributions/remove.js +67 -0
- package/cli/cli/commands/distributions/remove.js.map +1 -0
- package/cli/cli/commands/fellowship/create.js +97 -54
- package/cli/cli/commands/fellowship/create.js.map +1 -1
- package/cli/cli/commands/fellowship/game/cancel.js +45 -0
- package/cli/cli/commands/fellowship/game/cancel.js.map +1 -0
- package/cli/cli/commands/fellowship/game/create.js +91 -0
- package/cli/cli/commands/fellowship/game/create.js.map +1 -0
- package/cli/cli/commands/fellowship/game/end-turn.js +43 -0
- package/cli/cli/commands/fellowship/game/end-turn.js.map +1 -0
- package/cli/cli/commands/fellowship/game/index.js +31 -0
- package/cli/cli/commands/fellowship/game/index.js.map +1 -0
- package/cli/cli/commands/fellowship/game/join.js +131 -0
- package/cli/cli/commands/fellowship/game/join.js.map +1 -0
- package/cli/cli/commands/fellowship/{games.js → game/list.js} +6 -6
- package/cli/cli/commands/fellowship/game/list.js.map +1 -0
- package/cli/cli/commands/fellowship/game/propose.js +178 -0
- package/cli/cli/commands/fellowship/game/propose.js.map +1 -0
- package/cli/cli/commands/fellowship/game/start.js +117 -0
- package/cli/cli/commands/fellowship/game/start.js.map +1 -0
- package/cli/cli/commands/fellowship/game/vote.js +114 -0
- package/cli/cli/commands/fellowship/game/vote.js.map +1 -0
- package/cli/cli/commands/fellowship/index.js +4 -2
- package/cli/cli/commands/fellowship/index.js.map +1 -1
- package/cli/cli/commands/fellowship/params.js +49 -0
- package/cli/cli/commands/fellowship/params.js.map +1 -0
- package/cli/cli/commands/getPk.js +48 -0
- package/cli/cli/commands/getPk.js.map +1 -0
- package/cli/cli/commands/playbook.js +92 -0
- package/cli/cli/commands/playbook.js.map +1 -0
- package/cli/cli/getPk.js +62 -0
- package/cli/cli/getPk.js.map +1 -0
- package/cli/cli/helpers.js +64 -0
- package/cli/cli/helpers.js.map +1 -0
- package/cli/cli/index.js +6 -0
- package/cli/cli/index.js.map +1 -1
- package/cli/cli/utils.js +64 -0
- package/cli/cli/utils.js.map +1 -0
- package/cli/rankify/GameMaster.js +1066 -0
- package/cli/rankify/GameMaster.js.map +1 -0
- package/cli/rankify/InstanceBase.js +61 -36
- package/cli/rankify/InstanceBase.js.map +1 -1
- package/cli/rankify/MAODistributor.js +28 -0
- package/cli/rankify/MAODistributor.js.map +1 -1
- package/cli/rankify/Player.js +355 -0
- package/cli/rankify/Player.js.map +1 -0
- package/cli/utils/ApiError.js +11 -6
- package/cli/utils/ApiError.js.map +1 -1
- package/cli/utils/blockchain.js +62 -0
- package/cli/utils/blockchain.js.map +1 -0
- package/docs/classes/GameMaster.md +23 -63
- package/docs/classes/InstanceBase.md +23 -29
- package/docs/classes/InstancePlayer.md +27 -33
- package/docs/classes/MAODistributorClient.md +18 -1
- package/docs/docs/classes/GameMaster.md +23 -63
- package/docs/docs/classes/InstanceBase.md +23 -29
- package/docs/docs/classes/InstancePlayer.md +27 -33
- package/docs/docs/classes/MAODistributorClient.md +18 -1
- package/docs/docs/index.md +4 -4
- package/docs/index.md +4 -4
- package/lib.commonjs/abis/MAODistribution.d.ts +8 -0
- package/lib.commonjs/abis/MAODistribution.d.ts.map +1 -1
- package/lib.commonjs/abis/MAODistribution.js +10 -0
- package/lib.commonjs/abis/MAODistribution.js.map +1 -1
- package/lib.commonjs/abis/RankToken.d.ts +65 -0
- package/lib.commonjs/abis/RankToken.d.ts.map +1 -1
- package/lib.commonjs/abis/RankToken.js +84 -0
- package/lib.commonjs/abis/RankToken.js.map +1 -1
- package/lib.commonjs/abis/index.d.ts +74 -1
- package/lib.commonjs/abis/index.d.ts.map +1 -1
- package/lib.commonjs/abis/superinterface.d.ts +1 -1
- package/lib.commonjs/abis/superinterface.d.ts.map +1 -1
- package/lib.commonjs/abis/superinterface.js +9 -3
- package/lib.commonjs/abis/superinterface.js.map +1 -1
- package/lib.commonjs/multipass/MultipassBase.d.ts.map +1 -1
- package/lib.commonjs/multipass/Registrar.d.ts.map +1 -1
- package/lib.commonjs/rankify/GameMaster.d.ts +27 -34
- package/lib.commonjs/rankify/GameMaster.d.ts.map +1 -1
- package/lib.commonjs/rankify/GameMaster.js +218 -217
- package/lib.commonjs/rankify/GameMaster.js.map +1 -1
- package/lib.commonjs/rankify/InstanceBase.d.ts +65 -51
- package/lib.commonjs/rankify/InstanceBase.d.ts.map +1 -1
- package/lib.commonjs/rankify/InstanceBase.js +61 -36
- package/lib.commonjs/rankify/InstanceBase.js.map +1 -1
- package/lib.commonjs/rankify/MAODistributor.d.ts +1036 -0
- package/lib.commonjs/rankify/MAODistributor.d.ts.map +1 -1
- package/lib.commonjs/rankify/MAODistributor.js +28 -0
- package/lib.commonjs/rankify/MAODistributor.js.map +1 -1
- package/lib.commonjs/rankify/Player.d.ts.map +1 -1
- package/lib.commonjs/rankify/RankToken.d.ts.map +1 -1
- package/lib.commonjs/utils/ApiError.d.ts.map +1 -1
- package/lib.commonjs/utils/ApiError.js +11 -6
- package/lib.commonjs/utils/ApiError.js.map +1 -1
- package/lib.commonjs/utils/artifacts.d.ts.map +1 -1
- package/lib.commonjs/utils/blockchain.d.ts +32 -0
- package/lib.commonjs/utils/blockchain.d.ts.map +1 -0
- package/lib.commonjs/utils/blockchain.js +62 -0
- package/lib.commonjs/utils/blockchain.js.map +1 -0
- package/lib.commonjs/utils/index.d.ts.map +1 -1
- package/lib.commonjs/utils/permutations.d.ts.map +1 -1
- package/lib.esm/abis/MAODistribution.d.ts +8 -0
- package/lib.esm/abis/MAODistribution.d.ts.map +1 -1
- package/lib.esm/abis/MAODistribution.js +10 -0
- package/lib.esm/abis/MAODistribution.js.map +1 -1
- package/lib.esm/abis/RankToken.d.ts +65 -0
- package/lib.esm/abis/RankToken.d.ts.map +1 -1
- package/lib.esm/abis/RankToken.js +84 -0
- package/lib.esm/abis/RankToken.js.map +1 -1
- package/lib.esm/abis/index.d.ts +74 -1
- package/lib.esm/abis/index.d.ts.map +1 -1
- package/lib.esm/abis/superinterface.d.ts +1 -1
- package/lib.esm/abis/superinterface.d.ts.map +1 -1
- package/lib.esm/abis/superinterface.js +9 -3
- package/lib.esm/abis/superinterface.js.map +1 -1
- package/lib.esm/multipass/MultipassBase.d.ts.map +1 -1
- package/lib.esm/multipass/Registrar.d.ts.map +1 -1
- package/lib.esm/rankify/GameMaster.d.ts +27 -34
- package/lib.esm/rankify/GameMaster.d.ts.map +1 -1
- package/lib.esm/rankify/GameMaster.js +219 -218
- package/lib.esm/rankify/GameMaster.js.map +1 -1
- package/lib.esm/rankify/InstanceBase.d.ts +65 -51
- package/lib.esm/rankify/InstanceBase.d.ts.map +1 -1
- package/lib.esm/rankify/InstanceBase.js +61 -36
- package/lib.esm/rankify/InstanceBase.js.map +1 -1
- package/lib.esm/rankify/MAODistributor.d.ts +1036 -0
- package/lib.esm/rankify/MAODistributor.d.ts.map +1 -1
- package/lib.esm/rankify/MAODistributor.js +28 -0
- package/lib.esm/rankify/MAODistributor.js.map +1 -1
- package/lib.esm/rankify/Player.d.ts.map +1 -1
- package/lib.esm/rankify/RankToken.d.ts.map +1 -1
- package/lib.esm/utils/ApiError.d.ts.map +1 -1
- package/lib.esm/utils/ApiError.js +11 -6
- package/lib.esm/utils/ApiError.js.map +1 -1
- package/lib.esm/utils/artifacts.d.ts.map +1 -1
- package/lib.esm/utils/blockchain.d.ts +32 -0
- package/lib.esm/utils/blockchain.d.ts.map +1 -0
- package/lib.esm/utils/blockchain.js +58 -0
- package/lib.esm/utils/blockchain.js.map +1 -0
- package/lib.esm/utils/index.d.ts.map +1 -1
- package/lib.esm/utils/permutations.d.ts.map +1 -1
- package/package.json +8 -8
- package/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15.groth16.vkey.json +1 -0
- package/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15.groth16.zkey +0 -0
- package/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15.r1cs +0 -0
- package/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15.sym +19202 -0
- package/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15_artifacts.json +84 -0
- package/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15_js/ProposalsIntegrity15.wasm +0 -0
- package/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15_js/generate_witness.js +21 -0
- package/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15_js/witness_calculator.js +384 -0
- package/zk_artifacts/types/core/ProposalsIntegrity15.ts +163 -0
- package/zk_artifacts/types/core/index.ts +6 -0
- package/zk_artifacts/types/hardhat.d.ts +14 -0
- package/zk_artifacts/types/helpers.ts +44 -0
- package/zk_artifacts/types/index.ts +8 -0
- package/zk_artifacts/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15.groth16.vkey.json +1 -0
- package/zk_artifacts/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15.groth16.zkey +0 -0
- package/zk_artifacts/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15.r1cs +0 -0
- package/zk_artifacts/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15.sym +19202 -0
- package/zk_artifacts/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15_artifacts.json +84 -0
- package/zk_artifacts/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15_js/ProposalsIntegrity15.wasm +0 -0
- package/zk_artifacts/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15_js/generate_witness.js +21 -0
- package/zk_artifacts/zk_artifacts/circuits/proposals_integrity_15.circom/ProposalsIntegrity15_js/witness_calculator.js +384 -0
- package/zk_artifacts/zk_artifacts/types/core/ProposalsIntegrity15.ts +163 -0
- package/zk_artifacts/zk_artifacts/types/core/index.ts +6 -0
- package/zk_artifacts/zk_artifacts/types/hardhat.d.ts +14 -0
- package/zk_artifacts/zk_artifacts/types/helpers.ts +44 -0
- package/zk_artifacts/zk_artifacts/types/index.ts +8 -0
- package/cli/cli/commands/fellowship/games.js.map +0 -1
|
@@ -0,0 +1,1066 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.GameMaster = void 0;
|
|
7
|
+
const viem_1 = require("viem");
|
|
8
|
+
const abis_1 = require("../abis");
|
|
9
|
+
const InstanceBase_1 = __importDefault(require("./InstanceBase"));
|
|
10
|
+
const types_1 = require("../types");
|
|
11
|
+
const utils_1 = require("../utils");
|
|
12
|
+
const accounts_1 = require("viem/accounts");
|
|
13
|
+
const log_1 = require("../utils/log");
|
|
14
|
+
const circomlibjs_1 = require("circomlibjs");
|
|
15
|
+
const aes_1 = __importDefault(require("crypto-js/aes"));
|
|
16
|
+
const crypto_js_1 = __importDefault(require("crypto-js"));
|
|
17
|
+
const zkit_1 = require("@solarity/zkit");
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const permutations_1 = require("../utils/permutations");
|
|
20
|
+
/**
|
|
21
|
+
* GameMaster class for managing game state and cryptographic operations in Rankify
|
|
22
|
+
* Extends InstanceBase to provide game master specific functionality
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
class GameMaster {
|
|
26
|
+
/**
|
|
27
|
+
* Creates a new GameMaster instance
|
|
28
|
+
|
|
29
|
+
* @param walletClient - Viem wallet client for transactions
|
|
30
|
+
* @param publicClient - Viem public client for reading state
|
|
31
|
+
* @param chainId - Chain ID of the network
|
|
32
|
+
*/
|
|
33
|
+
constructor({ walletClient, chainId, publicClient, }) {
|
|
34
|
+
this.maxSlotSizeForProofs = 15;
|
|
35
|
+
/**
|
|
36
|
+
* Decrypts a proposal for a specific game turn
|
|
37
|
+
* @param proposal - The encrypted proposal
|
|
38
|
+
* @param turn - The turn number
|
|
39
|
+
* @param instanceAddress - The address of the instance
|
|
40
|
+
* @param gameId - The ID of the game
|
|
41
|
+
* @param proposer - The address of the proposer
|
|
42
|
+
* @returns The decrypted proposal
|
|
43
|
+
*/
|
|
44
|
+
this.decryptProposal = async ({ proposal, turn, instanceAddress, gameId, proposer, instance, }) => {
|
|
45
|
+
const _instance = instance ?? new InstanceBase_1.default({ instanceAddress, publicClient: this.publicClient, chainId: this.chainId });
|
|
46
|
+
const proposerPubKey = await _instance.getPlayerPubKey({
|
|
47
|
+
instanceAddress,
|
|
48
|
+
gameId,
|
|
49
|
+
player: proposer,
|
|
50
|
+
});
|
|
51
|
+
const sharedKey = _instance.sharedSigner({
|
|
52
|
+
publicKey: proposerPubKey,
|
|
53
|
+
privateKey: await this.gameKey({ gameId, contractAddress: instanceAddress }),
|
|
54
|
+
gameId,
|
|
55
|
+
turn,
|
|
56
|
+
contractAddress: instanceAddress,
|
|
57
|
+
chainId: this.chainId,
|
|
58
|
+
});
|
|
59
|
+
(0, log_1.logger)(`Decrypting proposal ${proposal} with shared key (hashed value: ${(0, viem_1.keccak256)(sharedKey)})`);
|
|
60
|
+
const decryptedProposal = aes_1.default.decrypt(proposal, sharedKey).toString(crypto_js_1.default.enc.Utf8);
|
|
61
|
+
(0, log_1.logger)(`Decrypted proposal ${decryptedProposal}`);
|
|
62
|
+
return decryptedProposal;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Decrypts proposals for a specific game turn
|
|
66
|
+
* @param gameId - ID of the game
|
|
67
|
+
* @param turn - Turn number
|
|
68
|
+
* @param proposer - Optional proposer address to filter proposals
|
|
69
|
+
* @returns Array of decrypted proposals with proposer addresses
|
|
70
|
+
*/
|
|
71
|
+
this.decryptProposals = async ({ instanceAddress, gameId, turn, players, padToMaxSize = false, permute = false, }) => {
|
|
72
|
+
(0, log_1.logger)(`Getting proposals for instance ${instanceAddress}, game ${gameId}, turn ${turn.toString()}`);
|
|
73
|
+
const ProposalSubmittedEvents = await this.publicClient.getContractEvents({
|
|
74
|
+
abi: abis_1.RankifyDiamondInstanceAbi,
|
|
75
|
+
address: instanceAddress,
|
|
76
|
+
eventName: "ProposalSubmitted",
|
|
77
|
+
args: { gameId: gameId, turn: turn },
|
|
78
|
+
fromBlock: 0n,
|
|
79
|
+
});
|
|
80
|
+
(0, log_1.logger)(`Found ${ProposalSubmittedEvents.length} proposals`);
|
|
81
|
+
const instance = new InstanceBase_1.default({ instanceAddress, publicClient: this.publicClient, chainId: this.chainId });
|
|
82
|
+
(0, log_1.logger)(`Decrypting ${ProposalSubmittedEvents.length} proposals`);
|
|
83
|
+
const proposalsForPlayers = await Promise.all((players)?.map(async (player) => {
|
|
84
|
+
const log = ProposalSubmittedEvents.find((log) => log.args.proposer === player);
|
|
85
|
+
if (!log) {
|
|
86
|
+
return {
|
|
87
|
+
proposer: player,
|
|
88
|
+
proposal: "",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
(0, log_1.logger)(`Decrypting proposal ${log.args.proposer}`);
|
|
93
|
+
if (!log.args.proposer)
|
|
94
|
+
throw new Error("No proposer");
|
|
95
|
+
if (!log.args.encryptedProposal)
|
|
96
|
+
throw new Error("No proposalEncryptedByGM");
|
|
97
|
+
return {
|
|
98
|
+
proposer: log.args.proposer,
|
|
99
|
+
proposal: await this.decryptProposal({
|
|
100
|
+
proposal: log.args.encryptedProposal,
|
|
101
|
+
turn: turn,
|
|
102
|
+
instanceAddress: instanceAddress,
|
|
103
|
+
gameId: gameId,
|
|
104
|
+
proposer: log.args.proposer,
|
|
105
|
+
instance,
|
|
106
|
+
}),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}));
|
|
110
|
+
if (permute) {
|
|
111
|
+
const proposalsPermuted = await this.permuteArray({ array: proposalsForPlayers, gameId, turn, verifierAddress: instanceAddress });
|
|
112
|
+
return padToMaxSize ? this.padProposalsArrayWithZeroAddress(proposalsPermuted) : proposalsPermuted;
|
|
113
|
+
}
|
|
114
|
+
return padToMaxSize ? this.padProposalsArrayWithZeroAddress(proposalsForPlayers) : proposalsForPlayers;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Generates a deterministic permutation for a specific game turn
|
|
118
|
+
* @param gameId - ID of the game
|
|
119
|
+
* @param turn - Turn number
|
|
120
|
+
* @param size - Size of the permutation
|
|
121
|
+
* @param verifierAddress - Address of the verifier
|
|
122
|
+
* @returns The generated permutation, secret, and commitment
|
|
123
|
+
*/
|
|
124
|
+
this.getPermutation = async ({ gameId, turn, size, verifierAddress, }) => {
|
|
125
|
+
const turnSalt = await this.getTurnSalt({ gameId, turn, verifierAddress });
|
|
126
|
+
// Create deterministic seed from game parameters and GM's signature
|
|
127
|
+
// Use the seed to generate permutation
|
|
128
|
+
const permutation = Array.from({ length: this.maxSlotSizeForProofs }, (_, i) => i);
|
|
129
|
+
// Fisher-Yates shuffle with deterministic randomness
|
|
130
|
+
for (let i = size - 1; i >= 0; i--) {
|
|
131
|
+
// Generate deterministic random number for this position
|
|
132
|
+
const randHash = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["uint256", "uint256"], [turnSalt, BigInt(i)]));
|
|
133
|
+
const rand = BigInt(randHash);
|
|
134
|
+
const j = Number(rand % BigInt(i + 1));
|
|
135
|
+
// Swap elements
|
|
136
|
+
[permutation[i], permutation[j]] = [permutation[j], permutation[i]];
|
|
137
|
+
}
|
|
138
|
+
// Ensure inactive slots map to themselves
|
|
139
|
+
for (let i = size; i < this.maxSlotSizeForProofs; i++) {
|
|
140
|
+
permutation[i] = i;
|
|
141
|
+
}
|
|
142
|
+
return { permutation, turnSalt };
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Generates a deterministic permutation for a specific game turn
|
|
146
|
+
* @param gameId - ID of the game
|
|
147
|
+
* @param turn - Turn number
|
|
148
|
+
* @param size - Size of the permutation
|
|
149
|
+
* @param verifierAddress - Address of the verifier
|
|
150
|
+
* @returns The generated permutation, secret, and commitment
|
|
151
|
+
*/
|
|
152
|
+
this.generateDeterministicPermutation = async ({ gameId, turn, size = 15, verifierAddress, }) => {
|
|
153
|
+
// This is kept secret to generate witness
|
|
154
|
+
// Create deterministic seed from game parameters and GM's signature
|
|
155
|
+
const { permutation, turnSalt } = await this.getPermutation({ gameId, turn, size, verifierAddress });
|
|
156
|
+
// Generate commitment
|
|
157
|
+
const poseidon = await (0, circomlibjs_1.buildPoseidon)();
|
|
158
|
+
const PoseidonFirst = BigInt(poseidon.F.toObject(poseidon([permutation[0], permutation[1], permutation[2], permutation[3], permutation[4]])));
|
|
159
|
+
const PoseidonSecond = BigInt(poseidon.F.toObject(poseidon([PoseidonFirst, permutation[5], permutation[6], permutation[7], permutation[8], permutation[9]])));
|
|
160
|
+
const PoseidonThird = BigInt(poseidon.F.toObject(poseidon([PoseidonSecond, permutation[10], permutation[11], permutation[12], permutation[13], permutation[14]])));
|
|
161
|
+
const commitment = BigInt(poseidon.F.toObject(poseidon([PoseidonThird, turnSalt])));
|
|
162
|
+
return {
|
|
163
|
+
permutation,
|
|
164
|
+
turnSalt,
|
|
165
|
+
commitment,
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Permutes an array based on a deterministic permutation
|
|
170
|
+
* @param array - Array to permute
|
|
171
|
+
* @param gameId - ID of the game
|
|
172
|
+
* @param turn - Turn number
|
|
173
|
+
* @param verifierAddress - Address of the verifier
|
|
174
|
+
* @returns The permuted array
|
|
175
|
+
*/
|
|
176
|
+
this.permuteArray = async ({ array, gameId, turn, verifierAddress, }) => {
|
|
177
|
+
const { permutation } = await this.getPermutation({ gameId, turn, size: array.length, verifierAddress });
|
|
178
|
+
return (0, permutations_1.permuteArray)({ array, permutation });
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Reverses a permutation of an array
|
|
182
|
+
* @param permutedArray - Array to reverse
|
|
183
|
+
* @param gameId - ID of the game
|
|
184
|
+
* @param turn - Turn number
|
|
185
|
+
* @param verifierAddress - Address of the verifier
|
|
186
|
+
* @returns The original array
|
|
187
|
+
*/
|
|
188
|
+
this.reversePermutation = async ({ permutedArray, gameId, turn, verifierAddress, }) => {
|
|
189
|
+
const { permutation } = await this.getPermutation({ gameId, turn, size: permutedArray.length, verifierAddress });
|
|
190
|
+
return (0, permutations_1.reversePermutation)({ array: permutedArray, permutation });
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Generates a salt for a specific game turn
|
|
194
|
+
* @param gameId - ID of the game
|
|
195
|
+
* @param turn - Turn number
|
|
196
|
+
* @param verifierAddress - Address of the verifier
|
|
197
|
+
* @returns Generated salt as Hex
|
|
198
|
+
*/
|
|
199
|
+
this.getTurnSalt = async ({ gameId, turn, verifierAddress, }) => {
|
|
200
|
+
const gameKey = await this.gameKey({ gameId, contractAddress: verifierAddress });
|
|
201
|
+
const instance = new InstanceBase_1.default({
|
|
202
|
+
instanceAddress: verifierAddress,
|
|
203
|
+
publicClient: this.publicClient,
|
|
204
|
+
chainId: this.chainId,
|
|
205
|
+
});
|
|
206
|
+
const seed = instance.pkdf({
|
|
207
|
+
privateKey: gameKey,
|
|
208
|
+
turn,
|
|
209
|
+
gameId,
|
|
210
|
+
contractAddress: verifierAddress,
|
|
211
|
+
chainId: this.chainId,
|
|
212
|
+
scope: "turnSalt",
|
|
213
|
+
});
|
|
214
|
+
return BigInt(seed);
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Generates a salt for a specific player in a game turn
|
|
218
|
+
* @param gameId - ID of the game
|
|
219
|
+
* @param turn - Turn number
|
|
220
|
+
* @param player - Address of the player
|
|
221
|
+
* @param verifierAddress - Address of the verifier
|
|
222
|
+
* @param size - Size of the permutation
|
|
223
|
+
* @returns Generated salt as Hex
|
|
224
|
+
*/
|
|
225
|
+
this.getTurnPlayersSalt = async ({ gameId, turn, player, verifierAddress, size, }) => {
|
|
226
|
+
(0, log_1.logger)(`Generating vote salt for player ${player} in game ${gameId}, turn ${turn}`);
|
|
227
|
+
const result = await this.generateDeterministicPermutation({
|
|
228
|
+
gameId,
|
|
229
|
+
turn: turn - 1n,
|
|
230
|
+
verifierAddress,
|
|
231
|
+
size,
|
|
232
|
+
}).then((perm) => {
|
|
233
|
+
return (0, viem_1.keccak256)((0, viem_1.encodePacked)(["address", "uint256"], [player, perm.turnSalt]));
|
|
234
|
+
});
|
|
235
|
+
(0, log_1.logger)(`Generated vote salt for player ${player}`);
|
|
236
|
+
return result;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Finds the index of a player's ongoing proposal
|
|
240
|
+
* @param gameId - ID of the game
|
|
241
|
+
* @param player - Address of the player
|
|
242
|
+
* @returns Index of the player's proposal, -1 if not found
|
|
243
|
+
*/
|
|
244
|
+
this.findPlayerOngoingProposalIndex = async ({ instanceAddress, gameId, player, turn, }) => {
|
|
245
|
+
if (!turn) {
|
|
246
|
+
turn = await this.currentTurn({ instanceAddress, gameId });
|
|
247
|
+
}
|
|
248
|
+
const players = await this.getPlayers({ instanceAddress, gameId });
|
|
249
|
+
const decryptedProposalsPermuted = await this.decryptProposals({ instanceAddress, gameId, turn: turn - 1n, players: [...players], permute: true });
|
|
250
|
+
return decryptedProposalsPermuted.findIndex((p) => p?.proposer === player);
|
|
251
|
+
};
|
|
252
|
+
this.validateJoinGame = async (props) => {
|
|
253
|
+
const { gameId, participant, instanceAddress } = props;
|
|
254
|
+
try {
|
|
255
|
+
const gameState = await this.getGameState({ gameId, instanceAddress });
|
|
256
|
+
if (gameState.gamePhase !== types_1.gameStatusEnum.open) {
|
|
257
|
+
return { result: false, errorMessage: "Game is not open for registration" };
|
|
258
|
+
}
|
|
259
|
+
if (gameState.players.length === Number(gameState.maxPlayerCnt)) {
|
|
260
|
+
return { result: false, errorMessage: "Game is already full" };
|
|
261
|
+
}
|
|
262
|
+
if (gameState.players.indexOf(participant) !== -1) {
|
|
263
|
+
return { result: false, errorMessage: "Player already registered" };
|
|
264
|
+
}
|
|
265
|
+
return { result: true, errorMessage: "" };
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
throw await (0, utils_1.handleRPCError)(e);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* Signs a joining game event
|
|
273
|
+
* @param gameId - ID of the game
|
|
274
|
+
* @param participant - Address of the participant
|
|
275
|
+
* @param instanceAddress - Address of the game instance
|
|
276
|
+
* @returns Signature and gmCommitment
|
|
277
|
+
*/
|
|
278
|
+
this.signJoiningGame = async (props, timeToJoin = 60 * 10) => {
|
|
279
|
+
if (!this.walletClient.account)
|
|
280
|
+
throw new Error("No account");
|
|
281
|
+
(0, log_1.logger)(`Signing joining game..`);
|
|
282
|
+
const { gameId, participant, instanceAddress, participantPubKeyHash } = props;
|
|
283
|
+
const { result: isValid, errorMessage } = await this.validateJoinGame({ gameId, participant, instanceAddress });
|
|
284
|
+
if (!isValid) {
|
|
285
|
+
throw new Error(errorMessage);
|
|
286
|
+
}
|
|
287
|
+
const baseInstance = new InstanceBase_1.default({ instanceAddress, publicClient: this.publicClient, chainId: this.chainId });
|
|
288
|
+
const eip712 = await baseInstance.getEIP712Domain();
|
|
289
|
+
(0, log_1.logger)({
|
|
290
|
+
gameId: props.gameId,
|
|
291
|
+
participant: props.participant,
|
|
292
|
+
instanceAddress: props.instanceAddress,
|
|
293
|
+
chainId: this.chainId,
|
|
294
|
+
name: eip712.name,
|
|
295
|
+
version: eip712.version,
|
|
296
|
+
gameMaster: this.walletClient.account?.address,
|
|
297
|
+
participantPubKeyHash,
|
|
298
|
+
}, 2);
|
|
299
|
+
const deadline = BigInt(Math.floor(Date.now() / 1000) + timeToJoin);
|
|
300
|
+
//ToDo This is placeholder for now, we will need it later in staking
|
|
301
|
+
const gmCommitment = (0, viem_1.stringToHex)("0x123131231311", { size: 32 });
|
|
302
|
+
console.log({
|
|
303
|
+
name: eip712.name,
|
|
304
|
+
version: eip712.version,
|
|
305
|
+
chainId: this.chainId,
|
|
306
|
+
verifyingContract: instanceAddress,
|
|
307
|
+
}, {
|
|
308
|
+
participant,
|
|
309
|
+
gameId,
|
|
310
|
+
gmCommitment,
|
|
311
|
+
deadline,
|
|
312
|
+
participantPubKeyHash,
|
|
313
|
+
});
|
|
314
|
+
const signature = await this.walletClient.signTypedData({
|
|
315
|
+
domain: {
|
|
316
|
+
name: eip712.name,
|
|
317
|
+
version: eip712.version,
|
|
318
|
+
chainId: this.chainId,
|
|
319
|
+
verifyingContract: instanceAddress,
|
|
320
|
+
},
|
|
321
|
+
types: {
|
|
322
|
+
AttestJoiningGame: [
|
|
323
|
+
{ type: "address", name: "participant" },
|
|
324
|
+
{ type: "uint256", name: "gameId" },
|
|
325
|
+
{ type: "bytes32", name: "gmCommitment" },
|
|
326
|
+
{ type: "uint256", name: "deadline" },
|
|
327
|
+
{ type: "bytes32", name: "participantPubKeyHash" },
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
message: {
|
|
331
|
+
participant,
|
|
332
|
+
gameId,
|
|
333
|
+
gmCommitment,
|
|
334
|
+
deadline,
|
|
335
|
+
participantPubKeyHash,
|
|
336
|
+
},
|
|
337
|
+
primaryType: "AttestJoiningGame",
|
|
338
|
+
account: this.walletClient.account,
|
|
339
|
+
});
|
|
340
|
+
return { signature, gmCommitment, deadline };
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* Submits a vote for proposals
|
|
344
|
+
* @param gameId - ID of the game
|
|
345
|
+
* @param vote - Array of vote values
|
|
346
|
+
* @param voter - Address of the voter
|
|
347
|
+
* @returns Transaction hash
|
|
348
|
+
*/
|
|
349
|
+
this.submitVote = async ({ instanceAddress, gameId, vote, voter, voterSignature, ballotHash, ballotId, }) => {
|
|
350
|
+
const players = await this.getPlayers({ instanceAddress, gameId });
|
|
351
|
+
const turn = await this.currentTurn({ instanceAddress, gameId });
|
|
352
|
+
const validationResult = await this.validateVote({ gameId, turn, voter, vote, instanceAddress, players: [...players] });
|
|
353
|
+
if (!validationResult.result) {
|
|
354
|
+
throw new Error('Vote validation failed: ' + validationResult.reason);
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
const { request } = await this.publicClient.simulateContract({
|
|
358
|
+
account: this.walletClient.account,
|
|
359
|
+
address: instanceAddress,
|
|
360
|
+
abi: abis_1.RankifyDiamondInstanceAbi,
|
|
361
|
+
functionName: "submitVote",
|
|
362
|
+
args: [gameId, ballotId, voter, viem_1.zeroHash, voterSignature, ballotHash],
|
|
363
|
+
});
|
|
364
|
+
return this.walletClient.writeContract(request);
|
|
365
|
+
}
|
|
366
|
+
catch (e) {
|
|
367
|
+
throw await (0, utils_1.handleRPCError)(e);
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Gets the current turn progress in percent value
|
|
372
|
+
* @param instanceAddress - Address of the instance
|
|
373
|
+
* @param gameState - Current game state
|
|
374
|
+
* @param gameId - ID of the game
|
|
375
|
+
* @returns Current turn progress
|
|
376
|
+
*/
|
|
377
|
+
this.getTurnProgress = async ({ instanceAddress, gameState, gameId }) => {
|
|
378
|
+
const prevTurnProposals = await this.decryptProposals({ instanceAddress, gameId, turn: gameState.currentTurn - 1n, players: [...gameState.players] });
|
|
379
|
+
const proposalCountInPrevTurn = prevTurnProposals.filter(p => p.proposal !== "").length;
|
|
380
|
+
const proposalsMadeInCurrentTurn = await this.decryptProposals({ instanceAddress, gameId, turn: gameState.currentTurn, players: [...gameState.players] });
|
|
381
|
+
const proposalCountInCurrentTurn = proposalsMadeInCurrentTurn.filter(p => p.proposal !== "").length;
|
|
382
|
+
if (proposalCountInPrevTurn === 0) {
|
|
383
|
+
return (proposalCountInCurrentTurn / gameState.players.length) * 100;
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
const votesMadeInCurrentTurn = await this.decryptTurnVotes({ instanceAddress, gameId, turn: gameState.currentTurn, players: [...gameState.players] });
|
|
387
|
+
const votesCount = votesMadeInCurrentTurn.filter(v => this.hasVoted({ vote: v })).length;
|
|
388
|
+
return ((votesCount + proposalCountInCurrentTurn) / (gameState.players.length * 2)) * 100;
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
this.validateVote = async ({ gameId, turn, voter, vote, instanceAddress, players }) => {
|
|
392
|
+
const decryptedProposals = await this.decryptProposals({ instanceAddress, gameId, turn: turn - 1n, players, permute: true });
|
|
393
|
+
//Invalid vote length
|
|
394
|
+
if (vote.length !== decryptedProposals.length) {
|
|
395
|
+
return { result: false, reason: "Invalid vote length" };
|
|
396
|
+
}
|
|
397
|
+
// Check if points used are correct (Quadratic voting system)
|
|
398
|
+
let pointsUsed = 0n;
|
|
399
|
+
for (let i = 0; i < vote.length; i++) {
|
|
400
|
+
if (vote[i] > 0n) {
|
|
401
|
+
pointsUsed += 1n ** vote[i];
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const gameState = await this.getGameState({ gameId, instanceAddress });
|
|
405
|
+
if (pointsUsed > gameState.voteCredits) {
|
|
406
|
+
return { result: false, reason: "Too many points used" };
|
|
407
|
+
}
|
|
408
|
+
if (pointsUsed < gameState.voteCredits) {
|
|
409
|
+
return { result: false, reason: "Not all points used" };
|
|
410
|
+
}
|
|
411
|
+
// Check if voter voted for a non-proposed player or their own proposal
|
|
412
|
+
for (let i = 0; i < vote.length; i++) {
|
|
413
|
+
if (vote[i] === 0n)
|
|
414
|
+
continue;
|
|
415
|
+
if (decryptedProposals[i].proposal === "" || decryptedProposals[i].proposer === viem_1.zeroAddress) {
|
|
416
|
+
return { result: false, reason: "Vote for non existing proposal" };
|
|
417
|
+
}
|
|
418
|
+
if (decryptedProposals[i].proposer === voter) {
|
|
419
|
+
return { result: false, reason: "Voter cannot vote for their own proposal" };
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
// Valid vote
|
|
423
|
+
return { result: true, reason: "" };
|
|
424
|
+
};
|
|
425
|
+
this.padProposalsArrayWithZeroAddress = (proposals) => {
|
|
426
|
+
if (proposals.length < this.maxSlotSizeForProofs) {
|
|
427
|
+
for (let i = proposals.length; i < this.maxSlotSizeForProofs; i++) {
|
|
428
|
+
proposals.push({
|
|
429
|
+
proposer: viem_1.zeroAddress,
|
|
430
|
+
proposal: "",
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return proposals;
|
|
435
|
+
};
|
|
436
|
+
/**
|
|
437
|
+
* Types for proposal submission
|
|
438
|
+
*/
|
|
439
|
+
this.proposalTypes = {
|
|
440
|
+
SubmitProposal: [
|
|
441
|
+
{ type: "uint256", name: "gameId" },
|
|
442
|
+
{ type: "address", name: "proposer" },
|
|
443
|
+
{ type: "string", name: "encryptedProposal" },
|
|
444
|
+
{ type: "uint256", name: "commitment" },
|
|
445
|
+
],
|
|
446
|
+
};
|
|
447
|
+
this.signProposal = async ({ verifierAddress, proposer, gameId, encryptedProposal, commitment, eip712, }) => {
|
|
448
|
+
// Generate typed data hash matching Solidity's keccak256(abi.encode(...))
|
|
449
|
+
if (!this.walletClient.account)
|
|
450
|
+
throw new Error("No account");
|
|
451
|
+
return this.walletClient.signTypedData({
|
|
452
|
+
domain: {
|
|
453
|
+
name: eip712.name,
|
|
454
|
+
version: eip712.version,
|
|
455
|
+
chainId: this.chainId,
|
|
456
|
+
verifyingContract: verifierAddress,
|
|
457
|
+
},
|
|
458
|
+
types: this.proposalTypes,
|
|
459
|
+
message: {
|
|
460
|
+
gameId,
|
|
461
|
+
proposer,
|
|
462
|
+
encryptedProposal,
|
|
463
|
+
commitment,
|
|
464
|
+
},
|
|
465
|
+
primaryType: "SubmitProposal",
|
|
466
|
+
account: this.walletClient.account,
|
|
467
|
+
});
|
|
468
|
+
};
|
|
469
|
+
this.proposalValues = async ({ instanceAddress, gameId, proposal, proposer, turn, }) => {
|
|
470
|
+
const instance = new InstanceBase_1.default({ instanceAddress, publicClient: this.publicClient, chainId: this.chainId });
|
|
471
|
+
const proposerPubKey = await instance.getPlayerPubKey({
|
|
472
|
+
instanceAddress,
|
|
473
|
+
gameId,
|
|
474
|
+
player: proposer,
|
|
475
|
+
});
|
|
476
|
+
const sharedKey = instance.sharedSigner({
|
|
477
|
+
publicKey: proposerPubKey,
|
|
478
|
+
privateKey: await this.gameKey({ gameId, contractAddress: instanceAddress }),
|
|
479
|
+
gameId,
|
|
480
|
+
turn,
|
|
481
|
+
contractAddress: instanceAddress,
|
|
482
|
+
chainId: this.chainId,
|
|
483
|
+
});
|
|
484
|
+
// const poseidon = await buildPoseidon();
|
|
485
|
+
const proposalValue = BigInt((0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [proposal])));
|
|
486
|
+
const randomnessValue = BigInt((0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [sharedKey])));
|
|
487
|
+
// Calculate commitment using poseidon
|
|
488
|
+
return {
|
|
489
|
+
proposalValue,
|
|
490
|
+
randomnessValue,
|
|
491
|
+
proposal,
|
|
492
|
+
};
|
|
493
|
+
};
|
|
494
|
+
/**
|
|
495
|
+
* Encrypts a proposal
|
|
496
|
+
* @param proposal - Proposal to encrypt
|
|
497
|
+
* @param turn - Turn number
|
|
498
|
+
* @param instanceAddress - Address of the game instance
|
|
499
|
+
* @param gameId - ID of the game
|
|
500
|
+
* @param proposerPubKey - Public key of the proposer
|
|
501
|
+
* @returns Encrypted proposal and shared key
|
|
502
|
+
*/
|
|
503
|
+
this.encryptProposal = async ({ proposal, turn, instanceAddress, gameId, proposerPubKey, }) => {
|
|
504
|
+
const instance = new InstanceBase_1.default({ instanceAddress, publicClient: this.publicClient, chainId: this.chainId });
|
|
505
|
+
const sharedKey = instance.sharedSigner({
|
|
506
|
+
publicKey: proposerPubKey,
|
|
507
|
+
privateKey: await this.gameKey({ gameId, contractAddress: instanceAddress }),
|
|
508
|
+
gameId,
|
|
509
|
+
turn,
|
|
510
|
+
contractAddress: instanceAddress,
|
|
511
|
+
chainId: this.chainId,
|
|
512
|
+
});
|
|
513
|
+
(0, log_1.logger)(`Encrypting proposal ${proposal} with shared key (hashed value: ${(0, viem_1.keccak256)(sharedKey)})`);
|
|
514
|
+
const encryptedProposal = aes_1.default.encrypt(proposal, sharedKey).toString();
|
|
515
|
+
(0, log_1.logger)(`Encrypted proposal ${encryptedProposal}`);
|
|
516
|
+
return { encryptedProposal, sharedKey };
|
|
517
|
+
};
|
|
518
|
+
/**
|
|
519
|
+
* Attests a proposal
|
|
520
|
+
* @param instanceAddress - Address of the game instance
|
|
521
|
+
* @param gameId - ID of the game
|
|
522
|
+
* @param proposal - Proposal to attest
|
|
523
|
+
* @param proposerPubKey - Public key of the proposer
|
|
524
|
+
* @param turn - Turn number
|
|
525
|
+
* @returns The attested proposal
|
|
526
|
+
*/
|
|
527
|
+
this.attestProposal = async ({ instanceAddress, gameId, proposal, proposerPubKey, turn, }) => {
|
|
528
|
+
const proposerAddress = (0, accounts_1.publicKeyToAddress)(proposerPubKey);
|
|
529
|
+
(0, log_1.logger)(`Creating proposal secrets for player ${proposerAddress} in game ${gameId}`);
|
|
530
|
+
const poseidon = await (0, circomlibjs_1.buildPoseidon)();
|
|
531
|
+
const instance = new InstanceBase_1.default({ instanceAddress, publicClient: this.publicClient, chainId: this.chainId });
|
|
532
|
+
const { encryptedProposal, sharedKey } = await this.encryptProposal({
|
|
533
|
+
proposal,
|
|
534
|
+
turn,
|
|
535
|
+
instanceAddress,
|
|
536
|
+
gameId,
|
|
537
|
+
proposerPubKey,
|
|
538
|
+
});
|
|
539
|
+
const proposalValue = BigInt((0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [proposal])));
|
|
540
|
+
const randomnessValue = BigInt((0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [sharedKey])));
|
|
541
|
+
// Calculate commitment using poseidon
|
|
542
|
+
const hash = poseidon([proposalValue, randomnessValue]);
|
|
543
|
+
const poseidonCommitment = BigInt(poseidon.F.toObject(hash));
|
|
544
|
+
const eip712 = await instance.getEIP712Domain();
|
|
545
|
+
const signature = await this.signProposal({
|
|
546
|
+
verifierAddress: instanceAddress,
|
|
547
|
+
proposer: proposerAddress,
|
|
548
|
+
gameId,
|
|
549
|
+
encryptedProposal,
|
|
550
|
+
commitment: poseidonCommitment,
|
|
551
|
+
eip712,
|
|
552
|
+
});
|
|
553
|
+
const params = {
|
|
554
|
+
gameId,
|
|
555
|
+
encryptedProposal,
|
|
556
|
+
commitment: poseidonCommitment,
|
|
557
|
+
proposer: proposerAddress,
|
|
558
|
+
gmSignature: signature,
|
|
559
|
+
};
|
|
560
|
+
(0, log_1.logger)(`Generated proposal secrets with commitment ${poseidonCommitment}`);
|
|
561
|
+
return {
|
|
562
|
+
submissionParams: params,
|
|
563
|
+
proposal,
|
|
564
|
+
proposerAddress,
|
|
565
|
+
proposalValue,
|
|
566
|
+
randomnessValue,
|
|
567
|
+
};
|
|
568
|
+
};
|
|
569
|
+
/**
|
|
570
|
+
* Submits a proposal to the game
|
|
571
|
+
* @param gameId - ID of the game
|
|
572
|
+
* @param commitmentHash - Hash of the proposal commitment
|
|
573
|
+
* @param encryptedProposal - Encrypted proposal data
|
|
574
|
+
* @param proposer - Address of the proposer
|
|
575
|
+
* @returns Transaction hash
|
|
576
|
+
*/
|
|
577
|
+
this.submitProposal = async ({ instanceAddress, submissionParams, proposerSignature, }) => {
|
|
578
|
+
const txParams = [
|
|
579
|
+
{
|
|
580
|
+
...submissionParams,
|
|
581
|
+
proposerSignature,
|
|
582
|
+
},
|
|
583
|
+
];
|
|
584
|
+
try {
|
|
585
|
+
const { request } = await this.publicClient.simulateContract({
|
|
586
|
+
account: this.walletClient.account,
|
|
587
|
+
address: instanceAddress,
|
|
588
|
+
abi: abis_1.RankifyDiamondInstanceAbi,
|
|
589
|
+
functionName: "submitProposal",
|
|
590
|
+
args: txParams,
|
|
591
|
+
});
|
|
592
|
+
return this.walletClient.writeContract(request);
|
|
593
|
+
}
|
|
594
|
+
catch (e) {
|
|
595
|
+
throw await (0, utils_1.handleRPCError)(e);
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
/**
|
|
599
|
+
* Decrypts votes for a specific game turn
|
|
600
|
+
* @param gameId - ID of the game
|
|
601
|
+
* @param turn - Turn number
|
|
602
|
+
* @returns Array of decrypted votes with player addresses
|
|
603
|
+
*/
|
|
604
|
+
this.decryptTurnVotes = async ({ instanceAddress, gameId, turn, players = [], }) => {
|
|
605
|
+
(0, log_1.logger)(`Decrypting votes for game ${BigInt(gameId)} turn ${turn} at address ${instanceAddress} at ${await this.publicClient.getBlockNumber()} block`);
|
|
606
|
+
const VoteSubmittedEvents = await this.publicClient.getContractEvents({
|
|
607
|
+
address: instanceAddress,
|
|
608
|
+
abi: abis_1.RankifyDiamondInstanceAbi,
|
|
609
|
+
eventName: "VoteSubmitted",
|
|
610
|
+
fromBlock: 0n,
|
|
611
|
+
args: { turn, gameId },
|
|
612
|
+
});
|
|
613
|
+
(0, log_1.logger)(`Found ${VoteSubmittedEvents.length} events`);
|
|
614
|
+
if (VoteSubmittedEvents.length === 0)
|
|
615
|
+
return [];
|
|
616
|
+
//Decrypting votes from events
|
|
617
|
+
const votes = [];
|
|
618
|
+
console.log("Events:", VoteSubmittedEvents);
|
|
619
|
+
for (const event of VoteSubmittedEvents) {
|
|
620
|
+
console.log("Event:", event);
|
|
621
|
+
if (!event.args.player)
|
|
622
|
+
throw new Error("No player in event");
|
|
623
|
+
if (!event.args.sealedBallotId)
|
|
624
|
+
throw new Error("No sealedBallotId in event");
|
|
625
|
+
const turnKey = await this.calculateSharedTurnKey({
|
|
626
|
+
instanceAddress,
|
|
627
|
+
gameId,
|
|
628
|
+
turn,
|
|
629
|
+
player: event.args.player,
|
|
630
|
+
});
|
|
631
|
+
const decryptedVotes = await this.decryptVote(event.args.sealedBallotId, turnKey);
|
|
632
|
+
votes.push({
|
|
633
|
+
player: event.args.player,
|
|
634
|
+
votes: decryptedVotes,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
const votesForEachPlayer = await Promise.all(players.map(async (player) => {
|
|
638
|
+
const vote = votes.find((v) => v.player === player);
|
|
639
|
+
if (!vote?.votes)
|
|
640
|
+
return players.map(() => 0n);
|
|
641
|
+
return vote.votes;
|
|
642
|
+
}));
|
|
643
|
+
return votesForEachPlayer;
|
|
644
|
+
};
|
|
645
|
+
/**
|
|
646
|
+
* Checks if the current turn can be ended
|
|
647
|
+
* @param gameId - ID of the game
|
|
648
|
+
* @returns Boolean indicating if turn can be ended
|
|
649
|
+
*/
|
|
650
|
+
this.canEndTurn = async ({ instanceAddress, gameId }) => {
|
|
651
|
+
const canEndTurn = await this.publicClient.readContract({
|
|
652
|
+
address: instanceAddress,
|
|
653
|
+
abi: abis_1.RankifyDiamondInstanceAbi,
|
|
654
|
+
functionName: "canEndTurn",
|
|
655
|
+
args: [gameId],
|
|
656
|
+
});
|
|
657
|
+
if (!canEndTurn)
|
|
658
|
+
return false;
|
|
659
|
+
//Extra check to not allow to end turn if current phase timeout is not passed and progress is less than 100% (probably must be fixed in contracts!)
|
|
660
|
+
// TODO: if fixed in contracts, remove this check
|
|
661
|
+
const gameState = await this.getGameState({ instanceAddress, gameId });
|
|
662
|
+
const lastBlock = await this.publicClient.getBlock({ blockNumber: BigInt(await this.publicClient.getBlockNumber()) });
|
|
663
|
+
if (gameState.currentPhaseTimeoutAt > lastBlock.timestamp) {
|
|
664
|
+
const turnProgress = await this.getTurnProgress({ instanceAddress, gameState, gameId });
|
|
665
|
+
if (turnProgress <= 100)
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
return true;
|
|
669
|
+
};
|
|
670
|
+
/**
|
|
671
|
+
* Gets the current turn number
|
|
672
|
+
* @param gameId - ID of the game
|
|
673
|
+
* @returns Current turn number
|
|
674
|
+
*/
|
|
675
|
+
this.currentTurn = async ({ instanceAddress, gameId }) => {
|
|
676
|
+
try {
|
|
677
|
+
return this.publicClient.readContract({
|
|
678
|
+
address: instanceAddress,
|
|
679
|
+
abi: abis_1.RankifyDiamondInstanceAbi,
|
|
680
|
+
functionName: "getTurn",
|
|
681
|
+
args: [gameId],
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
catch (e) {
|
|
685
|
+
throw await (0, utils_1.handleRPCError)(e);
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
/**
|
|
689
|
+
* Gets the list of players in the game
|
|
690
|
+
* @param gameId - ID of the game
|
|
691
|
+
* @returns Array of player addresses
|
|
692
|
+
*/
|
|
693
|
+
this.getPlayers = async ({ instanceAddress, gameId }) => {
|
|
694
|
+
try {
|
|
695
|
+
return this.publicClient.readContract({
|
|
696
|
+
address: instanceAddress,
|
|
697
|
+
abi: abis_1.RankifyDiamondInstanceAbi,
|
|
698
|
+
functionName: "getPlayers",
|
|
699
|
+
args: [gameId],
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
catch (e) {
|
|
703
|
+
throw await (0, utils_1.handleRPCError)(e);
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
/**
|
|
707
|
+
* Ends the current turn and processes votes
|
|
708
|
+
* @param gameId - ID of the game
|
|
709
|
+
* @returns Transaction hash
|
|
710
|
+
*/
|
|
711
|
+
this.endTurn = async ({ instanceAddress, gameId }) => {
|
|
712
|
+
(0, log_1.logger)(`Ending turn for game ${gameId}`, 2);
|
|
713
|
+
try {
|
|
714
|
+
if (!(await this.canEndTurn({ instanceAddress, gameId }))) {
|
|
715
|
+
throw new Error("Cannot end turn");
|
|
716
|
+
}
|
|
717
|
+
const turn = await this.currentTurn({ instanceAddress, gameId });
|
|
718
|
+
const players = await this.getPlayers({ instanceAddress, gameId });
|
|
719
|
+
(0, log_1.logger)(`Current turn: ${turn}, Players count: ${players.length}`, 2);
|
|
720
|
+
const newPaddedDecryptedProposals = await this.decryptProposals({ instanceAddress, gameId, turn, players: [...players], padToMaxSize: true });
|
|
721
|
+
(0, log_1.logger)(`newPaddedDecryptedProposals:`);
|
|
722
|
+
(0, log_1.logger)(newPaddedDecryptedProposals);
|
|
723
|
+
const votesDecrypted = await this.decryptTurnVotes({ instanceAddress, gameId, turn, players: [...players] });
|
|
724
|
+
(0, log_1.logger)(`votesDecrypted:`);
|
|
725
|
+
(0, log_1.logger)(votesDecrypted);
|
|
726
|
+
const tableData = players.map((player, idx) => ({
|
|
727
|
+
player,
|
|
728
|
+
voted: this.hasVoted({ vote: votesDecrypted[idx] }),
|
|
729
|
+
proposal: newPaddedDecryptedProposals[idx]?.proposal.substring(0, 50) || "not-proposed",
|
|
730
|
+
}));
|
|
731
|
+
console.table(tableData);
|
|
732
|
+
const attested = await this.getProposalsIntegrity({
|
|
733
|
+
gameId,
|
|
734
|
+
turn,
|
|
735
|
+
verifierAddress: instanceAddress,
|
|
736
|
+
size: players.length,
|
|
737
|
+
proposals: newPaddedDecryptedProposals,
|
|
738
|
+
});
|
|
739
|
+
const { request } = await this.publicClient.simulateContract({
|
|
740
|
+
abi: abis_1.RankifyDiamondInstanceAbi,
|
|
741
|
+
account: this.walletClient.account,
|
|
742
|
+
address: instanceAddress,
|
|
743
|
+
functionName: "endTurn",
|
|
744
|
+
args: [gameId, votesDecrypted, attested.newProposals, attested.prevTurnPermutation, attested.prevTurnSalt],
|
|
745
|
+
});
|
|
746
|
+
return this.walletClient.writeContract(request);
|
|
747
|
+
}
|
|
748
|
+
catch (e) {
|
|
749
|
+
throw await (0, utils_1.handleRPCError)(e);
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
this.gameKey = async ({ gameId, contractAddress }) => {
|
|
753
|
+
(0, log_1.logger)(`Signing game key for game ${gameId} at address ${contractAddress}`);
|
|
754
|
+
const message = (0, viem_1.encodePacked)(["uint256", "address", "string"], [gameId, contractAddress, "gameKey"]);
|
|
755
|
+
(0, log_1.logger)(`Signing message: ${message}`, 2);
|
|
756
|
+
if (!this.walletClient.account)
|
|
757
|
+
throw new Error("No account");
|
|
758
|
+
const gameKey = await this.walletClient
|
|
759
|
+
.signMessage({
|
|
760
|
+
message,
|
|
761
|
+
account: this.walletClient.account,
|
|
762
|
+
})
|
|
763
|
+
.then((sig) => (0, viem_1.keccak256)(sig));
|
|
764
|
+
(0, log_1.logger)(`Game key: ${gameKey}`, 2);
|
|
765
|
+
return gameKey;
|
|
766
|
+
};
|
|
767
|
+
this.calculateSharedTurnKey = async ({ instanceAddress, gameId, turn, player, }) => {
|
|
768
|
+
(0, log_1.logger)(`Calculating shared turn key for player ${player} in game ${gameId} at address ${instanceAddress}`);
|
|
769
|
+
const instance = new InstanceBase_1.default({ instanceAddress, publicClient: this.publicClient, chainId: this.chainId });
|
|
770
|
+
const playerPubKey = await instance.getPlayerPubKey({ instanceAddress, gameId, player });
|
|
771
|
+
(0, log_1.logger)(`Player public key: ${playerPubKey}`, 2);
|
|
772
|
+
console.log("Player public key:", playerPubKey);
|
|
773
|
+
console.log("Game key:", await this.gameKey({ gameId, contractAddress: instanceAddress }));
|
|
774
|
+
return instance.sharedSigner({
|
|
775
|
+
publicKey: playerPubKey,
|
|
776
|
+
privateKey: await this.gameKey({ gameId, contractAddress: instanceAddress }),
|
|
777
|
+
gameId,
|
|
778
|
+
turn,
|
|
779
|
+
contractAddress: instanceAddress,
|
|
780
|
+
chainId: this.chainId,
|
|
781
|
+
});
|
|
782
|
+
};
|
|
783
|
+
this.decryptVote = async (vote, privateKey) => {
|
|
784
|
+
const decrypted = aes_1.default.decrypt(vote, privateKey).toString(crypto_js_1.default.enc.Utf8);
|
|
785
|
+
if (!decrypted) {
|
|
786
|
+
throw new Error("Failed to decrypt vote");
|
|
787
|
+
}
|
|
788
|
+
try {
|
|
789
|
+
const parsed = JSON.parse(decrypted);
|
|
790
|
+
(0, log_1.logger)(`Decrypted vote:`, 2);
|
|
791
|
+
(0, log_1.logger)(parsed, 2);
|
|
792
|
+
return parsed.map((v) => BigInt(v));
|
|
793
|
+
// eslint-disable-next-line
|
|
794
|
+
}
|
|
795
|
+
catch (e) {
|
|
796
|
+
throw new Error("Unexpected token");
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
/**
|
|
800
|
+
* Creates and signs a vote for testing purposes
|
|
801
|
+
* @param params - Parameters including voter, game info, and vote configuration
|
|
802
|
+
* @returns A complete mock vote with signatures
|
|
803
|
+
*/
|
|
804
|
+
this.attestVote = async ({ voter, gameId, turn, vote, verifierAddress, }) => {
|
|
805
|
+
(0, log_1.logger)(`Attesting vote for player ${voter} in game ${gameId}, turn ${turn}`);
|
|
806
|
+
const players = await this.getPlayers({ instanceAddress: verifierAddress, gameId });
|
|
807
|
+
const validationResult = await this.validateVote({ gameId, turn, voter, vote, instanceAddress: verifierAddress, players: [...players] });
|
|
808
|
+
if (!validationResult.result) {
|
|
809
|
+
throw new Error('Vote validation failed: ' + validationResult.reason);
|
|
810
|
+
}
|
|
811
|
+
const gameSize = players.length;
|
|
812
|
+
const instance = new InstanceBase_1.default({
|
|
813
|
+
instanceAddress: verifierAddress,
|
|
814
|
+
publicClient: this.publicClient,
|
|
815
|
+
chainId: this.chainId,
|
|
816
|
+
});
|
|
817
|
+
const eip712 = await instance.getEIP712Domain();
|
|
818
|
+
const playerSalt = await this.getTurnPlayersSalt({
|
|
819
|
+
gameId,
|
|
820
|
+
turn,
|
|
821
|
+
player: voter,
|
|
822
|
+
verifierAddress,
|
|
823
|
+
size: gameSize,
|
|
824
|
+
});
|
|
825
|
+
const ballot = {
|
|
826
|
+
vote: vote,
|
|
827
|
+
salt: playerSalt,
|
|
828
|
+
};
|
|
829
|
+
const ballotHash = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["uint256[]", "bytes32"], [vote, playerSalt]));
|
|
830
|
+
const turnKey = await this.calculateSharedTurnKey({
|
|
831
|
+
instanceAddress: verifierAddress,
|
|
832
|
+
gameId,
|
|
833
|
+
turn,
|
|
834
|
+
player: voter,
|
|
835
|
+
});
|
|
836
|
+
const ballotId = aes_1.default.encrypt(JSON.stringify(ballot.vote.map((v) => v.toString())), turnKey).toString();
|
|
837
|
+
const gmSignature = await this.signVote({
|
|
838
|
+
verifierAddress,
|
|
839
|
+
voter,
|
|
840
|
+
gameId,
|
|
841
|
+
sealedBallotId: ballotId,
|
|
842
|
+
ballotHash,
|
|
843
|
+
name: eip712.name,
|
|
844
|
+
version: eip712.version,
|
|
845
|
+
});
|
|
846
|
+
(0, log_1.logger)(`Vote attested for player ${voter} by ${this.walletClient.account?.address}`);
|
|
847
|
+
(0, log_1.logger)({
|
|
848
|
+
gameId,
|
|
849
|
+
turn,
|
|
850
|
+
vote,
|
|
851
|
+
verifierAddress,
|
|
852
|
+
gameSize,
|
|
853
|
+
name: eip712.name,
|
|
854
|
+
version: eip712.version,
|
|
855
|
+
chainId: this.chainId,
|
|
856
|
+
}, 2);
|
|
857
|
+
return { vote, ballotHash, ballot, ballotId, gmSignature };
|
|
858
|
+
};
|
|
859
|
+
/**
|
|
860
|
+
* Signs a vote
|
|
861
|
+
* @param params - Parameters including voter, game info, and vote configuration
|
|
862
|
+
* @returns The signed vote
|
|
863
|
+
*/
|
|
864
|
+
this.signVote = async (params) => {
|
|
865
|
+
const { voter, gameId, verifierAddress, sealedBallotId, ballotHash, name, version } = params;
|
|
866
|
+
(0, log_1.logger)(`Signing vote for player ${voter} in game ${gameId}`);
|
|
867
|
+
const types = {
|
|
868
|
+
SubmitVote: [
|
|
869
|
+
{ name: "gameId", type: "uint256" },
|
|
870
|
+
{ name: "voter", type: "address" },
|
|
871
|
+
{ name: "sealedBallotId", type: "string" },
|
|
872
|
+
{ name: "ballotHash", type: "bytes32" },
|
|
873
|
+
],
|
|
874
|
+
};
|
|
875
|
+
if (!this.walletClient.account)
|
|
876
|
+
throw new Error("No account");
|
|
877
|
+
const signature = await this.walletClient.signTypedData({
|
|
878
|
+
domain: {
|
|
879
|
+
name,
|
|
880
|
+
version,
|
|
881
|
+
chainId: this.chainId,
|
|
882
|
+
verifyingContract: verifierAddress,
|
|
883
|
+
},
|
|
884
|
+
types,
|
|
885
|
+
primaryType: "SubmitVote",
|
|
886
|
+
message: {
|
|
887
|
+
gameId,
|
|
888
|
+
voter,
|
|
889
|
+
sealedBallotId,
|
|
890
|
+
ballotHash,
|
|
891
|
+
},
|
|
892
|
+
account: this.walletClient.account,
|
|
893
|
+
});
|
|
894
|
+
(0, log_1.logger)(`Vote signed for player ${voter}`);
|
|
895
|
+
return signature;
|
|
896
|
+
};
|
|
897
|
+
/**
|
|
898
|
+
* Generates integrity data for the end of a game turn
|
|
899
|
+
* @param params - Parameters including game info, turn, and proposal data
|
|
900
|
+
* @returns Integrity data including permutation, secret, and proof
|
|
901
|
+
*/
|
|
902
|
+
this.generateEndTurnIntegrity = async ({ gameId, turn, verifierAddress, size = 15, proposals, }) => {
|
|
903
|
+
let _proposals = [...proposals];
|
|
904
|
+
const { permutation: prevTurnPermutation, turnSalt: prevTurnSalt } = await this.generateDeterministicPermutation({
|
|
905
|
+
gameId,
|
|
906
|
+
turn: turn - 1n,
|
|
907
|
+
verifierAddress,
|
|
908
|
+
size,
|
|
909
|
+
});
|
|
910
|
+
const values = await Promise.all(_proposals.map((p) => p.proposal === ""
|
|
911
|
+
? {
|
|
912
|
+
proposalValue: 0n,
|
|
913
|
+
randomnessValue: 0n,
|
|
914
|
+
proposer: p.proposer,
|
|
915
|
+
}
|
|
916
|
+
: this.proposalValues({
|
|
917
|
+
instanceAddress: verifierAddress,
|
|
918
|
+
gameId,
|
|
919
|
+
proposal: p.proposal,
|
|
920
|
+
turn,
|
|
921
|
+
proposer: p.proposer,
|
|
922
|
+
})));
|
|
923
|
+
(0, log_1.logger)("proposals with added empty proposals:", 3);
|
|
924
|
+
(0, log_1.logger)(_proposals, 3);
|
|
925
|
+
const inputs = await this.createInputs({
|
|
926
|
+
numActive: size,
|
|
927
|
+
proposals: values.map((v) => v.proposalValue),
|
|
928
|
+
commitmentRandomnesses: values.map((v) => v.randomnessValue),
|
|
929
|
+
gameId,
|
|
930
|
+
turn,
|
|
931
|
+
verifierAddress,
|
|
932
|
+
});
|
|
933
|
+
(0, log_1.logger)("inputs:", 3);
|
|
934
|
+
(0, log_1.logger)(inputs, 3);
|
|
935
|
+
// Apply permutation to proposals array
|
|
936
|
+
console.log("permutation used on new proposals:", inputs.permutation);
|
|
937
|
+
console.log("revealed permutation for prevTurn proposals:", prevTurnPermutation);
|
|
938
|
+
const permutedProposals = (0, permutations_1.permuteArray)({ array: _proposals, permutation: inputs.permutation });
|
|
939
|
+
console.log("permutedProposals:", permutedProposals);
|
|
940
|
+
const config = {
|
|
941
|
+
circuitName: "ProposalsIntegrity15",
|
|
942
|
+
circuitArtifactsPath: path_1.default.join(__dirname, "../../zk_artifacts/circuits/proposals_integrity_15.circom/"),
|
|
943
|
+
verifierDirPath: path_1.default.join(__dirname, "../../zk_artifacts/verifiers"),
|
|
944
|
+
};
|
|
945
|
+
const implementer = new zkit_1.Groth16Implementer();
|
|
946
|
+
const circuit = new zkit_1.CircuitZKit(config, implementer);
|
|
947
|
+
const proof = await circuit.generateProof(inputs);
|
|
948
|
+
(0, log_1.logger)("proof:", 3);
|
|
949
|
+
(0, log_1.logger)(proof, 3);
|
|
950
|
+
const callData = await circuit.generateCalldata(proof);
|
|
951
|
+
(0, log_1.logger)("callData:", 3);
|
|
952
|
+
(0, log_1.logger)(callData, 3);
|
|
953
|
+
if (!proof) {
|
|
954
|
+
throw new Error("Proof not found");
|
|
955
|
+
}
|
|
956
|
+
// const callData = await circuit.generateCalldata(proof);
|
|
957
|
+
const a = callData[0].map((a) => BigInt(a));
|
|
958
|
+
const b = callData[1].map((b) => b.map((b) => BigInt(b)));
|
|
959
|
+
const c = callData[2].map((c) => BigInt(c));
|
|
960
|
+
return {
|
|
961
|
+
commitment: inputs.permutationCommitment,
|
|
962
|
+
prevTurnSalt,
|
|
963
|
+
prevTurnPermutation,
|
|
964
|
+
permutedProposals: permutedProposals.map((proposal) => proposal.proposal),
|
|
965
|
+
a,
|
|
966
|
+
b,
|
|
967
|
+
c,
|
|
968
|
+
};
|
|
969
|
+
};
|
|
970
|
+
/**
|
|
971
|
+
* Creates inputs for the proposal integrity circuit
|
|
972
|
+
* @param params - Parameters including number of active proposals, proposals, commitment random numbers, game ID, turn, and verifier address
|
|
973
|
+
* @returns The inputs for the proposal integrity circuit
|
|
974
|
+
*/
|
|
975
|
+
this.createInputs = async ({ numActive, proposals, commitmentRandomnesses, gameId, turn, verifierAddress, }) => {
|
|
976
|
+
const poseidon = await (0, circomlibjs_1.buildPoseidon)();
|
|
977
|
+
// Initialize arrays with zeros
|
|
978
|
+
const commitments = Array(this.maxSlotSizeForProofs).fill(0n);
|
|
979
|
+
const randomnesses = Array(this.maxSlotSizeForProofs).fill(0n);
|
|
980
|
+
const permutedProposals = Array(this.maxSlotSizeForProofs).fill(0n);
|
|
981
|
+
// Generate deterministic permutation
|
|
982
|
+
const { permutation, turnSalt: secret, commitment } = await this.generateDeterministicPermutation({
|
|
983
|
+
gameId,
|
|
984
|
+
turn,
|
|
985
|
+
verifierAddress,
|
|
986
|
+
size: numActive,
|
|
987
|
+
});
|
|
988
|
+
// Fill arrays with values
|
|
989
|
+
for (let i = 0; i < this.maxSlotSizeForProofs; i++) {
|
|
990
|
+
if (i < numActive) {
|
|
991
|
+
// Active slots
|
|
992
|
+
const proposal = proposals[i];
|
|
993
|
+
const randomness = commitmentRandomnesses[i];
|
|
994
|
+
const hash = poseidon([proposal, randomness]);
|
|
995
|
+
commitments[i] = BigInt(poseidon.F.toObject(hash));
|
|
996
|
+
randomnesses[i] = randomness;
|
|
997
|
+
// Store proposal in permuted position
|
|
998
|
+
permutedProposals[permutation[i]] = proposal;
|
|
999
|
+
}
|
|
1000
|
+
else {
|
|
1001
|
+
(0, log_1.logger)(`Inactive slot ${i}`, 3);
|
|
1002
|
+
// Inactive slots
|
|
1003
|
+
const hash = poseidon([0n, 0n]);
|
|
1004
|
+
commitments[i] = BigInt(poseidon.F.toObject(hash));
|
|
1005
|
+
randomnesses[i] = 0n;
|
|
1006
|
+
// permutedProposals already 0n
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return {
|
|
1010
|
+
numActive: BigInt(numActive),
|
|
1011
|
+
commitments,
|
|
1012
|
+
permutedProposals,
|
|
1013
|
+
permutationCommitment: commitment,
|
|
1014
|
+
permutation: permutation.map((p) => BigInt(p)),
|
|
1015
|
+
randomnesses,
|
|
1016
|
+
permutationRandomness: secret,
|
|
1017
|
+
};
|
|
1018
|
+
};
|
|
1019
|
+
this.chainId = chainId;
|
|
1020
|
+
this.publicClient = publicClient;
|
|
1021
|
+
this.walletClient = walletClient;
|
|
1022
|
+
}
|
|
1023
|
+
hasVoted({ vote }) {
|
|
1024
|
+
return vote?.reduce((a, b) => a + b, 0n) !== 0n;
|
|
1025
|
+
}
|
|
1026
|
+
;
|
|
1027
|
+
getGameState({ gameId, instanceAddress }) {
|
|
1028
|
+
const baseInstance = new InstanceBase_1.default({
|
|
1029
|
+
instanceAddress,
|
|
1030
|
+
publicClient: this.publicClient,
|
|
1031
|
+
chainId: this.chainId,
|
|
1032
|
+
});
|
|
1033
|
+
return baseInstance.getGameStateDetails(gameId);
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Gets proposal integrity data for testing
|
|
1037
|
+
* @param params - Parameters including game info and proposal data
|
|
1038
|
+
* @returns Proposal integrity information including permutations and proofs
|
|
1039
|
+
*/
|
|
1040
|
+
async getProposalsIntegrity({ size, gameId, turn, proposals, verifierAddress, }) {
|
|
1041
|
+
(0, log_1.logger)(`Generating proposals integrity for game ${gameId}, turn ${turn} with ${size} players.`);
|
|
1042
|
+
const { commitment, prevTurnSalt, prevTurnPermutation, permutedProposals, a, b, c } = await this.generateEndTurnIntegrity({
|
|
1043
|
+
gameId,
|
|
1044
|
+
turn,
|
|
1045
|
+
verifierAddress,
|
|
1046
|
+
size,
|
|
1047
|
+
proposals,
|
|
1048
|
+
});
|
|
1049
|
+
(0, log_1.logger)(`Generated proposals integrity with commitment ${commitment}`);
|
|
1050
|
+
return {
|
|
1051
|
+
newProposals: {
|
|
1052
|
+
a,
|
|
1053
|
+
b,
|
|
1054
|
+
c,
|
|
1055
|
+
proposals: permutedProposals,
|
|
1056
|
+
permutationCommitment: commitment,
|
|
1057
|
+
},
|
|
1058
|
+
prevTurnPermutation: prevTurnPermutation.map((p) => BigInt(p)),
|
|
1059
|
+
proposalsNotPermuted: proposals.map((proposal) => proposal.proposal),
|
|
1060
|
+
prevTurnSalt,
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
exports.GameMaster = GameMaster;
|
|
1065
|
+
exports.default = GameMaster;
|
|
1066
|
+
//# sourceMappingURL=GameMaster.js.map
|