Skip to content

Commit 5b75130

Browse files
authored
Merge pull request #179 from keep-network/sortition-tree-test
Sortition Tree Tests This PR overhauls the sortition tree tests! Summary of changes: - Simplified the test layout (removed unnecessary beforeEach constructions) - Simplified the position constructions to make it more human-readable - Heavily annotated the tests - Added some additional specs where the assertions seemed sparse
2 parents 73b4667 + 4221406 commit 5b75130

1 file changed

Lines changed: 94 additions & 70 deletions

File tree

test/sortitionTreeTest.js

Lines changed: 94 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -19,86 +19,122 @@ describe("SortitionTree", () => {
1919

2020
describe("setLeaf", async () => {
2121
context("when one leaf is set", () => {
22-
beforeEach(async () => {
23-
const weight1 = 0x1234
24-
const position1 = parseInt("00123456", 8)
25-
26-
const leaf = await sortition.toLeaf(alice.address, weight1)
27-
await sortition.publicSetLeaf(position1, leaf, weight1)
28-
})
29-
3022
it("should return correct value for the tree", async () => {
23+
const weight = 0x1234
24+
const position = 42798
25+
26+
const leaf = await sortition.toLeaf(alice.address, weight)
27+
await sortition.publicSetLeaf(position, leaf, weight)
3128
const root = await sortition.getRoot()
29+
//
30+
// Since the only leaf in the tree is the one we set, that's the only
31+
// weight that propagates to the root node. The first slot in the root
32+
// covers the sum of the first 8^6 = 262144 leaves. The next slot in
33+
// the root covers the sum of the next 262144, and so on.
3234
expect(ethers.utils.hexlify(root)).to.be.equal("0x1234")
35+
// The full output here looks like
36+
// 0x00000000,00000000,00000000,00000000,00000000,00000000,00000000,00001234
37+
// slot 7 slot 6 slot 5 slot 4 slot 3 slot 2 slot 1 slot 0
38+
// without the commas added for readability. All the padding zeros are
39+
// dropped when we hexlify.
3340
})
3441
})
3542

43+
it("should return correct value for the tree with a leaf in a second slot", async () => {
44+
const weight = 0x1234
45+
const position = 262145
46+
47+
const leaf = await sortition.toLeaf(alice.address, weight)
48+
await sortition.publicSetLeaf(position, leaf, weight)
49+
const root = await sortition.getRoot()
50+
//
51+
// Since the only leaf in the tree is the one we set, that's the only
52+
// weight that propagates to the root node. The first slot in the root
53+
// covers the sum of the first 8^6 = 262144 leaves. The next slot in
54+
// the root covers the sum of the next 262144, and so on.
55+
expect(ethers.utils.hexlify(root)).to.be.equal("0x123400000000")
56+
// The full output here looks like
57+
// 0x00000000,00000000,00000000,00000000,00000000,00000000,00001234,00000000
58+
// slot 7 slot 6 slot 5 slot 4 slot 3 slot 2 slot 1 slot 0
59+
// without the commas added for readability. All the padding zeros are
60+
// dropped when we hexlify, which simplifies to 0x123400000000.
61+
})
62+
3663
context("when two leaves are set", () => {
37-
beforeEach(async () => {
64+
it("should return correct value for the tree", async () => {
3865
const weight1 = 0x1234
39-
const position1 = parseInt("00123456", 8)
66+
const position1 = 42798
67+
4068
const weight2 = 0x11
41-
const position2 = parseInt("01234567", 8)
69+
const position2 = 342391
4270

4371
const leaf1 = await sortition.toLeaf(alice.address, weight1)
4472
await sortition.publicSetLeaf(position1, leaf1, weight1)
4573

4674
const leaf2 = await sortition.toLeaf(bob.address, weight2)
4775
await sortition.publicSetLeaf(position2, leaf2, weight2)
48-
})
49-
50-
it("should return correct value for the tree", async () => {
5176
const root = await sortition.getRoot()
5277
expect(ethers.utils.hexlify(root)).to.be.equal("0x1100001234")
78+
// The full output here looks like
79+
// 0x00000000,00000000,00000000,00000000,00000000,00000000,00000011,00001234
80+
// slot 7 slot 6 slot 5 slot 4 slot 3 slot 2 slot 1 slot 0
81+
// without the commas added for readability. All the padding zeros are
82+
// dropped when we hexlify, which simplifies to 0x1100001234.
5383
})
5484
})
5585
})
5686

5787
describe("removeLeaf", () => {
5888
context("when leaf is removed", () => {
59-
beforeEach(async () => {
89+
it("should return correct value for the tree", async () => {
6090
const weight1 = 0x1234
61-
const position1 = parseInt("00123456", 8)
91+
const position1 = 42798
92+
6293
const weight2 = 0x11
63-
const position2 = parseInt("01234567", 8)
94+
const position2 = 342391
6495

6596
const leaf1 = await sortition.toLeaf(alice.address, weight1)
6697
await sortition.publicSetLeaf(position1, leaf1, weight1)
6798

6899
const leaf2 = await sortition.toLeaf(bob.address, weight2)
69100
await sortition.publicSetLeaf(position2, leaf2, weight2)
70101
await sortition.publicRemoveLeaf(position1)
71-
})
72-
73-
it("should return correct value for the tree", async () => {
74102
const root = await sortition.getRoot()
75103
expect(ethers.utils.hexlify(root)).to.be.equal("0x1100000000")
104+
// The full output here looks like
105+
// 0x00000000,00000000,00000000,00000000,00000000,00000000,00000011,00000000
106+
// slot 7 slot 6 slot 5 slot 4 slot 3 slot 2 slot 1 slot 0
107+
// without the commas added for readability. All the padding zeros are
108+
// dropped when we hexlify, which simplifies to 0x1100000000.
76109
})
77110
})
78111
})
79112

80113
describe("insertOperator", () => {
81114
const weightA = 0xfff0
82115
const weightB = 0x10000001
116+
// weightA + weightB = 0x1000fff1
83117

84118
context("when operators are inserted", () => {
85-
beforeEach(async () => {
119+
it("should return correct value for the tree", async () => {
120+
// insertion begins left to right, so alice is inserted at position 0,
121+
// and bob is inserted at position 1. Their weights will propagate to
122+
// the root's first slot.
86123
await sortition.publicInsertOperator(alice.address, weightA)
87124
await sortition.publicInsertOperator(bob.address, weightB)
88-
})
89-
90-
it("should return correct value for the tree", async () => {
91125
const root = await sortition.getRoot()
92-
expect(ethers.utils.hexlify(root)).to.be.equal("0x1000fff1")
126+
expect(ethers.utils.hexlify(root)).to.be.equal("0x1000fff1") // weightA + weightB
127+
// The full output here looks like
128+
// 0x00000000,00000000,00000000,00000000,00000000,00000000,00000000,1000fff1
129+
// slot 7 slot 6 slot 5 slot 4 slot 3 slot 2 slot 1 slot 0
130+
// without the commas added for readability. All the padding zeros are
131+
// dropped when we hexlify, which simplifies to 0x1100000000.
93132
})
94133
})
95134

96135
context("when operator is already registered", () => {
97-
beforeEach(async () => {
98-
await sortition.publicInsertOperator(alice.address, weightA)
99-
})
100-
101136
it("should revert", async () => {
137+
await sortition.publicInsertOperator(alice.address, weightA)
102138
await expect(
103139
sortition.publicInsertOperator(alice.address, weightB),
104140
).to.be.revertedWith("Operator is already registered in the pool")
@@ -108,47 +144,38 @@ describe("SortitionTree", () => {
108144

109145
describe("getOperatorID", () => {
110146
context("when operator is inserted", () => {
111-
beforeEach(async () => {
112-
await sortition.publicInsertOperator(alice.address, 0xfff0)
113-
})
114-
115147
it("should return the id of the operator", async () => {
148+
await sortition.publicInsertOperator(alice.address, 0xfff0)
116149
const aliceID = await sortition.getOperatorID(alice.address)
117150
expect(aliceID).to.be.equal(1)
118151
})
152+
})
119153

120-
it("should return zero id when the operator is unknown", async () => {
121-
const bobID = await sortition.getOperatorID(bob.address)
122-
expect(bobID).to.be.equal(0)
123-
})
154+
it("should return zero id when the operator is unknown", async () => {
155+
const bobID = await sortition.getOperatorID(bob.address)
156+
expect(bobID).to.be.equal(0)
124157
})
125158
})
126159

127160
describe("getIDOperator", () => {
128161
context("when operator is inserted", () => {
129-
beforeEach(async () => {
130-
await sortition.publicInsertOperator(alice.address, 0xfff0)
131-
})
132-
133162
it("should return the address of the operator by their id", async () => {
163+
await sortition.publicInsertOperator(alice.address, 0xfff0)
134164
const aliceAddress = await sortition.getIDOperator(1)
135165
expect(aliceAddress).to.be.equal(alice.address)
136166
})
167+
})
137168

138-
it("should return zero address when the id of operator is unknown", async () => {
139-
const aliceAddress = await sortition.getIDOperator(2)
140-
expect(aliceAddress).to.be.equal(ZERO_ADDRESS)
141-
})
169+
it("should return zero address when the id of operator is unknown", async () => {
170+
const aliceAddress = await sortition.getIDOperator(2)
171+
expect(aliceAddress).to.be.equal(ZERO_ADDRESS)
142172
})
143173
})
144174

145175
describe("removeOperator", () => {
146176
context("when operator is not registered", () => {
147-
beforeEach(async () => {
148-
await sortition.publicInsertOperator(alice.address, 0x1234)
149-
})
150-
151177
it("should revert", async () => {
178+
await sortition.publicInsertOperator(alice.address, 0x1234)
152179
await expect(
153180
sortition.publicRemoveOperator(bob.address),
154181
).to.be.revertedWith("Operator is not registered in the pool")
@@ -182,22 +209,16 @@ describe("SortitionTree", () => {
182209

183210
describe("isOperatorRegistered", async () => {
184211
context("when operator is not registered", () => {
185-
beforeEach(async () => {
186-
await sortition.publicInsertOperator(alice.address, 0x1234)
187-
})
188-
189212
it("should return false", async () => {
213+
await sortition.publicInsertOperator(alice.address, 0x1234)
190214
expect(await sortition.publicIsOperatorRegistered(bob.address)).to.be
191215
.false
192216
})
193217
})
194218

195219
context("when operator is registered", () => {
196-
beforeEach(async () => {
197-
await sortition.publicInsertOperator(alice.address, 0x1234)
198-
})
199-
200220
it("should return false", async () => {
221+
await sortition.publicInsertOperator(alice.address, 0x1234)
201222
expect(await sortition.publicIsOperatorRegistered(alice.address)).to.be
202223
.true
203224
})
@@ -206,12 +227,9 @@ describe("SortitionTree", () => {
206227

207228
describe("updateLeaf", async () => {
208229
context("when leaf is updated", () => {
209-
beforeEach(async () => {
230+
it("should return the correct value for the root", async () => {
210231
await sortition.publicInsertOperator(alice.address, 0x1234)
211232
await sortition.publicUpdateLeaf(0x00000, 0x9876)
212-
})
213-
214-
it("should return the correct value for the root", async () => {
215233
const root = await sortition.getRoot()
216234
expect(ethers.utils.hexlify(root)).to.be.equal("0x9876")
217235
})
@@ -220,19 +238,24 @@ describe("SortitionTree", () => {
220238

221239
describe("trunk stacks", async () => {
222240
it("works as expected", async () => {
241+
// inserted in the first position
223242
await sortition.publicInsertOperator(alice.address, 0x1234)
243+
// inserted in the second position
224244
await sortition.publicInsertOperator(bob.address, 0x9876)
225245

226246
await sortition.publicRemoveOperator(alice.address)
227247
const deletedLeaf = await sortition.getLeaf(0x00000)
228248
expect(deletedLeaf).to.be.equal(0)
229249

250+
// the first position isn't reused until we've inserted 8^7 = 2097152
251+
// operators. Alice is inserted in the third position.
230252
await sortition.publicInsertOperator(alice.address, 0xdead)
231253

232254
const stillDeletedLeaf = await sortition.getLeaf(0x00000)
233255
expect(stillDeletedLeaf).to.be.equal(0)
234256

235257
const root = await sortition.getRoot()
258+
// 0x9876 + 0xdead = 0x17723
236259
expect(ethers.utils.hexlify(root)).to.be.equal("0x017723")
237260
})
238261
})
@@ -244,6 +267,8 @@ describe("SortitionTree", () => {
244267
const index1 = 450
245268
const index2 = 451
246269

270+
// alice is assigned weights [0-450] (451 values) and bob has weight
271+
// [451-2434] (1984 values).
247272
const position1 = await sortition.publicPickWeightedLeaf(index1)
248273
expect(position1).to.be.equal(0)
249274

@@ -261,15 +286,14 @@ describe("SortitionTree", () => {
261286
})
262287

263288
describe("operatorsInPool", async () => {
264-
context("when the operator is in the pool", () => {
265-
beforeEach(async () => {
266-
await sortition.publicInsertOperator(alice.address, 1)
267-
})
268-
269-
it("should return true", async () => {
270-
const nOperators = await sortition.operatorsInPool()
271-
expect(nOperators).to.be.equal(1)
272-
})
289+
it("counts the number of operators in the pool", async () => {
290+
await sortition.publicInsertOperator(alice.address, 1)
291+
const justAlice = await sortition.operatorsInPool()
292+
expect(justAlice).to.be.equal(1)
293+
294+
await sortition.publicInsertOperator(bob.address, 1)
295+
const aliceAndBob = await sortition.operatorsInPool()
296+
expect(aliceAndBob).to.be.equal(2)
273297
})
274298
})
275299

0 commit comments

Comments
 (0)