decred.org/dcrdex@v1.0.5/client/orderbook/orderbook_test.go (about)

     1  package orderbook
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"testing"
     7  
     8  	"decred.org/dcrdex/dex/msgjson"
     9  	"decred.org/dcrdex/dex/order"
    10  )
    11  
    12  func makeOrderBookMsg(seq uint64, mid string, orders []*msgjson.BookOrderNote) *msgjson.OrderBook {
    13  	return &msgjson.OrderBook{
    14  		Seq:      seq,
    15  		MarketID: mid,
    16  		Orders:   orders,
    17  	}
    18  }
    19  
    20  func makeBookOrderNote(seq uint64, mid string, oid order.OrderID, side uint8,
    21  	qty uint64, rate uint64, time uint64) *msgjson.BookOrderNote {
    22  	return &msgjson.BookOrderNote{
    23  		TradeNote: msgjson.TradeNote{
    24  			Side:     side,
    25  			Quantity: qty,
    26  			Rate:     rate,
    27  			TiF:      0,
    28  			Time:     time,
    29  		},
    30  		OrderNote: msgjson.OrderNote{
    31  			Seq:      seq,
    32  			MarketID: mid,
    33  			OrderID:  oid[:],
    34  		},
    35  	}
    36  }
    37  
    38  func makeUnbookOrderNote(seq uint64, mid string, oid order.OrderID) *msgjson.UnbookOrderNote {
    39  	return &msgjson.UnbookOrderNote{
    40  		Seq:      seq,
    41  		MarketID: mid,
    42  		OrderID:  oid[:],
    43  	}
    44  }
    45  
    46  func makeCachedBookOrderNote(orderNote *msgjson.BookOrderNote) *cachedOrderNote {
    47  	return &cachedOrderNote{
    48  		Route:     msgjson.BookOrderRoute,
    49  		OrderNote: orderNote,
    50  	}
    51  }
    52  
    53  func makeCachedUnbookOrderNote(orderNote *msgjson.UnbookOrderNote) *cachedOrderNote {
    54  	return &cachedOrderNote{
    55  		Route:     msgjson.UnbookOrderRoute,
    56  		OrderNote: orderNote,
    57  	}
    58  }
    59  
    60  func makeOrderBook(seq uint64, marketID string, orders []*Order, cachedOrders []*cachedOrderNote, synced bool) *OrderBook {
    61  	ob := NewOrderBook(tLogger)
    62  	ob.noteQueue = cachedOrders
    63  	ob.marketID = marketID
    64  	ob.seq = seq
    65  
    66  	for _, order := range orders {
    67  		ob.orders[order.OrderID] = rateSell{order.Rate, order.sell()}
    68  
    69  		switch order.Side {
    70  		case msgjson.BuyOrderNum:
    71  			ob.buys.Add(order)
    72  
    73  		case msgjson.SellOrderNum:
    74  			ob.sells.Add(order)
    75  		}
    76  	}
    77  
    78  	ob.setSynced(synced)
    79  
    80  	return ob
    81  }
    82  
    83  func TestOrderBookSync(t *testing.T) {
    84  	tests := []struct {
    85  		label             string
    86  		snapshot          *msgjson.OrderBook
    87  		orderBook         *OrderBook
    88  		expected          *OrderBook
    89  		initialQueueState []*cachedOrderNote
    90  		initialSyncState  bool
    91  		wantErr           bool
    92  	}{
    93  		{
    94  			label: "Sync blank unsynced order book",
    95  			snapshot: makeOrderBookMsg(
    96  				2,
    97  				"ob",
    98  				[]*msgjson.BookOrderNote{
    99  					makeBookOrderNote(1, "ob", [32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   100  					makeBookOrderNote(2, "ob", [32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   101  				},
   102  			),
   103  			orderBook: NewOrderBook(tLogger),
   104  			expected: makeOrderBook(
   105  				2,
   106  				"ob",
   107  				[]*Order{
   108  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   109  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   110  				},
   111  				make([]*cachedOrderNote, 0),
   112  				true,
   113  			),
   114  			initialQueueState: make([]*cachedOrderNote, 0),
   115  			initialSyncState:  false,
   116  			wantErr:           false,
   117  		},
   118  		{
   119  			label: "Sync blank order book with no order notes",
   120  			snapshot: makeOrderBookMsg(
   121  				2,
   122  				"ob",
   123  				[]*msgjson.BookOrderNote{},
   124  			),
   125  			orderBook: NewOrderBook(tLogger),
   126  			expected: makeOrderBook(
   127  				2,
   128  				"ob",
   129  				[]*Order{},
   130  				make([]*cachedOrderNote, 0),
   131  				true,
   132  			),
   133  			initialQueueState: make([]*cachedOrderNote, 0),
   134  			initialSyncState:  false,
   135  			wantErr:           false,
   136  		},
   137  		{
   138  			label: "Sync already synced order book",
   139  			snapshot: makeOrderBookMsg(
   140  				2,
   141  				"ob",
   142  				[]*msgjson.BookOrderNote{
   143  					makeBookOrderNote(1, "ob", [32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   144  					makeBookOrderNote(2, "ob", [32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   145  				},
   146  			),
   147  			orderBook:         NewOrderBook(tLogger),
   148  			expected:          nil,
   149  			initialQueueState: make([]*cachedOrderNote, 0),
   150  			initialSyncState:  true,
   151  			wantErr:           true,
   152  		},
   153  		{
   154  			label: "Sync order book with cached unbook order note",
   155  			snapshot: makeOrderBookMsg(
   156  				2,
   157  				"ob",
   158  				[]*msgjson.BookOrderNote{
   159  					makeBookOrderNote(1, "ob", [32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   160  					makeBookOrderNote(2, "ob", [32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   161  				},
   162  			),
   163  			orderBook: NewOrderBook(tLogger),
   164  			expected: makeOrderBook(
   165  				3,
   166  				"ob",
   167  				[]*Order{
   168  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 1, 5),
   169  				},
   170  				make([]*cachedOrderNote, 0),
   171  				true,
   172  			),
   173  			initialQueueState: []*cachedOrderNote{
   174  				makeCachedUnbookOrderNote(makeUnbookOrderNote(3, "ob", [32]byte{'b'})),
   175  			},
   176  			initialSyncState: false,
   177  			wantErr:          false,
   178  		},
   179  		{
   180  			label: "Sync order book with cached book order note",
   181  			snapshot: makeOrderBookMsg(
   182  				3,
   183  				"ob",
   184  				[]*msgjson.BookOrderNote{
   185  					makeBookOrderNote(1, "ob", [32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   186  					makeBookOrderNote(2, "ob", [32]byte{'c'}, msgjson.BuyOrderNum, 5, 2, 5),
   187  					makeBookOrderNote(3, "ob", [32]byte{'d'}, msgjson.SellOrderNum, 6, 3, 10),
   188  				},
   189  			),
   190  			orderBook: NewOrderBook(tLogger),
   191  			expected: makeOrderBook(
   192  				4,
   193  				"ob",
   194  				[]*Order{
   195  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   196  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 5, 2, 5),
   197  					makeOrder([32]byte{'d'}, msgjson.SellOrderNum, 6, 3, 10),
   198  					makeOrder([32]byte{'e'}, msgjson.SellOrderNum, 4, 2, 12),
   199  				},
   200  				make([]*cachedOrderNote, 0),
   201  				true,
   202  			),
   203  			initialQueueState: []*cachedOrderNote{
   204  				makeCachedBookOrderNote(
   205  					makeBookOrderNote(4, "ob", [32]byte{'e'}, msgjson.SellOrderNum, 4, 2, 12)),
   206  			},
   207  			initialSyncState: false,
   208  			wantErr:          false,
   209  		},
   210  	}
   211  
   212  	for idx, tc := range tests {
   213  		tc.orderBook.noteQueue = tc.initialQueueState
   214  		tc.orderBook.setSynced(tc.initialSyncState)
   215  		err := tc.orderBook.Sync(tc.snapshot)
   216  		if (err != nil) != tc.wantErr {
   217  			t.Fatalf("[OrderBook.Sync] #%d: error: %v, wantErr: %v",
   218  				idx+1, err, tc.wantErr)
   219  		}
   220  
   221  		if !tc.wantErr {
   222  			if tc.orderBook.seq != tc.expected.seq {
   223  				t.Fatalf("[OrderBook.Sync] #%d: expected sequence of %d,"+
   224  					" got %d", idx+1, tc.expected.seq, tc.orderBook.seq)
   225  			}
   226  
   227  			if tc.orderBook.marketID != tc.expected.marketID {
   228  				t.Fatalf("[OrderBook.Sync] #%d: expected market id of %s,"+
   229  					" got %s", idx+1, tc.expected.marketID, tc.orderBook.marketID)
   230  			}
   231  
   232  			if len(tc.orderBook.orders) != len(tc.expected.orders) {
   233  				t.Fatalf("[OrderBook.Sync] #%d: expected orders size of %d,"+
   234  					" got %d", idx+1, len(tc.expected.orders), len(tc.orderBook.orders))
   235  			}
   236  
   237  			if len(tc.orderBook.buys.bins) != len(tc.expected.buys.bins) {
   238  				t.Fatalf("[OrderBook.Sync] #%d: expected buys book side "+
   239  					"size of %d, got %d", idx+1, len(tc.expected.buys.bins),
   240  					len(tc.orderBook.buys.bins))
   241  			}
   242  
   243  			if len(tc.orderBook.sells.bins) != len(tc.expected.sells.bins) {
   244  				t.Fatalf("[OrderBook.Sync] #%d: expected buys book side "+
   245  					"size of %d, got %d", idx+1, len(tc.expected.sells.bins),
   246  					len(tc.orderBook.sells.bins))
   247  			}
   248  
   249  			if len(tc.orderBook.noteQueue) != len(tc.expected.noteQueue) {
   250  				t.Fatalf("[OrderBook.Sync] #%d: expected note queue "+
   251  					"size of %d, got %d", idx+1, len(tc.expected.noteQueue),
   252  					len(tc.orderBook.noteQueue))
   253  			}
   254  		}
   255  	}
   256  }
   257  
   258  func TestOrderBookBook(t *testing.T) {
   259  	tests := []struct {
   260  		label     string
   261  		orderBook *OrderBook
   262  		note      *msgjson.BookOrderNote
   263  		expected  *OrderBook
   264  		wantErr   bool
   265  	}{
   266  		{
   267  			label: "Book a buy order",
   268  			orderBook: makeOrderBook(
   269  				2,
   270  				"ob",
   271  				[]*Order{
   272  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   273  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   274  				},
   275  				make([]*cachedOrderNote, 0),
   276  				true,
   277  			),
   278  			note: makeBookOrderNote(3, "ob", [32]byte{'d'}, msgjson.BuyOrderNum, 5, 3, 10),
   279  			expected: makeOrderBook(
   280  				3,
   281  				"ob",
   282  				[]*Order{
   283  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   284  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   285  					makeOrder([32]byte{'d'}, msgjson.BuyOrderNum, 5, 3, 10),
   286  				},
   287  				make([]*cachedOrderNote, 0),
   288  				true,
   289  			),
   290  			wantErr: false,
   291  		},
   292  		// May want to re-implement strict sequence checking. Might use these tests
   293  		// again.
   294  		// {
   295  		// 	label: "Book buy order with outdated sequence value",
   296  		// 	orderBook: makeOrderBook(
   297  		// 		2,
   298  		// 		"ob",
   299  		// 		[]*Order{
   300  		// 			makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   301  		// 			makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   302  		// 		},
   303  		// 		make([]*cachedOrderNote, 0),
   304  		// 		true,
   305  		// 	),
   306  		// 	note: makeBookOrderNote(0, "ob", [32]byte{'a'}, msgjson.BuyOrderNum, 2, 3, 1),
   307  		// 	expected: makeOrderBook(
   308  		// 		2,
   309  		// 		"ob",
   310  		// 		[]*Order{
   311  		// 			makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   312  		// 			makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   313  		// 		},
   314  		// 		make([]*cachedOrderNote, 0),
   315  		// 		true,
   316  		// 	),
   317  		// 	wantErr: false,
   318  		// },
   319  		{
   320  			label: "Book buy order to unsynced order book",
   321  			orderBook: makeOrderBook(
   322  				2,
   323  				"ob",
   324  				[]*Order{
   325  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   326  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   327  				},
   328  				make([]*cachedOrderNote, 0),
   329  				false,
   330  			),
   331  			note: makeBookOrderNote(3, "ob", [32]byte{'a'}, msgjson.BuyOrderNum, 2, 3, 10),
   332  			expected: makeOrderBook(
   333  				2,
   334  				"ob",
   335  				[]*Order{
   336  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   337  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   338  				},
   339  				[]*cachedOrderNote{
   340  					makeCachedBookOrderNote(
   341  						makeBookOrderNote(3, "ob", [32]byte{'a'}, msgjson.BuyOrderNum, 2, 3, 10)),
   342  				},
   343  				false,
   344  			),
   345  			wantErr: false,
   346  		},
   347  		{
   348  			label: "Book sell order to order book with different market id",
   349  			orderBook: makeOrderBook(
   350  				2,
   351  				"ob",
   352  				[]*Order{
   353  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   354  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   355  				},
   356  				make([]*cachedOrderNote, 0),
   357  				true,
   358  			),
   359  			note:     makeBookOrderNote(3, "oc", [32]byte{'a'}, msgjson.SellOrderNum, 2, 3, 10),
   360  			expected: nil,
   361  			wantErr:  true,
   362  		},
   363  		// May want to re-implement strict sequence checking. Might use these tests
   364  		// again.
   365  		// {
   366  		// 	label: "Book sell order to synced order book with future sequence value",
   367  		// 	orderBook: makeOrderBook(
   368  		// 		2,
   369  		// 		"ob",
   370  		// 		[]*Order{
   371  		// 			makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   372  		// 			makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   373  		// 		},
   374  		// 		make([]*cachedOrderNote, 0),
   375  		// 		true,
   376  		// 	),
   377  		// 	note:     makeBookOrderNote(5, "ob", [32]byte{'d'}, msgjson.SellOrderNum, 5, 3, 10),
   378  		// 	expected: nil,
   379  		// 	wantErr:  true,
   380  		// },
   381  	}
   382  
   383  	for idx, tc := range tests {
   384  		err := tc.orderBook.Book(tc.note)
   385  		if (err != nil) != tc.wantErr {
   386  			t.Fatalf("[OrderBook.Book:%s]: error: %v, wantErr: %v",
   387  				tc.label, err, tc.wantErr)
   388  		}
   389  
   390  		if !tc.wantErr {
   391  			if tc.orderBook.seq != tc.expected.seq {
   392  				t.Fatalf("[OrderBook.Book] #%d: expected sequence of %d,"+
   393  					" got %d", idx+1, tc.expected.seq, tc.orderBook.seq)
   394  			}
   395  
   396  			if tc.orderBook.marketID != tc.expected.marketID {
   397  				t.Fatalf("[OrderBook.Book] #%d: expected market id of %s,"+
   398  					" got %s", idx+1, tc.expected.marketID, tc.orderBook.marketID)
   399  			}
   400  
   401  			if len(tc.orderBook.orders) != len(tc.expected.orders) {
   402  				t.Fatalf("[OrderBook.Book] #%d: expected orders size of %d,"+
   403  					" got %d", idx+1, len(tc.expected.orders), len(tc.orderBook.orders))
   404  			}
   405  
   406  			if len(tc.orderBook.buys.bins) != len(tc.expected.buys.bins) {
   407  				t.Fatalf("[OrderBook.Book] #%d: expected buys book side "+
   408  					"size of %d, got %d", idx+1, len(tc.expected.buys.bins),
   409  					len(tc.orderBook.buys.bins))
   410  			}
   411  
   412  			if len(tc.orderBook.sells.bins) != len(tc.expected.sells.bins) {
   413  				t.Fatalf("[OrderBook.Book] #%d: expected buys book side "+
   414  					"size of %d, got %d", idx+1, len(tc.expected.sells.bins),
   415  					len(tc.orderBook.sells.bins))
   416  			}
   417  
   418  			if len(tc.orderBook.noteQueue) != len(tc.expected.noteQueue) {
   419  				t.Fatalf("[OrderBook.Book] #%d: expected note queue "+
   420  					"size of %d, got %d", idx+1, len(tc.expected.noteQueue),
   421  					len(tc.orderBook.noteQueue))
   422  			}
   423  		}
   424  	}
   425  }
   426  
   427  func TestOrderBookUpdateRemaining(t *testing.T) {
   428  	var qty uint64 = 10
   429  	var remaining uint64 = 5
   430  	mid := "abc_xyz"
   431  	oid := order.OrderID{0x01}
   432  
   433  	book := makeOrderBook(
   434  		1,
   435  		mid,
   436  		[]*Order{
   437  			makeOrder(oid, msgjson.SellOrderNum, qty, 1, 2),
   438  		},
   439  		make([]*cachedOrderNote, 0),
   440  		true,
   441  	)
   442  
   443  	urNote := &msgjson.UpdateRemainingNote{
   444  		OrderNote: msgjson.OrderNote{
   445  			OrderID:  oid[:],
   446  			MarketID: mid,
   447  		},
   448  		Remaining: remaining,
   449  	}
   450  
   451  	err := book.UpdateRemaining(urNote)
   452  	if err != nil {
   453  		t.Fatalf("error updating remaining qty: %v", err)
   454  	}
   455  	_, sells, _ := book.Orders()
   456  	if sells[0].Quantity != remaining {
   457  		t.Fatalf("failed to update remaining,. Wanted %d, got %d", remaining, sells[0].Quantity)
   458  	}
   459  
   460  	// Unknown order
   461  	wrongID := order.OrderID{0x02}
   462  	urNote.OrderID = wrongID[:]
   463  	err = book.UpdateRemaining(urNote)
   464  	if err == nil {
   465  		t.Fatalf("no error updating remaining qty for unknown order")
   466  	}
   467  
   468  	// Bad order ID
   469  	urNote.OrderID = []byte{0x03}
   470  	err = book.UpdateRemaining(urNote)
   471  	if err == nil {
   472  		t.Fatalf("no error updating remaining qty for invalid order ID")
   473  	}
   474  }
   475  
   476  func TestOrderBookUnbook(t *testing.T) {
   477  	tests := []struct {
   478  		label     string
   479  		orderBook *OrderBook
   480  		note      *msgjson.UnbookOrderNote
   481  		expected  *OrderBook
   482  		wantErr   bool
   483  	}{
   484  		{
   485  			label: "Unbook sell order with synced order book",
   486  			orderBook: makeOrderBook(
   487  				2,
   488  				"ob",
   489  				[]*Order{
   490  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   491  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   492  				},
   493  				make([]*cachedOrderNote, 0),
   494  				true,
   495  			),
   496  			note: makeUnbookOrderNote(3, "ob", [32]byte{'c'}),
   497  			expected: makeOrderBook(
   498  				3,
   499  				"ob",
   500  				[]*Order{
   501  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   502  				},
   503  				make([]*cachedOrderNote, 0),
   504  				true,
   505  			),
   506  			wantErr: false,
   507  		},
   508  		// May want to re-implement strict sequence checking. Might use these tests
   509  		// again.
   510  		// {
   511  		// 	label: "Unbook sell order with outdated sequence value",
   512  		// 	orderBook: makeOrderBook(
   513  		// 		2,
   514  		// 		"ob",
   515  		// 		[]*Order{
   516  		// 			makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   517  		// 			makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   518  		// 		},
   519  		// 		make([]*cachedOrderNote, 0),
   520  		// 		true,
   521  		// 	),
   522  		// 	note: makeUnbookOrderNote(0, "ob", [32]byte{'a'}),
   523  		// 	expected: makeOrderBook(
   524  		// 		2,
   525  		// 		"ob",
   526  		// 		[]*Order{
   527  		// 			makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   528  		// 			makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   529  		// 		},
   530  		// 		make([]*cachedOrderNote, 0),
   531  		// 		true,
   532  		// 	),
   533  		// 	wantErr: false,
   534  		// },
   535  		{
   536  			label: "Unbook sell order with unsynced order book",
   537  			orderBook: makeOrderBook(
   538  				2,
   539  				"ob",
   540  				[]*Order{
   541  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   542  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   543  				},
   544  				make([]*cachedOrderNote, 0),
   545  				false,
   546  			),
   547  			note: makeUnbookOrderNote(3, "ob", [32]byte{'a'}),
   548  			expected: makeOrderBook(
   549  				2,
   550  				"ob",
   551  				[]*Order{
   552  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   553  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   554  				},
   555  				[]*cachedOrderNote{
   556  					makeCachedUnbookOrderNote(
   557  						makeUnbookOrderNote(3, "ob", [32]byte{'a'})),
   558  				},
   559  				false,
   560  			),
   561  			wantErr: false,
   562  		},
   563  		{
   564  			label: "Unbook sell order with different market id",
   565  			orderBook: makeOrderBook(
   566  				2,
   567  				"ob",
   568  				[]*Order{
   569  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   570  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   571  				},
   572  				make([]*cachedOrderNote, 0),
   573  				true,
   574  			),
   575  			note:     makeUnbookOrderNote(3, "oc", [32]byte{'a'}),
   576  			expected: nil,
   577  			wantErr:  true,
   578  		},
   579  		{
   580  			label: "Unbook sell order with future sequence value",
   581  			orderBook: makeOrderBook(
   582  				2,
   583  				"ob",
   584  				[]*Order{
   585  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   586  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   587  				},
   588  				make([]*cachedOrderNote, 0),
   589  				true,
   590  			),
   591  			note:     makeUnbookOrderNote(5, "ob", [32]byte{'d'}),
   592  			expected: nil,
   593  			wantErr:  true,
   594  		},
   595  	}
   596  
   597  	for idx, tc := range tests {
   598  		err := tc.orderBook.Unbook(tc.note)
   599  		if (err != nil) != tc.wantErr {
   600  			t.Fatalf("[OrderBook.Book:%s]: error: %v, wantErr: %v",
   601  				tc.label, err, tc.wantErr)
   602  		}
   603  
   604  		if !tc.wantErr {
   605  			if tc.orderBook.seq != tc.expected.seq {
   606  				t.Fatalf("[OrderBook.Book] #%d: expected sequence of %d,"+
   607  					" got %d", idx+1, tc.expected.seq, tc.orderBook.seq)
   608  			}
   609  
   610  			if tc.orderBook.marketID != tc.expected.marketID {
   611  				t.Fatalf("[OrderBook.Book] #%d: expected market id of %s,"+
   612  					" got %s", idx+1, tc.expected.marketID, tc.orderBook.marketID)
   613  			}
   614  
   615  			if len(tc.orderBook.orders) != len(tc.expected.orders) {
   616  				t.Fatalf("[OrderBook.Book] #%d: expected orders size of %d,"+
   617  					" got %d", idx+1, len(tc.expected.orders), len(tc.orderBook.orders))
   618  			}
   619  
   620  			if len(tc.orderBook.buys.bins) != len(tc.expected.buys.bins) {
   621  				t.Fatalf("[OrderBook.Book] #%d: expected buys book side "+
   622  					"size of %d, got %d", idx+1, len(tc.expected.buys.bins),
   623  					len(tc.orderBook.buys.bins))
   624  			}
   625  
   626  			if len(tc.orderBook.sells.bins) != len(tc.expected.sells.bins) {
   627  				t.Fatalf("[OrderBook.Book] #%d: expected buys book side "+
   628  					"size of %d, got %d", idx+1, len(tc.expected.sells.bins),
   629  					len(tc.orderBook.sells.bins))
   630  			}
   631  
   632  			if len(tc.orderBook.noteQueue) != len(tc.expected.noteQueue) {
   633  				t.Fatalf("[OrderBook.Book] #%d: expected note queue "+
   634  					"size of %d, got %d", idx+1, len(tc.expected.noteQueue),
   635  					len(tc.orderBook.noteQueue))
   636  			}
   637  		}
   638  	}
   639  }
   640  
   641  func TestOrderBookBestNOrders(t *testing.T) {
   642  	tests := []struct {
   643  		label     string
   644  		orderBook *OrderBook
   645  		n         int
   646  		sell      bool
   647  		expected  []*Order
   648  		wantErr   bool
   649  	}{
   650  		{
   651  			label: "Fetch best N orders from buy book side",
   652  			orderBook: makeOrderBook(
   653  				2,
   654  				"ob",
   655  				[]*Order{
   656  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   657  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   658  					makeOrder([32]byte{'d'}, msgjson.BuyOrderNum, 5, 3, 10),
   659  					makeOrder([32]byte{'e'}, msgjson.BuyOrderNum, 8, 4, 12),
   660  				},
   661  				make([]*cachedOrderNote, 0),
   662  				true,
   663  			),
   664  			n:    3,
   665  			sell: false,
   666  			expected: []*Order{
   667  				makeOrder([32]byte{'e'}, msgjson.BuyOrderNum, 8, 4, 12),
   668  				makeOrder([32]byte{'d'}, msgjson.BuyOrderNum, 5, 3, 10),
   669  				makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   670  			},
   671  			wantErr: false,
   672  		},
   673  		{
   674  			label: "Fetch best N orders from empty sell book side",
   675  			orderBook: makeOrderBook(
   676  				2,
   677  				"ob",
   678  				[]*Order{
   679  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   680  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   681  					makeOrder([32]byte{'d'}, msgjson.BuyOrderNum, 5, 3, 10),
   682  					makeOrder([32]byte{'e'}, msgjson.BuyOrderNum, 8, 4, 12),
   683  				},
   684  				make([]*cachedOrderNote, 0),
   685  				true,
   686  			),
   687  			n:        3,
   688  			sell:     true,
   689  			expected: []*Order{},
   690  			wantErr:  false,
   691  		},
   692  		{
   693  			label: "Fetch best N orders (all orders) from sell book side",
   694  			orderBook: makeOrderBook(
   695  				2,
   696  				"ob",
   697  				[]*Order{
   698  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   699  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   700  					makeOrder([32]byte{'d'}, msgjson.SellOrderNum, 5, 3, 10),
   701  					makeOrder([32]byte{'e'}, msgjson.SellOrderNum, 8, 4, 12),
   702  				},
   703  				make([]*cachedOrderNote, 0),
   704  				true,
   705  			),
   706  			n:    5,
   707  			sell: true,
   708  			expected: []*Order{
   709  				makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   710  				makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   711  				makeOrder([32]byte{'d'}, msgjson.SellOrderNum, 5, 3, 10),
   712  				makeOrder([32]byte{'e'}, msgjson.SellOrderNum, 8, 4, 12),
   713  			},
   714  			wantErr: false,
   715  		},
   716  		{
   717  			label: "Fetch best N orders from unsynced order book",
   718  			orderBook: makeOrderBook(
   719  				2,
   720  				"ob",
   721  				[]*Order{
   722  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   723  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   724  					makeOrder([32]byte{'d'}, msgjson.SellOrderNum, 5, 3, 10),
   725  					makeOrder([32]byte{'e'}, msgjson.SellOrderNum, 8, 4, 12),
   726  				},
   727  				make([]*cachedOrderNote, 0),
   728  				false,
   729  			),
   730  			n:        5,
   731  			sell:     true,
   732  			expected: nil,
   733  			wantErr:  true,
   734  		},
   735  		{
   736  			label: "Fetch best N orders (some orders) from sell book side",
   737  			orderBook: makeOrderBook(
   738  				2,
   739  				"ob",
   740  				[]*Order{
   741  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   742  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   743  					makeOrder([32]byte{'d'}, msgjson.SellOrderNum, 5, 3, 10),
   744  					makeOrder([32]byte{'e'}, msgjson.SellOrderNum, 8, 4, 12),
   745  				},
   746  				make([]*cachedOrderNote, 0),
   747  				true,
   748  			),
   749  			n:    3,
   750  			sell: true,
   751  			expected: []*Order{
   752  				makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   753  				makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   754  				makeOrder([32]byte{'d'}, msgjson.SellOrderNum, 5, 3, 10),
   755  			},
   756  			wantErr: false,
   757  		},
   758  	}
   759  
   760  	for idx, tc := range tests {
   761  		best, _, err := tc.orderBook.BestNOrders(tc.n, tc.sell)
   762  		if (err != nil) != tc.wantErr {
   763  			t.Fatalf("[OrderBook.BestNOrders] #%d: error: %v, wantErr: %v",
   764  				idx+1, err, tc.wantErr)
   765  		}
   766  
   767  		if !tc.wantErr {
   768  			if len(best) != len(tc.expected) {
   769  				t.Fatalf("[OrderBook.BestNOrders] #%d: expected best N orders "+
   770  					"size of %d, got %d", idx+1, len(tc.expected),
   771  					len(best))
   772  			}
   773  
   774  			for i := 0; i < len(best); i++ {
   775  				if !bytes.Equal(best[i].OrderID[:], tc.expected[i].OrderID[:]) {
   776  					t.Fatalf("[OrderBook.BestNOrders] #%d: expected order at "+
   777  						"index %d to be %x, got %x", idx+1, i,
   778  						tc.expected[i].OrderID[:], best[i].OrderID[:])
   779  				}
   780  
   781  				if best[i].Quantity != tc.expected[i].Quantity {
   782  					t.Fatalf("[OrderBook.BestNOrders] #%d: expected order "+
   783  						"quantity at index %d to be %x, got %x", idx+1, i,
   784  						tc.expected[i].Quantity, best[i].Quantity)
   785  				}
   786  
   787  				if best[i].Time != tc.expected[i].Time {
   788  					t.Fatalf("[OrderBook.BestNOrders] #%d: expected order "+
   789  						"timestamp at index %d to be %x, got %x", idx+1, i,
   790  						tc.expected[i].Time, best[i].Time)
   791  				}
   792  			}
   793  		}
   794  	}
   795  }
   796  
   797  func TestOrderBookBestFill(t *testing.T) {
   798  	tests := []struct {
   799  		label     string
   800  		orderBook *OrderBook
   801  		qty       uint64
   802  		side      uint8
   803  		expected  []*Fill
   804  	}{
   805  		{
   806  			label: "Fetch best fill from buy side",
   807  			orderBook: makeOrderBook(
   808  				2,
   809  				"ob",
   810  				[]*Order{
   811  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   812  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   813  					makeOrder([32]byte{'d'}, msgjson.BuyOrderNum, 5, 3, 10),
   814  					makeOrder([32]byte{'e'}, msgjson.BuyOrderNum, 8, 4, 12),
   815  				},
   816  				make([]*cachedOrderNote, 0),
   817  				true,
   818  			),
   819  			qty:  24,
   820  			side: msgjson.BuyOrderNum,
   821  			expected: []*Fill{
   822  				{
   823  					Rate:     4,
   824  					Quantity: 8,
   825  				},
   826  				{
   827  					Rate:     3,
   828  					Quantity: 5,
   829  				},
   830  				{
   831  					Rate:     2,
   832  					Quantity: 10,
   833  				},
   834  				{
   835  					Rate:     1,
   836  					Quantity: 1,
   837  				},
   838  			},
   839  		},
   840  		{
   841  			label: "Fetch best fill from empty buy side",
   842  			orderBook: makeOrderBook(
   843  				2,
   844  				"ob",
   845  				[]*Order{
   846  					makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 1, 2),
   847  					makeOrder([32]byte{'c'}, msgjson.SellOrderNum, 10, 2, 5),
   848  					makeOrder([32]byte{'d'}, msgjson.SellOrderNum, 5, 3, 10),
   849  					makeOrder([32]byte{'e'}, msgjson.SellOrderNum, 8, 4, 12),
   850  				},
   851  				make([]*cachedOrderNote, 0),
   852  				true,
   853  			),
   854  			qty:      24,
   855  			side:     msgjson.BuyOrderNum,
   856  			expected: []*Fill{},
   857  		},
   858  		{
   859  			label: "Fetch best fill (order book total less than fill quantity) from buy side",
   860  			orderBook: makeOrderBook(
   861  				2,
   862  				"ob",
   863  				[]*Order{
   864  					makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 1, 2),
   865  					makeOrder([32]byte{'c'}, msgjson.BuyOrderNum, 10, 2, 5),
   866  				},
   867  				make([]*cachedOrderNote, 0),
   868  				true,
   869  			),
   870  			qty:  40,
   871  			side: msgjson.BuyOrderNum,
   872  			expected: []*Fill{
   873  				{
   874  					Rate:     2,
   875  					Quantity: 10,
   876  				},
   877  				{
   878  					Rate:     1,
   879  					Quantity: 10,
   880  				},
   881  			},
   882  		},
   883  	}
   884  
   885  	// bestFill returns the best fill for a quantity from the provided side.
   886  	bestFill := func(ob *OrderBook, qty uint64, side uint8) ([]*Fill, bool) {
   887  		switch side {
   888  		case msgjson.BuyOrderNum:
   889  			return ob.buys.BestFill(qty)
   890  		case msgjson.SellOrderNum:
   891  			return ob.sells.BestFill(qty)
   892  		}
   893  		return nil, false
   894  	}
   895  
   896  	for idx, tc := range tests {
   897  		best, _ := bestFill(tc.orderBook, tc.qty, tc.side)
   898  
   899  		if len(best) != len(tc.expected) {
   900  			t.Fatalf("[OrderBook.BestFill] #%d: expected best fill "+
   901  				"size of %d, got %d", idx+1, len(tc.expected), len(best))
   902  		}
   903  
   904  		for i := 0; i < len(best); i++ {
   905  			if best[i].Rate != tc.expected[i].Rate {
   906  				t.Fatalf("[OrderBook.BestFill] #%d: expected fill at "+
   907  					"index %d to have rate %d, got %d", idx+1, i,
   908  					tc.expected[i].Rate, best[i].Rate)
   909  			}
   910  
   911  			if best[i].Quantity != tc.expected[i].Quantity {
   912  				t.Fatalf("[OrderBook.BestFill] #%d: expected fill at "+
   913  					"index %d to have quantity %d, got %d", idx+1, i,
   914  					tc.expected[i].Quantity, best[i].Quantity)
   915  			}
   916  		}
   917  	}
   918  }
   919  
   920  func TestVWAP(t *testing.T) {
   921  	orders := []*Order{
   922  		// buys
   923  		makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 200, 2),
   924  		makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 20, 180, 2),
   925  		makeOrder([32]byte{'b'}, msgjson.BuyOrderNum, 10, 160, 2),
   926  
   927  		// sells
   928  		makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 220, 2),
   929  		makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 20, 240, 2),
   930  		makeOrder([32]byte{'b'}, msgjson.SellOrderNum, 10, 260, 2),
   931  	}
   932  
   933  	ob := makeOrderBook(1, "ob", orders, make([]*cachedOrderNote, 0), true)
   934  
   935  	type test struct {
   936  		sell    bool
   937  		lots    uint64
   938  		lotSize uint64
   939  
   940  		expectedFilled  bool
   941  		expectedAvg     uint64
   942  		expectedExtrema uint64
   943  	}
   944  
   945  	tests := []test{
   946  		{
   947  			sell:    true,
   948  			lots:    1,
   949  			lotSize: 10,
   950  
   951  			expectedFilled:  true,
   952  			expectedAvg:     220,
   953  			expectedExtrema: 220,
   954  		},
   955  		{
   956  			sell:    true,
   957  			lots:    2,
   958  			lotSize: 10,
   959  
   960  			expectedFilled:  true,
   961  			expectedAvg:     230,
   962  			expectedExtrema: 240,
   963  		},
   964  		{
   965  			sell:    true,
   966  			lots:    3,
   967  			lotSize: 10,
   968  
   969  			expectedFilled:  true,
   970  			expectedAvg:     (220 + 240 + 240) / 3,
   971  			expectedExtrema: 240,
   972  		},
   973  		{
   974  			sell:    true,
   975  			lots:    4,
   976  			lotSize: 10,
   977  
   978  			expectedFilled:  true,
   979  			expectedAvg:     (220 + 240 + 240 + 260) / 4,
   980  			expectedExtrema: 260,
   981  		},
   982  		{
   983  			sell:    true,
   984  			lots:    5,
   985  			lotSize: 10,
   986  
   987  			expectedFilled: false,
   988  		},
   989  		{
   990  			sell:    false,
   991  			lots:    1,
   992  			lotSize: 10,
   993  
   994  			expectedFilled:  true,
   995  			expectedAvg:     200,
   996  			expectedExtrema: 200,
   997  		},
   998  		{
   999  			sell:    false,
  1000  			lots:    2,
  1001  			lotSize: 10,
  1002  
  1003  			expectedFilled:  true,
  1004  			expectedAvg:     190,
  1005  			expectedExtrema: 180,
  1006  		},
  1007  		{
  1008  			sell:    false,
  1009  			lots:    3,
  1010  			lotSize: 10,
  1011  
  1012  			expectedFilled:  true,
  1013  			expectedAvg:     (200 + 180 + 180) / 3,
  1014  			expectedExtrema: 180,
  1015  		},
  1016  		{
  1017  			sell:    false,
  1018  			lots:    4,
  1019  			lotSize: 10,
  1020  
  1021  			expectedFilled:  true,
  1022  			expectedAvg:     (200 + 180 + 180 + 160) / 4,
  1023  			expectedExtrema: 160,
  1024  		},
  1025  		{
  1026  			sell:    false,
  1027  			lots:    5,
  1028  			lotSize: 10,
  1029  
  1030  			expectedFilled: false,
  1031  		},
  1032  	}
  1033  
  1034  	for idx, test := range tests {
  1035  		avg, extrema, filled, err := ob.VWAP(test.lots, test.lotSize, test.sell)
  1036  		if err != nil {
  1037  			t.Fatalf("[VWAP] #%d: unexpected error: %v", idx+1, err)
  1038  		}
  1039  
  1040  		if filled != test.expectedFilled {
  1041  			t.Fatalf("[VWAP] #%d: expected filled to be %t, got %t", idx+1, test.expectedFilled, filled)
  1042  		}
  1043  
  1044  		if avg != test.expectedAvg {
  1045  			t.Fatalf("[VWAP] #%d: expected average to be %d, got %d", idx+1, test.expectedAvg, avg)
  1046  		}
  1047  
  1048  		if extrema != test.expectedExtrema {
  1049  			t.Fatalf("[VWAP] #%d: expected extrema to be %d, got %d", idx+1, test.expectedExtrema, extrema)
  1050  		}
  1051  	}
  1052  }
  1053  
  1054  func TestValidateMatchProof(t *testing.T) {
  1055  	mid := "mkt"
  1056  	ob := NewOrderBook(tLogger)
  1057  	epoch := uint64(10)
  1058  	n1Pimg := [32]byte{'1'}
  1059  	n1Commitment := makeCommitment(n1Pimg)
  1060  	n1OrderID := [32]byte{'a'}
  1061  	n1 := makeEpochOrderNote(mid, n1OrderID, msgjson.BuyOrderNum, 1, 2, n1Commitment, epoch)
  1062  
  1063  	n2Pimg := [32]byte{'2'}
  1064  	n2Commitment := makeCommitment(n2Pimg)
  1065  	n2OrderID := [32]byte{'b'}
  1066  	n2 := makeEpochOrderNote(mid, n2OrderID, msgjson.BuyOrderNum, 1, 2, n2Commitment, epoch)
  1067  
  1068  	n3Pimg := [32]byte{'3'}
  1069  	n3Commitment := makeCommitment(n3Pimg)
  1070  	n3OrderID := [32]byte{'c'}
  1071  	n3 := makeEpochOrderNote(mid, n3OrderID, msgjson.BuyOrderNum, 1, 2, n3Commitment, epoch)
  1072  
  1073  	err := ob.Enqueue(n1)
  1074  	if err != nil {
  1075  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1076  	}
  1077  
  1078  	err = ob.Enqueue(n2)
  1079  	if err != nil {
  1080  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1081  	}
  1082  
  1083  	err = ob.Enqueue(n3)
  1084  	if err != nil {
  1085  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1086  	}
  1087  
  1088  	expectedCSum, _ := hex.DecodeString("9db8c0547f3b80574df730c3b7005ccef4310e93f766442110fc2c9353230985")
  1089  	expectedSeed, _ := hex.DecodeString("e2b770f60baab7ac877edfa55bd1443b591c1cdd461667c6eb737ae0c65daf2d")
  1090  
  1091  	matchProofNote := msgjson.MatchProofNote{
  1092  		MarketID:  mid,
  1093  		Epoch:     epoch,
  1094  		Preimages: []msgjson.Bytes{n1Pimg[:], n2Pimg[:], n3Pimg[:]},
  1095  		Misses:    []msgjson.Bytes{},
  1096  		CSum:      expectedCSum,
  1097  		Seed:      expectedSeed,
  1098  	}
  1099  
  1100  	// Ensure a valid match proof message gets validated as expected.
  1101  	if err := ob.ValidateMatchProof(matchProofNote); err != nil {
  1102  		t.Fatalf("[ValidateMatchProof]: unexpected error: %v", err)
  1103  	}
  1104  
  1105  	ob = NewOrderBook(tLogger)
  1106  
  1107  	err = ob.Enqueue(n1)
  1108  	if err != nil {
  1109  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1110  	}
  1111  
  1112  	err = ob.Enqueue(n2)
  1113  	if err != nil {
  1114  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1115  	}
  1116  
  1117  	err = ob.Enqueue(n3)
  1118  	if err != nil {
  1119  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1120  	}
  1121  
  1122  	expectedCSumWithMisses := expectedCSum // csum not affected by misses
  1123  	expectedSeedWithMisses, _ := hex.DecodeString("01a161289f06be16ea9b5a5a5492f5664f3e92750dc5ce3fa5775eb9be225730")
  1124  
  1125  	matchProofNoteWithMisses := msgjson.MatchProofNote{
  1126  		MarketID:  mid,
  1127  		Epoch:     epoch,
  1128  		Preimages: []msgjson.Bytes{n1Pimg[:], n3Pimg[:]},
  1129  		Misses:    []msgjson.Bytes{n2.OrderID},
  1130  		CSum:      expectedCSumWithMisses,
  1131  		Seed:      expectedSeedWithMisses,
  1132  	}
  1133  
  1134  	// Ensure a valid match proof message gets validated as expected.
  1135  	if err := ob.ValidateMatchProof(matchProofNoteWithMisses); err != nil {
  1136  		t.Fatalf("[ValidateMatchProof (with misses)]: unexpected error: %v", err)
  1137  	}
  1138  
  1139  	ob = NewOrderBook(tLogger)
  1140  
  1141  	// firstProof for idx-1, length mismatch ignored
  1142  	emptyProofNote := msgjson.MatchProofNote{
  1143  		MarketID:  mid,
  1144  		Epoch:     epoch - 1,                  // previous
  1145  		Preimages: []msgjson.Bytes{n1Pimg[:]}, // ob will be missing this, but it's the firstProof, so no error
  1146  	}
  1147  	if err := ob.ValidateMatchProof(emptyProofNote); err != nil {
  1148  		t.Fatalf("[ValidateMatchProof (empty)]: unexpected error: %v", err)
  1149  	}
  1150  
  1151  	// next proof will not permit length mismatches
  1152  	err = ob.Enqueue(n1)
  1153  	if err != nil {
  1154  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1155  	}
  1156  
  1157  	err = ob.Enqueue(n2)
  1158  	if err != nil {
  1159  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1160  	}
  1161  
  1162  	err = ob.Enqueue(n3)
  1163  	if err != nil {
  1164  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1165  	}
  1166  
  1167  	matchProofNote = msgjson.MatchProofNote{
  1168  		MarketID:  mid,
  1169  		Epoch:     epoch,
  1170  		Preimages: []msgjson.Bytes{n1Pimg[:], n2Pimg[:]},
  1171  		Misses:    []msgjson.Bytes{},
  1172  		CSum:      expectedCSum,
  1173  		Seed:      expectedSeed,
  1174  	}
  1175  
  1176  	// Ensure a invalid match proof message (missing a preimage) gets
  1177  	// detected as expected.
  1178  	if err := ob.ValidateMatchProof(matchProofNote); err == nil {
  1179  		t.Fatalf("[ValidateMatchProof (missing a preimage)]: unexpected an error")
  1180  	}
  1181  
  1182  	ob = NewOrderBook(tLogger)
  1183  
  1184  	err = ob.Enqueue(n1)
  1185  	if err != nil {
  1186  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1187  	}
  1188  
  1189  	err = ob.Enqueue(n2)
  1190  	if err != nil {
  1191  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1192  	}
  1193  
  1194  	err = ob.Enqueue(n3)
  1195  	if err != nil {
  1196  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1197  	}
  1198  
  1199  	junkSeed, _ := hex.DecodeString("e2b770f60baab7ac877edfa55bd1443b591c1cdd461667c6eb737ae0c65daf2d")
  1200  
  1201  	matchProofNote = msgjson.MatchProofNote{
  1202  		MarketID:  mid,
  1203  		Epoch:     epoch,
  1204  		Preimages: []msgjson.Bytes{n1Pimg[:], n3Pimg[:]},
  1205  		Misses:    []msgjson.Bytes{n2.OrderID},
  1206  		CSum:      expectedCSum,
  1207  		Seed:      junkSeed,
  1208  	}
  1209  
  1210  	// Ensure a invalid match proof message (invalid seed) gets detected
  1211  	// as expected.
  1212  	if err := ob.ValidateMatchProof(matchProofNote); err == nil {
  1213  		t.Fatalf("[ValidateMatchProof (invalid seed)]: unexpected error: %v", err)
  1214  	}
  1215  
  1216  	ob = NewOrderBook(tLogger)
  1217  
  1218  	err = ob.Enqueue(n1)
  1219  	if err != nil {
  1220  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1221  	}
  1222  
  1223  	err = ob.Enqueue(n2)
  1224  	if err != nil {
  1225  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1226  	}
  1227  
  1228  	err = ob.Enqueue(n3)
  1229  	if err != nil {
  1230  		t.Fatalf("[Enqueue]: unexpected error: %v", err)
  1231  	}
  1232  
  1233  	junkCSum, _ := hex.DecodeString("000000000f3b80574df730c3b7005ccef4310e93f766442110fc2c9353230985")
  1234  
  1235  	matchProofNote = msgjson.MatchProofNote{
  1236  		MarketID:  mid,
  1237  		Epoch:     epoch,
  1238  		Preimages: []msgjson.Bytes{n1Pimg[:], n3Pimg[:]},
  1239  		Misses:    []msgjson.Bytes{n2.OrderID},
  1240  		CSum:      junkCSum,
  1241  		Seed:      expectedSeedWithMisses,
  1242  	}
  1243  
  1244  	// Ensure a invalid match proof message (invalid csum) gets detected
  1245  	// as expected.
  1246  	if err := ob.ValidateMatchProof(matchProofNote); err == nil {
  1247  		t.Fatalf("[ValidateMatchProof (invalid csum)]: unexpected error: %v", err)
  1248  	}
  1249  }