mirror of
https://github.com/paritytech/banana_split.git
synced 2026-09-14 11:06:18 +05:00
fix: stop rendering a threshold derived from an invalid shard count
Gating the generate button left the sentence above it still interpolating defaultThreshold(totalShards) for values that had just been rejected: an emptied field coerces to 0 and renders "Will require any 1 shards", and 3.5 renders 2. Give Share.vue's `requiredShards` the same contract as Print.vue's `threshold` — `undefined` unless the count is usable — show an em dash in the sentence, and guard both the generated-shards block and crypto.share() on it. Also strengthen the threshold invariant test. Asserting only `defaultThreshold(n) > n / 2` does not pin the policy: floor(n/2)+2, the very formula this branch removed, satisfies it for every even n. Assert the smallest strict majority instead, which floor(n/2)+1 alone satisfies.
This commit is contained in:
parent
9f74cc3f6c
commit
fca0f7fe89
@ -31,7 +31,7 @@
|
||||
<p>
|
||||
<label>3. Shards</label>
|
||||
<br />
|
||||
Will require any {{ requiredShards }} shards out of
|
||||
Will require any {{ requiredShardsLabel }} shards out of
|
||||
<input
|
||||
id="totalShards"
|
||||
v-model.number="totalShards"
|
||||
@ -67,7 +67,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="encryptionMode">
|
||||
<div v-if="encryptionMode && requiredShards !== undefined">
|
||||
<div class="card" framed="true" transparent="true">
|
||||
<label>4. Your passphrase for the recovery is:</label>
|
||||
<div class="flex justify-between align-center">
|
||||
@ -131,12 +131,18 @@ export default Vue.extend({
|
||||
shardCountValid(): boolean {
|
||||
return isValidShardCount(this.totalShards);
|
||||
},
|
||||
requiredShards(): number {
|
||||
return defaultThreshold(this.totalShards);
|
||||
// Same contract as Print.vue's `threshold`: `undefined` while the count is
|
||||
// not usable, so a coerced value ("" divides to 0, giving a bogus 1) can
|
||||
// neither be printed nor reach ShardInfo's required Number prop.
|
||||
requiredShards(): number | undefined {
|
||||
return isValidShardCount(this.totalShards) ? defaultThreshold(this.totalShards) : undefined;
|
||||
},
|
||||
requiredShardsLabel(): string {
|
||||
return this.requiredShards === undefined ? "—" : String(this.requiredShards);
|
||||
},
|
||||
shards(): string[] {
|
||||
this.$eventHub.$emit("clearAlerts");
|
||||
if (!this.encryptionMode) {
|
||||
if (!this.encryptionMode || this.requiredShards === undefined) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
|
||||
@ -16,9 +16,14 @@ describe("defaultThreshold", () => {
|
||||
expect(defaultThreshold(total)).toBe(expected);
|
||||
});
|
||||
|
||||
test("always requires a strict majority across the whole UI range", () => {
|
||||
// A lower bound alone is too weak to pin the policy: floor(8/2)+2 = 6 is also
|
||||
// "more than half of 8". Assert the threshold is the *smallest* strict
|
||||
// majority, which is the property that makes floor(n/2)+1 the only answer.
|
||||
test("is the smallest strict majority across the whole UI range", () => {
|
||||
for (let n = 3; n <= 255; n++) {
|
||||
expect(defaultThreshold(n)).toBeGreaterThan(n / 2);
|
||||
const threshold = defaultThreshold(n);
|
||||
expect(threshold).toBeGreaterThan(n / 2);
|
||||
expect(threshold - 1).toBeLessThanOrEqual(n / 2);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user