decred.org/dcrdex@v1.0.5/client/orderbook/epochqueue_test.go (about) 1 package orderbook 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "math/rand" 7 "os" 8 "sort" 9 "testing" 10 11 "decred.org/dcrdex/dex" 12 "decred.org/dcrdex/dex/msgjson" 13 "decred.org/dcrdex/dex/order" 14 "github.com/decred/dcrd/crypto/blake256" 15 ) 16 17 var tLogger = dex.NewLogger("TBOOK", dex.LevelTrace, os.Stdout) 18 19 func makeEpochOrderNote(mid string, oid order.OrderID, side uint8, rate uint64, qty uint64, commitment order.Commitment, epoch uint64) *msgjson.EpochOrderNote { 20 return &msgjson.EpochOrderNote{ 21 Commit: commitment[:], 22 BookOrderNote: msgjson.BookOrderNote{ 23 OrderNote: msgjson.OrderNote{ 24 MarketID: mid, 25 OrderID: oid[:], 26 }, 27 TradeNote: msgjson.TradeNote{ 28 Side: side, 29 Rate: rate, 30 Quantity: qty, 31 }, 32 }, 33 Epoch: epoch, 34 } 35 } 36 37 func makeCommitment(pimg order.Preimage) order.Commitment { 38 return order.Commitment(blake256.Sum256(pimg[:])) 39 } 40 41 // makeMatchProof generates the sorting seed and commitment checksum from the 42 // provided ordered set of preimages. The provide commitments are sorted. 43 func makeMatchProof(preimages []order.Preimage, commitments []order.Commitment) (msgjson.Bytes, msgjson.Bytes) { 44 sort.Slice(commitments, func(i, j int) bool { 45 return bytes.Compare(commitments[i][:], commitments[j][:]) < 0 46 }) 47 48 cbuff := make([]byte, 0, len(commitments)*order.CommitmentSize) 49 for i := range commitments { 50 cbuff = append(cbuff, commitments[i][:]...) 51 } 52 csum := blake256.Sum256(cbuff) 53 54 sbuff := make([]byte, 0, len(preimages)*order.PreimageSize) 55 for i := 0; i < len(preimages); i++ { 56 sbuff = append(sbuff, preimages[i][:]...) 57 } 58 seed := blake256.Sum256(sbuff) 59 60 return seed[:], csum[:] 61 } 62 63 func TestEpochQueue(t *testing.T) { 64 mid := "mkt" 65 epoch := uint64(10) 66 eq := NewEpochQueue() 67 n1PimgB, _ := hex.DecodeString("e1f796fa0fc16ba7bb90be2a33e87c3d60ab628471a420834383661801bb0bfd") 68 var n1Pimg order.Preimage 69 copy(n1Pimg[:], n1PimgB) 70 n1Commitment := n1Pimg.Commit() // aba75140b1f6edf26955a97e1b09d7b17abdc9c0b099fc73d9729501652fbf66 71 n1OrderID := [32]byte{'a'} 72 n1 := makeEpochOrderNote(mid, n1OrderID, msgjson.BuyOrderNum, 1, 2, n1Commitment, epoch) 73 74 n2PimgB, _ := hex.DecodeString("8e6c140071db1eb2f7a18194f1a045a94c078835c75dff2f3e836180baad9e95") 75 var n2Pimg order.Preimage 76 copy(n2Pimg[:], n2PimgB) 77 n2Commitment := n2Pimg.Commit() // 0f4bc030d392cef3f44d0781870ab7fcb78a0cda36c73e50b88c741b4f851600 78 n2OrderID := [32]byte{'b'} 79 n2 := makeEpochOrderNote(mid, n2OrderID, msgjson.BuyOrderNum, 1, 2, n2Commitment, epoch) 80 81 n3PimgB, _ := hex.DecodeString("e1f796fa0fc16ba7bb90be2a33e87c3d60ab628471a420834383661801bb0bfd") 82 var n3Pimg order.Preimage 83 copy(n3Pimg[:], n3PimgB) 84 n3Commitment := n3Pimg.Commit() // aba75140b1f6edf26955a97e1b09d7b17abdc9c0b099fc73d9729501652fbf66 85 n3OrderID := [32]byte{'c'} 86 n3 := makeEpochOrderNote(mid, n3OrderID, msgjson.BuyOrderNum, 1, 2, n3Commitment, epoch) 87 88 // This csum matches the server-side tests. 89 wantCSum, _ := hex.DecodeString("8c743c3225b89ffbb50b5d766d3e078cd8e2658fa8cb6e543c4101e1d59a8e8e") 90 91 err := eq.Enqueue(n1) 92 if err != nil { 93 t.Fatalf("[Enqueue]: unexpected error: %v", err) 94 } 95 96 // Ensure the epoch queue size is 1. 97 if eq.Size() != 1 { 98 t.Fatalf("[Size] expected queue size of %d, got %d", 1, eq.Size()) 99 } 100 101 // Reset the epoch queue. 102 eq = NewEpochQueue() 103 104 // Ensure the epoch queue does not enqueue duplicate orders. 105 err = eq.Enqueue(n1) 106 if err != nil { 107 t.Fatalf("[Enqueue]: unexpected error: %v", err) 108 } 109 110 if epoch != n1.Epoch { 111 t.Fatalf("[Epoch]: expected epoch value of %d, got %d", n1.Epoch, epoch) 112 } 113 114 err = eq.Enqueue(n1) 115 if err == nil { 116 t.Fatal("[Enqueue]: expected a duplicate enqueue error") 117 } 118 119 // Ensure the epoch queue size is 1. 120 if eq.Size() != 1 { 121 t.Fatalf("[Size] expected queue size of %d, got %d", 1, eq.Size()) 122 } 123 124 // Reset the epoch queue. 125 eq = NewEpochQueue() 126 127 // Ensure match proof generation fails if there epoch queue is empty. 128 preimages := []order.Preimage{n1Pimg, n2Pimg, n3Pimg} 129 _, _, err = eq.GenerateMatchProof(preimages, nil) 130 if err == nil { 131 t.Fatalf("[GenerateMatchProof] expected an empty epoch queue error") 132 } 133 134 err = eq.Enqueue(n1) 135 if err != nil { 136 t.Fatalf("[Enqueue]: unexpected error: %v", err) 137 } 138 139 err = eq.Enqueue(n2) 140 if err != nil { 141 t.Fatalf("[Enqueue]: unexpected error: %v", err) 142 } 143 144 err = eq.Enqueue(n3) 145 if err != nil { 146 t.Fatalf("[Enqueue]: unexpected error: %v", err) 147 } 148 149 // Ensure the queue has n2 epoch order. 150 if !eq.Exists(n2OrderID) { 151 t.Fatalf("[Exists] expected order with id %x in the epoch queue", n2OrderID) 152 } 153 154 // Ensure the epoch queue size is 3. 155 if eq.Size() != 3 { 156 t.Fatalf("[Size] expected queue size of %d, got %d", 3, eq.Size()) 157 } 158 159 // Ensure match proof generation works as expected. 160 preimages = []order.Preimage{n1Pimg, n2Pimg, n3Pimg} 161 commitments := []order.Commitment{n1Commitment, n3Commitment, n2Commitment} 162 expectedSeed, expectedCmtChecksum := makeMatchProof(preimages, commitments) 163 164 seed, cmtChecksum, err := eq.GenerateMatchProof(preimages, nil) 165 if err != nil { 166 t.Fatalf("[GenerateMatchProof] unexpected error: %v", err) 167 } 168 169 if !bytes.Equal(expectedSeed, seed) { 170 t.Fatalf("expected seed %v, got %v", expectedSeed, seed) 171 } 172 173 if !bytes.Equal(expectedCmtChecksum, cmtChecksum) { 174 t.Fatalf("expected commitment checksum %v, got %v", 175 expectedCmtChecksum, cmtChecksum) 176 } 177 if !bytes.Equal(wantCSum, cmtChecksum) { 178 t.Fatalf("expected commitment checksum %v, got %v", 179 wantCSum, cmtChecksum) 180 } 181 182 eq = NewEpochQueue() 183 184 // Queue epoch orders. 185 err = eq.Enqueue(n3) 186 if err != nil { 187 t.Fatalf("[Enqueue]: unexpected error: %v", err) 188 } 189 190 err = eq.Enqueue(n1) 191 if err != nil { 192 t.Fatalf("[Enqueue]: unexpected error: %v", err) 193 } 194 195 err = eq.Enqueue(n2) 196 if err != nil { 197 t.Fatalf("[Enqueue]: unexpected error: %v", err) 198 } 199 200 // Ensure the queue has n1 epoch order. 201 if !eq.Exists(n1OrderID) { 202 t.Fatalf("[Exists] expected order with id %x in the epoch queue", n1OrderID) 203 } 204 205 // Ensure match proof generation works as expected, when there are misses. 206 preimages = []order.Preimage{n1Pimg, n2Pimg} // n3 missed 207 commitments = []order.Commitment{n1Commitment, n2Commitment, n3Commitment} // all orders in the epoch queue 208 expectedSeed, expectedCmtChecksum = makeMatchProof(preimages, commitments) 209 210 misses := []order.OrderID{n3OrderID} 211 seed, cmtChecksum, err = eq.GenerateMatchProof(preimages, misses) 212 if err != nil { 213 t.Fatalf("[GenerateMatchProof] unexpected error: %v", err) 214 } 215 216 if !bytes.Equal(expectedSeed, seed) { 217 t.Fatalf("expected seed %v, got %v", expectedSeed, seed) 218 } 219 220 if !bytes.Equal(expectedCmtChecksum, cmtChecksum) { 221 t.Fatalf("expected commitment checksum %v, got %v", 222 expectedCmtChecksum, cmtChecksum) 223 } 224 if !bytes.Equal(wantCSum, cmtChecksum) { 225 t.Fatalf("expected commitment checksum %v, got %v", 226 wantCSum, cmtChecksum) 227 } 228 229 // Ensure match proof fails when there is a preimage mismatch. 230 preimages = []order.Preimage{n1Pimg} 231 _, _, err = eq.GenerateMatchProof(preimages, nil) 232 if err == nil { 233 t.Fatalf("[GenerateMatchProof] expected a preimage/orders mismatch") 234 } 235 236 preimages = []order.Preimage{n1Pimg, n2Pimg, n3Pimg} 237 _, _, err = eq.GenerateMatchProof(preimages, nil) 238 if err == nil { 239 t.Fatalf("[GenerateMatchProof] expected no order match for preimage error") 240 } 241 } 242 243 func randOrderID() order.OrderID { 244 var oid order.OrderID 245 rand.Read(oid[:]) 246 return oid 247 } 248 249 func randPreimage() order.Preimage { 250 var pi order.Preimage 251 rand.Read(pi[:]) 252 return pi 253 } 254 255 func benchmarkGenerateMatchProof(c int, b *testing.B) { 256 notes := make([]*msgjson.EpochOrderNote, c) 257 preimages := make([]order.Preimage, 0, c) 258 for i := range notes { 259 pi := randPreimage() 260 notes[i] = makeEpochOrderNote("mkt", randOrderID(), 261 msgjson.BuyOrderNum, 1, 2, blake256.Sum256(pi[:]), 10) 262 preimages = append(preimages, pi) 263 } 264 265 numMisses := c / 20 266 misses := make([]order.OrderID, numMisses) 267 for i := range misses { 268 in := rand.Intn(len(notes)) 269 copy(misses[i][:], notes[in].OrderID) 270 271 // Remove the missed preimage. 272 for idx := 0; idx < len(preimages); idx++ { 273 commit := blake256.Sum256(preimages[idx][:]) 274 if bytes.Equal(commit[:], notes[in].Commit[:]) { 275 if idx < len(preimages)-1 { 276 copy(preimages[idx:], preimages[idx+1:]) 277 } 278 preimages[len(preimages)-1] = order.Preimage{} 279 preimages = preimages[:len(preimages)-1] 280 } 281 } 282 } 283 284 b.ResetTimer() 285 286 for i := 0; i < b.N; i++ { 287 eq := NewEpochQueue() 288 289 for _, note := range notes { 290 if err := eq.Enqueue(note); err != nil { 291 b.Fatalf("[Enqueue]: unexpected error: %v", err) 292 } 293 } 294 295 _, _, err := eq.GenerateMatchProof(preimages, misses) 296 if err != nil { 297 b.Error(err) 298 } 299 } 300 } 301 302 func BenchmarkMatchProof500(b *testing.B) { benchmarkGenerateMatchProof(500, b) } 303 func BenchmarkMatchProof1000(b *testing.B) { benchmarkGenerateMatchProof(1000, b) } 304 func BenchmarkMatchProof5000(b *testing.B) { benchmarkGenerateMatchProof(5000, b) }