decred.org/dcrdex@v1.0.5/server/db/driver/pg/orders_online_test.go (about)

     1  //go:build pgonline
     2  
     3  package pg
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"encoding/hex"
     9  	"fmt"
    10  	"testing"
    11  	"time"
    12  
    13  	"decred.org/dcrdex/dex/order"
    14  	"decred.org/dcrdex/server/account"
    15  	"decred.org/dcrdex/server/db"
    16  	"decred.org/dcrdex/server/db/driver/pg/internal"
    17  	"github.com/davecgh/go-spew/spew"
    18  )
    19  
    20  const cancelThreshWindow = 100 // spec
    21  
    22  func TestStoreOrder(t *testing.T) {
    23  	if err := cleanTables(archie.db); err != nil {
    24  		t.Fatalf("cleanTables: %v", err)
    25  	}
    26  
    27  	orderBadLotSize := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0)
    28  	orderBadLotSize.Quantity /= 2
    29  
    30  	orderBadMarket := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0)
    31  	orderBadMarket.BaseAsset = AssetDCR
    32  	orderBadMarket.QuoteAsset = AssetDCR // same as base
    33  
    34  	// order ID for a cancel order
    35  	orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d")
    36  	var targetOrderID order.OrderID
    37  	copy(targetOrderID[:], orderID0)
    38  
    39  	limitA := newLimitOrder(false, 4800000, 1, order.StandingTiF, 0)
    40  	marketSellA := newMarketSellOrder(2, 1)
    41  	marketSellB := newMarketSellOrder(2, 0)
    42  	cancelA := newCancelOrder(targetOrderID, AssetDCR, AssetBTC, 0)
    43  
    44  	// Order with the same commitment as limitA, but different order id.
    45  	limitAx := new(order.LimitOrder)
    46  	*limitAx = *limitA
    47  	limitAx.SetTime(time.Now())
    48  
    49  	var epochIdx, epochDur int64 = 13245678, 6000
    50  
    51  	type args struct {
    52  		ord    order.Order
    53  		status order.OrderStatus
    54  	}
    55  	tests := []struct {
    56  		name        string
    57  		args        args
    58  		wantErr     bool
    59  		wantErrType error
    60  	}{
    61  		{
    62  			name: "ok limit booked (active)",
    63  			args: args{
    64  				ord:    newLimitOrder(false, 4500000, 1, order.StandingTiF, 0),
    65  				status: order.OrderStatusBooked,
    66  			},
    67  			wantErr: false,
    68  		},
    69  		{
    70  			name: "ok limit epoch (active)",
    71  			args: args{
    72  				ord:    newLimitOrder(false, 5000000, 1, order.StandingTiF, 0),
    73  				status: order.OrderStatusEpoch,
    74  			},
    75  			wantErr: false,
    76  		},
    77  		{
    78  			name: "ok limit canceled (archived)",
    79  			args: args{
    80  				ord:    newLimitOrder(false, 4700000, 1, order.StandingTiF, 0),
    81  				status: order.OrderStatusCanceled,
    82  			},
    83  			wantErr: false,
    84  		},
    85  		{
    86  			name: "ok limit executed (archived)",
    87  			args: args{
    88  				ord:    limitA,
    89  				status: order.OrderStatusExecuted,
    90  			},
    91  			wantErr: false,
    92  		},
    93  		{
    94  			name: "limit duplicate",
    95  			args: args{
    96  				ord:    limitA,
    97  				status: order.OrderStatusExecuted,
    98  			},
    99  			wantErr:     true,
   100  			wantErrType: db.ArchiveError{Code: db.ErrReusedCommit},
   101  		},
   102  		{
   103  			name: "limit duplicate by commit only",
   104  			args: args{
   105  				ord:    limitAx,
   106  				status: order.OrderStatusExecuted,
   107  			},
   108  			wantErr:     true,
   109  			wantErrType: db.ArchiveError{Code: db.ErrReusedCommit},
   110  		},
   111  		{
   112  			name: "limit bad quantity (lot size)",
   113  			args: args{
   114  				ord:    orderBadLotSize,
   115  				status: order.OrderStatusEpoch,
   116  			},
   117  			wantErr:     true,
   118  			wantErrType: db.ArchiveError{Code: db.ErrInvalidOrder},
   119  		},
   120  		{
   121  			name: "limit bad trading pair",
   122  			args: args{
   123  				ord:    orderBadMarket,
   124  				status: order.OrderStatusEpoch,
   125  			},
   126  			wantErr:     true,
   127  			wantErrType: db.ArchiveError{Code: db.ErrUnsupportedMarket},
   128  		},
   129  		{
   130  			name: "market sell - bad status (booked)",
   131  			args: args{
   132  				ord:    marketSellB,
   133  				status: order.OrderStatusBooked,
   134  			},
   135  			wantErr:     true,
   136  			wantErrType: db.ArchiveError{Code: db.ErrInvalidOrder},
   137  		},
   138  		{
   139  			name: "market sell - bad status (canceled)",
   140  			args: args{
   141  				ord:    marketSellB,
   142  				status: order.OrderStatusCanceled,
   143  			},
   144  			wantErr:     true,
   145  			wantErrType: db.ArchiveError{Code: db.ErrInvalidOrder},
   146  		},
   147  		{
   148  			name: "market sell - active",
   149  			args: args{
   150  				ord:    marketSellB,
   151  				status: order.OrderStatusEpoch,
   152  			},
   153  			wantErr: false,
   154  		},
   155  		{
   156  			name: "market sell - archived",
   157  			args: args{
   158  				ord:    marketSellA,
   159  				status: order.OrderStatusExecuted,
   160  			},
   161  			wantErr: false,
   162  		},
   163  		{
   164  			name: "market sell - already in other table",
   165  			args: args{
   166  				ord:    marketSellA,
   167  				status: order.OrderStatusExecuted,
   168  			},
   169  			wantErr:     true,
   170  			wantErrType: db.ArchiveError{Code: db.ErrReusedCommit},
   171  		},
   172  		{
   173  			name: "market sell - duplicate archived order",
   174  			args: args{
   175  				ord:    marketSellB, // dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d
   176  				status: order.OrderStatusExecuted,
   177  			},
   178  			wantErr:     true,
   179  			wantErrType: db.ArchiveError{Code: db.ErrReusedCommit},
   180  		},
   181  		{
   182  			name: "cancel order",
   183  			args: args{
   184  				ord:    cancelA,
   185  				status: order.OrderStatusExecuted,
   186  			},
   187  			wantErr: false,
   188  		},
   189  		{
   190  			name: "cancel order - duplicate archived order",
   191  			args: args{
   192  				ord:    cancelA,
   193  				status: order.OrderStatusExecuted,
   194  			},
   195  			wantErr:     true,
   196  			wantErrType: db.ArchiveError{Code: db.ErrReusedCommit},
   197  		},
   198  	}
   199  	for _, tt := range tests {
   200  		t.Run(tt.name, func(t *testing.T) {
   201  			err := archie.StoreOrder(tt.args.ord, epochIdx, epochDur, tt.args.status)
   202  			if (err != nil) != tt.wantErr {
   203  				t.Errorf("StoreOrder() error = %v, wantErr %v", err, tt.wantErr)
   204  			}
   205  			if err != nil {
   206  				t.Logf("%s: %v", tt.name, err)
   207  				if !db.SameErrorTypes(err, tt.wantErrType) {
   208  					t.Errorf("Wrong error. Got %v, expected %v", err, tt.wantErrType)
   209  				}
   210  			}
   211  		})
   212  	}
   213  }
   214  
   215  func TestBookOrder(t *testing.T) {
   216  	if err := cleanTables(archie.db); err != nil {
   217  		t.Fatalf("cleanTables: %v", err)
   218  	}
   219  
   220  	// Store order (epoch) for new order
   221  	// BookOrder for existing order
   222  
   223  	var epochIdx, epochDur int64 = 13245678, 6000
   224  
   225  	// Store standing limit order in epoch status.
   226  	lo := newLimitOrder(true, 4200000, 1, order.StandingTiF, 0)
   227  	err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusEpoch)
   228  	if err != nil {
   229  		t.Fatalf("StoreOrder failed: %v", err)
   230  	}
   231  
   232  	// Book the same limit order.
   233  	err = archie.BookOrder(lo)
   234  	if err != nil {
   235  		t.Fatalf("BookOrder failed: %v", err)
   236  	}
   237  }
   238  
   239  func TestExecuteOrder(t *testing.T) {
   240  	if err := cleanTables(archie.db); err != nil {
   241  		t.Fatalf("cleanTables: %v", err)
   242  	}
   243  
   244  	// Store order (executed) for new order
   245  	// ExecuteOrder for existing order
   246  
   247  	var epochIdx, epochDur int64 = 13245678, 6000
   248  
   249  	// Store standing limit order in executed status.
   250  	lo := newLimitOrder(true, 4200000, 1, order.StandingTiF, 0)
   251  	err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusExecuted)
   252  	if err != nil {
   253  		t.Fatalf("StoreOrder failed: %v", err)
   254  	}
   255  
   256  	// Execute the same limit order.
   257  	err = archie.ExecuteOrder(lo)
   258  	if err != nil {
   259  		t.Fatalf("BookOrder failed: %v", err)
   260  	}
   261  }
   262  
   263  func TestCancelOrder(t *testing.T) {
   264  	if err := cleanTables(archie.db); err != nil {
   265  		t.Fatalf("cleanTables: %v", err)
   266  	}
   267  
   268  	// Standing limit == OK
   269  	var epochIdx, epochDur int64 = 13245678, 6000
   270  	lo := newLimitOrder(false, 4800000, 1, order.StandingTiF, 0)
   271  	err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusBooked)
   272  	if err != nil {
   273  		t.Fatalf("BookOrder failed: %v", err)
   274  	}
   275  
   276  	// Execute the same limit order.
   277  	err = archie.CancelOrder(lo)
   278  	if err != nil {
   279  		t.Fatalf("CancelOrder failed: %v", err)
   280  	}
   281  
   282  	// Cancel an order not in the tables yet
   283  	lo2 := newLimitOrder(true, 4600000, 1, order.StandingTiF, 0)
   284  	err = archie.CancelOrder(lo2)
   285  	if !db.IsErrOrderUnknown(err) {
   286  		t.Fatalf("CancelOrder should have failed for unknown order.")
   287  	}
   288  }
   289  
   290  func TestRevokeOrder(t *testing.T) {
   291  	if err := cleanTables(archie.db); err != nil {
   292  		t.Fatalf("cleanTables: %v", err)
   293  	}
   294  
   295  	// Standing limit == OK
   296  	var epochIdx, epochDur int64 = 13245678, 6000
   297  	lo := newLimitOrder(false, 4800000, 1, order.StandingTiF, 0)
   298  	err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusBooked)
   299  	if err != nil {
   300  		t.Fatalf("StoreOrder failed: %v", err)
   301  	}
   302  
   303  	// Revoke the same limit order.
   304  	cancelID, timeStamp, err := archie.RevokeOrder(lo)
   305  	if err != nil {
   306  		t.Fatalf("RevokeOrder failed: %v", err)
   307  	}
   308  
   309  	// Check for the server-generated cancel order.
   310  	co, coStatus, err := archie.Order(cancelID, lo.BaseAsset, lo.QuoteAsset)
   311  	if err != nil {
   312  		t.Fatalf("Failed to locate cancel order: %v", err)
   313  	}
   314  	if co.ID() != cancelID {
   315  		t.Errorf("incorrect cancel ID retrieved")
   316  	}
   317  	coT, ok := co.(*order.CancelOrder)
   318  	if !ok {
   319  		t.Fatalf("not a cancel order")
   320  	}
   321  	if coT.ClientTime != timeStamp {
   322  		t.Errorf("got ClientTime %v, expected %v", coT.ClientTime, timeStamp)
   323  	}
   324  	if coT.ServerTime != timeStamp {
   325  		t.Errorf("got ServerTime %v, expected %v", coT.ServerTime, timeStamp)
   326  	}
   327  	if coStatus != order.OrderStatusRevoked {
   328  		t.Errorf("got order status %v, expected %v", coStatus, order.OrderStatusRevoked)
   329  	}
   330  	if !coT.Commit.IsZero() {
   331  		t.Errorf("generated cancel order did not have NULL/zero-value commitment")
   332  	}
   333  
   334  	// Market orders may be revoked too, while swap is in progress.
   335  	// NOTE: executed -> revoked status change may be odd.
   336  	mo := newMarketSellOrder(1, 0)
   337  	err = archie.StoreOrder(mo, epochIdx, epochDur, order.OrderStatusExecuted)
   338  	if err != nil {
   339  		t.Fatalf("StoreOrder failed: %v", err)
   340  	}
   341  
   342  	cancelID, timeStamp, err = archie.RevokeOrderUncounted(mo)
   343  	if err != nil {
   344  		t.Fatalf("RevokeOrder failed: %v", err)
   345  	}
   346  
   347  	co, coStatus, err = archie.Order(cancelID, mo.BaseAsset, mo.QuoteAsset)
   348  	if err != nil {
   349  		t.Fatalf("Failed to locate cancel order: %v", err)
   350  	}
   351  	if co.ID() != cancelID {
   352  		t.Errorf("incorrect cancel ID retrieved")
   353  	}
   354  	coT, ok = co.(*order.CancelOrder)
   355  	if !ok {
   356  		t.Fatalf("not a cancel order")
   357  	}
   358  	if coT.ClientTime != timeStamp {
   359  		t.Errorf("got ClientTime %v, expected %v", coT.ClientTime, timeStamp)
   360  	}
   361  	if coT.ServerTime != timeStamp {
   362  		t.Errorf("got ServerTime %v, expected %v", coT.ServerTime, timeStamp)
   363  	}
   364  	if coStatus != order.OrderStatusRevoked {
   365  		t.Errorf("got order status %v, expected %v", coStatus, order.OrderStatusRevoked)
   366  	}
   367  	if !coT.Commit.IsZero() {
   368  		t.Errorf("generated cancel order did not have NULL/zero-value commitment")
   369  	}
   370  
   371  	// Revoke an order not in the tables yet
   372  	lo2 := newLimitOrder(true, 4600000, 1, order.StandingTiF, 0)
   373  	_, _, err = archie.RevokeOrder(lo2)
   374  	if !db.IsErrOrderUnknown(err) {
   375  		t.Fatalf("RevokeOrder should have failed for unknown order.")
   376  	}
   377  }
   378  
   379  func TestFlushBook(t *testing.T) {
   380  	if err := cleanTables(archie.db); err != nil {
   381  		t.Fatalf("cleanTables: %v", err)
   382  	}
   383  
   384  	// Standing limit == OK as booked
   385  	var epochIdx, epochDur int64 = 13245678, 6000
   386  	lo := newLimitOrder(false, 4800000, 1, order.StandingTiF, 0)
   387  	err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusBooked)
   388  	if err != nil {
   389  		t.Fatalf("StoreOrder failed: %v", err)
   390  	}
   391  
   392  	// A not booked order.
   393  	mo := newMarketSellOrder(1, 0)
   394  	mo.AccountID = lo.AccountID
   395  	err = archie.StoreOrder(mo, epochIdx, epochDur, order.OrderStatusExecuted)
   396  	if err != nil {
   397  		t.Fatalf("StoreOrder failed: %v", err)
   398  	}
   399  
   400  	sellsRemoved, buysRemoved, err := archie.FlushBook(lo.BaseAsset, lo.QuoteAsset)
   401  	if err != nil {
   402  		t.Fatalf("FlushBook failed: %v", err)
   403  	}
   404  	if len(sellsRemoved) != 0 {
   405  		t.Fatalf("flushed %d book sell orders, expected 0", len(sellsRemoved))
   406  	}
   407  	if len(buysRemoved) != 1 {
   408  		t.Fatalf("flushed %d book buy orders, expected 1", len(buysRemoved))
   409  	}
   410  	if buysRemoved[0] != lo.ID() {
   411  		t.Errorf("flushed sell order has ID %v, expected %v", buysRemoved[0], lo.ID())
   412  	}
   413  
   414  	// Check for new status of the order.
   415  	loNow, loStatus, err := archie.Order(lo.ID(), lo.BaseAsset, lo.QuoteAsset)
   416  	if err != nil {
   417  		t.Fatalf("Failed to locate order: %v", err)
   418  	}
   419  	if loNow.ID() != lo.ID() {
   420  		t.Errorf("incorrect order ID retrieved")
   421  	}
   422  	_, ok := loNow.(*order.LimitOrder)
   423  	if !ok {
   424  		t.Fatalf("not a limit order")
   425  	}
   426  	if loStatus != order.OrderStatusRevoked {
   427  		t.Errorf("got order status %v, expected %v", loStatus, order.OrderStatusRevoked)
   428  	}
   429  
   430  	ordersOut, _, err := archie.UserOrders(context.Background(), lo.User(), lo.BaseAsset, lo.QuoteAsset)
   431  	if err != nil {
   432  		t.Fatalf("UserOrders failed: %v", err)
   433  	}
   434  
   435  	wantNumOrders := 2 // market and limit
   436  	if len(ordersOut) != wantNumOrders {
   437  		t.Fatalf("got %d user orders, expected %d", len(ordersOut), wantNumOrders)
   438  	}
   439  
   440  	cancels, err := archie.ExecutedCancelsForUser(lo.User(), cancelThreshWindow)
   441  	if err != nil {
   442  		t.Errorf("ExecutedCancelsForUser failed: %v", err)
   443  	}
   444  	// ExecutedCancelsForUser should not find the (exempt) cancels created by
   445  	// FlushBook.
   446  	if len(cancels) != 0 {
   447  		t.Fatalf("got %d cancels, expected 0", len(cancels))
   448  	}
   449  
   450  	// Query for the revoke associated cancels without the exemption filter.
   451  	cancelTableName := fullCancelOrderTableName(archie.dbName, mktInfo.Name, false)
   452  	stmt := fmt.Sprintf(internal.SelectRevokeCancels, cancelTableName)
   453  	rows, err := archie.db.QueryContext(context.Background(), stmt, lo.User(), orderStatusRevoked, cancelThreshWindow)
   454  	if err != nil {
   455  		t.Fatalf("QueryContext failed: %v", err)
   456  	}
   457  
   458  	var ords []*db.CancelRecord
   459  	for rows.Next() {
   460  		var oid, target order.OrderID
   461  		var revokeTime time.Time
   462  		var epochIdx int64
   463  		err = rows.Scan(&oid, &target, &revokeTime, &epochIdx)
   464  		if err != nil {
   465  			rows.Close()
   466  			t.Fatalf("rows Scan failed")
   467  		}
   468  
   469  		if epochIdx != exemptEpochIdx {
   470  			t.Errorf("got epoch index %d, expected %d", epochIdx, exemptEpochIdx)
   471  		}
   472  
   473  		ords = append(ords, &db.CancelRecord{
   474  			ID:        oid,
   475  			TargetID:  target,
   476  			MatchTime: revokeTime.UnixMilli(),
   477  		})
   478  	}
   479  
   480  	if err = rows.Err(); err != nil {
   481  		t.Fatalf("rows Scan failed")
   482  	}
   483  
   484  	if len(ords) != 1 {
   485  		t.Fatalf("found %d cancels, wanted 1", len(ords))
   486  	}
   487  
   488  	if ords[0].TargetID != lo.ID() {
   489  		t.Fatalf("cancel order is targeting %v, expected %v", ords[0].TargetID, lo.ID())
   490  	}
   491  
   492  	// Ensure market order is still there.
   493  	moNow, moStatus, err := archie.Order(mo.ID(), mo.BaseAsset, mo.QuoteAsset)
   494  	if err != nil {
   495  		t.Fatalf("Failed to locate order: %v", err)
   496  	}
   497  	if moNow.ID() != mo.ID() {
   498  		t.Errorf("incorrect order ID retrieved")
   499  	}
   500  	_, ok = moNow.(*order.MarketOrder)
   501  	if !ok {
   502  		t.Fatalf("not a market order")
   503  	}
   504  	if moStatus != order.OrderStatusExecuted {
   505  		t.Errorf("got order status %v, expected %v", loStatus, order.OrderStatusExecuted)
   506  	}
   507  }
   508  
   509  func TestLoadOrderUnknown(t *testing.T) {
   510  	if err := cleanTables(archie.db); err != nil {
   511  		t.Fatalf("cleanTables: %v", err)
   512  	}
   513  
   514  	orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d")
   515  	var oid order.OrderID
   516  	copy(oid[:], orderID0)
   517  
   518  	ordOut, statusOut, err := archie.Order(oid, mktInfo.Base, mktInfo.Quote)
   519  	if err == nil || ordOut != nil {
   520  		t.Errorf("Order should have failed to load non-existent order")
   521  	}
   522  	if statusOut != order.OrderStatusUnknown {
   523  		t.Errorf("status of non-existent order should be OrderStatusUnknown, got %s", statusOut)
   524  	}
   525  }
   526  
   527  func TestStoreLoadLimitOrderActive(t *testing.T) {
   528  	if err := cleanTables(archie.db); err != nil {
   529  		t.Fatalf("cleanTables: %v", err)
   530  	}
   531  
   532  	var epochIdx, epochDur int64 = 13245678, 6000
   533  
   534  	// Limit: buy, standing, booked
   535  	ordIn := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0)
   536  	statusIn := order.OrderStatusBooked
   537  
   538  	// Do not use Stringers when dumping, and stop after 4 levels deep
   539  	spew.Config.MaxDepth = 4
   540  	spew.Config.DisableMethods = true
   541  
   542  	oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset
   543  
   544  	err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
   545  	if err != nil {
   546  		t.Fatalf("StoreOrder failed: %v", err)
   547  	}
   548  
   549  	ordOut, statusOut, err := archie.Order(oid, base, quote)
   550  	if err != nil {
   551  		t.Fatalf("Order failed: %v", err)
   552  	}
   553  
   554  	if ordOut.ID() != oid {
   555  		t.Errorf("Incorrect OrderId for retrieved order. Got %v, expected %v.",
   556  			ordOut.ID(), oid)
   557  		spew.Dump(ordIn)
   558  		spew.Dump(ordOut)
   559  	}
   560  
   561  	if statusOut != statusIn {
   562  		t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.",
   563  			statusOut, statusIn)
   564  	}
   565  }
   566  
   567  func TestStoreLoadLimitOrderArchived(t *testing.T) {
   568  	if err := cleanTables(archie.db); err != nil {
   569  		t.Fatalf("cleanTables: %v", err)
   570  	}
   571  
   572  	var epochIdx, epochDur int64 = 13245678, 6000
   573  
   574  	// Limit: buy, standing, executed
   575  	ordIn := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0)
   576  	statusIn := order.OrderStatusExecuted
   577  
   578  	// Do not use Stringers when dumping, and stop after 4 levels deep
   579  	spew.Config.MaxDepth = 4
   580  	spew.Config.DisableMethods = true
   581  
   582  	oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset
   583  
   584  	err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
   585  	if err != nil {
   586  		t.Fatalf("StoreOrder failed: %v", err)
   587  	}
   588  
   589  	ordOut, statusOut, err := archie.Order(oid, base, quote)
   590  	if err != nil {
   591  		t.Fatalf("Order failed: %v", err)
   592  	}
   593  
   594  	if ordOut.ID() != oid {
   595  		t.Errorf("Incorrect OrderId for retrieved order. Got %v, expected %v.",
   596  			ordOut.ID(), oid)
   597  		spew.Dump(ordIn)
   598  		spew.Dump(ordOut)
   599  	}
   600  
   601  	if statusOut != statusIn {
   602  		t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.",
   603  			statusOut, statusIn)
   604  	}
   605  }
   606  
   607  func TestStoreLoadMarketOrderActive(t *testing.T) {
   608  	if err := cleanTables(archie.db); err != nil {
   609  		t.Fatalf("cleanTables: %v", err)
   610  	}
   611  
   612  	var epochIdx, epochDur int64 = 13245678, 6000
   613  
   614  	// Market: sell, epoch (active)
   615  	ordIn := newMarketSellOrder(1, 0)
   616  	statusIn := order.OrderStatusEpoch
   617  
   618  	// Do not use Stringers when dumping, and stop after 4 levels deep
   619  	spew.Config.MaxDepth = 4
   620  	spew.Config.DisableMethods = true
   621  
   622  	oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset
   623  
   624  	err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
   625  	if err != nil {
   626  		t.Fatalf("StoreOrder failed: %v", err)
   627  	}
   628  
   629  	ordOut, statusOut, err := archie.Order(oid, base, quote)
   630  	if err != nil {
   631  		t.Fatalf("Order failed: %v", err)
   632  	}
   633  
   634  	if ordOut.ID() != oid {
   635  		t.Errorf("Incorrect OrderId for retrieved order. Got %v, expected %v.",
   636  			ordOut.ID(), oid)
   637  		spew.Dump(ordIn)
   638  		spew.Dump(ordOut)
   639  	}
   640  
   641  	if statusOut != statusIn {
   642  		t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.",
   643  			statusOut, statusIn)
   644  	}
   645  }
   646  
   647  func TestStoreLoadCancelOrder(t *testing.T) {
   648  	if err := cleanTables(archie.db); err != nil {
   649  		t.Fatalf("cleanTables: %v", err)
   650  	}
   651  
   652  	var epochIdx, epochDur int64 = 13245678, 6000
   653  
   654  	// order ID for a cancel order
   655  	orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d")
   656  	var targetOrderID order.OrderID
   657  	copy(targetOrderID[:], orderID0)
   658  
   659  	// Cancel: epoch (active)
   660  	ordIn := newCancelOrder(targetOrderID, AssetDCR, AssetBTC, 0)
   661  	statusIn := order.OrderStatusEpoch
   662  
   663  	// Do not use Stringers when dumping, and stop after 4 levels deep
   664  	spew.Config.MaxDepth = 4
   665  	spew.Config.DisableMethods = true
   666  
   667  	oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset
   668  
   669  	err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
   670  	if err != nil {
   671  		t.Fatalf("StoreOrder failed: %v", err)
   672  	}
   673  
   674  	ordOut, statusOut, err := archie.Order(oid, base, quote)
   675  	if err != nil {
   676  		t.Fatalf("Order failed: %v", err)
   677  	}
   678  
   679  	if ordOut.ID() != oid {
   680  		t.Errorf("Incorrect OrderId for retrieved order. Got %v, expected %v.",
   681  			ordOut.ID(), oid)
   682  		spew.Dump(ordIn)
   683  		spew.Dump(ordOut)
   684  	}
   685  
   686  	if statusOut != statusIn {
   687  		t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.",
   688  			statusOut, statusIn)
   689  	}
   690  }
   691  
   692  func TestOrderStatusUnknown(t *testing.T) {
   693  	if err := cleanTables(archie.db); err != nil {
   694  		t.Fatalf("cleanTables: %v", err)
   695  	}
   696  
   697  	ord := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0) // not stored
   698  	_, _, _, err := archie.OrderStatus(ord)
   699  	if err == nil {
   700  		t.Fatalf("OrderStatus succeeded to find nonexistent order!")
   701  	}
   702  	if !db.SameErrorTypes(err, db.ArchiveError{Code: db.ErrUnknownOrder}) {
   703  		if errA, ok := err.(db.ArchiveError); ok {
   704  			t.Fatalf("Expected ArchiveError with code ErrUnknownOrder, got %d", errA.Code)
   705  		}
   706  		t.Fatalf("Expected ArchiveError with code ErrUnknownOrder, got %v", err)
   707  	}
   708  }
   709  
   710  // Test ActiveOrderCoins, BookOrders, and EpochOrders.
   711  func TestActiveOrderCoins(t *testing.T) {
   712  	if err := cleanTables(archie.db); err != nil {
   713  		t.Fatalf("cleanTables: %v", err)
   714  	}
   715  
   716  	var epochIdx, epochDur int64 = 13245678, 6000
   717  
   718  	multiCoinLO := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0)
   719  	multiCoinLO.Coins = append(multiCoinLO.Coins, order.CoinID{0x22, 0x23})
   720  
   721  	epochLO := newLimitOrder(true, 1, 1, order.StandingTiF, 0)
   722  	epochCO := newCancelOrder(multiCoinLO.ID(), AssetDCR, AssetBTC, 0)
   723  
   724  	orderStatuses := []struct {
   725  		ord         order.Order
   726  		status      order.OrderStatus
   727  		activeCoins int
   728  	}{
   729  		{
   730  			multiCoinLO,
   731  			order.OrderStatusBooked, // active, buy, booked
   732  			-1,
   733  		},
   734  		{
   735  			newLimitOrder(false, 4500000, 1, order.StandingTiF, 0),
   736  			order.OrderStatusExecuted, // archived, buy
   737  			0,
   738  		},
   739  		{
   740  			newMarketSellOrder(2, 0),
   741  			order.OrderStatusEpoch, // active, sell, epoch
   742  			1,
   743  		},
   744  		{
   745  			epochLO,
   746  			order.OrderStatusEpoch, // active, buy, epoch
   747  			1,
   748  		},
   749  		{
   750  			epochCO,
   751  			order.OrderStatusEpoch, // cancel, epoch
   752  			0,
   753  		},
   754  		{
   755  			newMarketSellOrder(1, 0),
   756  			order.OrderStatusExecuted, // archived, sell
   757  			0,
   758  		},
   759  		{
   760  			newMarketBuyOrder(2000000000, 0),
   761  			order.OrderStatusEpoch, // active, buy
   762  			-1,
   763  		},
   764  		{
   765  			newMarketBuyOrder(2100000000, 0),
   766  			order.OrderStatusExecuted, // archived, buy
   767  			0,
   768  		},
   769  	}
   770  
   771  	for i := range orderStatuses {
   772  		ordIn := orderStatuses[i].ord
   773  		statusIn := orderStatuses[i].status
   774  		err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
   775  		if err != nil {
   776  			t.Fatalf("StoreOrder failed: %v", err)
   777  		}
   778  	}
   779  
   780  	baseCoins, quoteCoins, err := archie.ActiveOrderCoins(mktInfo.Base, mktInfo.Quote)
   781  	if err != nil {
   782  		t.Fatalf("ActiveOrderCoins failed: %v", err)
   783  	}
   784  
   785  	for _, os := range orderStatuses {
   786  		var coins, wantCoins []order.CoinID
   787  		switch os.activeCoins {
   788  		case 0: // no active
   789  		case 1: // active base coins (sell order)
   790  			coins = baseCoins[os.ord.ID()]
   791  			wantCoins = os.ord.Trade().Coins
   792  		case -1: // active quote coins (buy order)
   793  			coins = quoteCoins[os.ord.ID()]
   794  			wantCoins = os.ord.Trade().Coins
   795  		}
   796  
   797  		if len(coins) != len(wantCoins) {
   798  			t.Errorf("Order %v has %d coins, expected %d", os.ord.ID(),
   799  				len(coins), len(wantCoins))
   800  			continue
   801  		}
   802  		for i := range coins {
   803  			if !bytes.Equal(coins[i], wantCoins[i]) {
   804  				t.Errorf("Order %v coin %d mismatch:\n\tgot %v\n\texpected %v",
   805  					os.ord.ID(), i, coins[i], wantCoins[i])
   806  			}
   807  		}
   808  	}
   809  
   810  	bookOrders, err := archie.BookOrders(mktInfo.Base, mktInfo.Quote)
   811  	if err != nil {
   812  		t.Fatalf("BookOrders failed: %v", err)
   813  	}
   814  
   815  	if len(bookOrders) != 1 {
   816  		t.Fatalf("got %d book orders, expected 1", len(bookOrders))
   817  	}
   818  
   819  	// Verify the order ID of the loaded order is correct. This ensures the
   820  	// order is being loaded with all the fields to provide and identical
   821  	// serialization.
   822  	if multiCoinLO.ID() != bookOrders[0].ID() {
   823  		t.Errorf("loaded book order has an incorrect order ID. Got %v, expected %v",
   824  			bookOrders[0].ID(), multiCoinLO.ID())
   825  	}
   826  
   827  	los, mos, cos, err := archie.epochOrders(mktInfo.Base, mktInfo.Quote)
   828  	if err != nil {
   829  		t.Fatalf("epochOrders failed: %v", err)
   830  	}
   831  
   832  	if len(los) != 1 || len(mos) != 2 || len(cos) != 1 {
   833  		t.Fatalf("got %d epoch limit orders, %d epoch market orders, and %d epoch cancel orders, expected 1, 2, and 1",
   834  			len(los), len(mos), len(cos))
   835  	}
   836  
   837  	// Verify the order ID of the loaded order is correct. This ensures the
   838  	// order is being loaded with all the fields to provide and identical
   839  	// serialization.
   840  	if epochLO.ID() != los[0].ID() {
   841  		t.Errorf("epoch limit order has an incorrect order ID. Got %v, expected %v",
   842  			los[0].ID(), epochLO.ID())
   843  	}
   844  	if epochCO.ID() != cos[0].ID() {
   845  		t.Errorf("epoch cancel order has an incorrect order ID. Got %v, expected %v",
   846  			cos[0].ID(), epochCO.ID())
   847  	}
   848  
   849  	// The exported version should return the same orders.
   850  	orders, err := archie.EpochOrders(mktInfo.Base, mktInfo.Quote)
   851  	if err != nil {
   852  		t.Fatalf("EpochOrders failed: %v", err)
   853  	}
   854  
   855  	if len(orders) != 4 {
   856  		t.Fatalf("got %d epoch orders, expected 4", len(orders))
   857  	}
   858  	for _, o := range orders {
   859  		if o.ID() == los[0].ID() ||
   860  			o.ID() == mos[0].ID() ||
   861  			o.ID() == mos[1].ID() ||
   862  			o.ID() == cos[0].ID() {
   863  			continue
   864  		}
   865  		t.Fatalf("order %v in EpochOrders but not epochOrders", o.ID())
   866  	}
   867  }
   868  
   869  func TestOrderStatus(t *testing.T) {
   870  	if err := cleanTables(archie.db); err != nil {
   871  		t.Fatalf("cleanTables: %v", err)
   872  	}
   873  
   874  	var epochIdx, epochDur int64 = 13245678, 6000
   875  
   876  	orderStatuses := []struct {
   877  		ord    order.Order
   878  		status order.OrderStatus
   879  	}{
   880  		{
   881  			newLimitOrder(false, 4900000, 1, order.StandingTiF, 0),
   882  			order.OrderStatusBooked, // active
   883  		},
   884  		{
   885  			newLimitOrder(false, 4500000, 1, order.StandingTiF, 0),
   886  			order.OrderStatusExecuted, // archived
   887  		},
   888  		{
   889  			newMarketSellOrder(2, 0),
   890  			order.OrderStatusEpoch, // active
   891  		},
   892  		{
   893  			newMarketSellOrder(1, 0),
   894  			order.OrderStatusExecuted, // archived
   895  		},
   896  		{
   897  			newMarketBuyOrder(2000000000, 0),
   898  			order.OrderStatusEpoch, // active
   899  		},
   900  		{
   901  			newMarketBuyOrder(2100000000, 0),
   902  			order.OrderStatusExecuted, // archived
   903  		},
   904  	}
   905  
   906  	for i := range orderStatuses {
   907  		ordIn := orderStatuses[i].ord
   908  		trade := ordIn.Trade()
   909  		statusIn := orderStatuses[i].status
   910  		err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
   911  		if err != nil {
   912  			t.Fatalf("StoreOrder failed: %v", err)
   913  		}
   914  
   915  		statusOut, typeOut, filledOut, err := archie.OrderStatus(ordIn)
   916  		if err != nil {
   917  			t.Fatalf("OrderStatus(%d:%v) failed: %v", i, ordIn, err)
   918  		}
   919  
   920  		if statusOut != statusIn {
   921  			t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.",
   922  				statusOut, statusIn)
   923  		}
   924  
   925  		if typeOut != ordIn.Type() {
   926  			t.Errorf("Incorrect OrderType for retrieved order. Got %v, expected %v.",
   927  				typeOut, ordIn.Type())
   928  		}
   929  
   930  		if filledOut != int64(trade.Filled()) {
   931  			t.Errorf("Incorrect FillAmt for retrieved order. Got %v, expected %v.",
   932  				filledOut, trade.Filled())
   933  		}
   934  	}
   935  }
   936  
   937  func TestCancelOrderStatus(t *testing.T) {
   938  	if err := cleanTables(archie.db); err != nil {
   939  		t.Fatalf("cleanTables: %v", err)
   940  	}
   941  
   942  	var epochIdx, epochDur int64 = 13245678, 6000
   943  
   944  	// order ID for a cancel order
   945  	orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d")
   946  	var targetOrderID order.OrderID
   947  	copy(targetOrderID[:], orderID0)
   948  
   949  	// Cancel: executed (archived)
   950  	ordIn := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 0)
   951  	statusIn := order.OrderStatusExecuted
   952  
   953  	//oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset
   954  
   955  	err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
   956  	if err != nil {
   957  		t.Fatalf("StoreOrder failed: %v", err)
   958  	}
   959  
   960  	statusOut, typeOut, filledOut, err := archie.OrderStatus(ordIn)
   961  	if err != nil {
   962  		t.Fatalf("Order failed: %v", err)
   963  	}
   964  
   965  	if statusOut != statusIn {
   966  		t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.",
   967  			statusOut, statusIn)
   968  	}
   969  
   970  	if typeOut != ordIn.Type() {
   971  		t.Errorf("Incorrect OrderType for retrieved order. Got %v, expected %v.",
   972  			typeOut, ordIn.Type())
   973  	}
   974  
   975  	if filledOut != -1 {
   976  		t.Errorf("Incorrect FilledAmt for retrieved order. Got %v, expected %v.",
   977  			filledOut, -1)
   978  	}
   979  }
   980  
   981  func TestUpdateOrderUnknown(t *testing.T) {
   982  	if err := cleanTables(archie.db); err != nil {
   983  		t.Fatalf("cleanTables: %v", err)
   984  	}
   985  
   986  	ord := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0) // not stored
   987  
   988  	err := archie.UpdateOrderStatus(ord, order.OrderStatusExecuted)
   989  	if err == nil {
   990  		t.Fatalf("UpdateOrder succeeded to update nonexistent order!")
   991  	}
   992  	if !db.SameErrorTypes(err, db.ArchiveError{Code: db.ErrUnknownOrder}) {
   993  		if errA, ok := err.(db.ArchiveError); ok {
   994  			t.Fatalf("Expected ArchiveError with code ErrUnknownOrder, got %d", errA.Code)
   995  		}
   996  		t.Fatalf("Expected ArchiveError with code ErrUnknownOrder, got %v", err)
   997  	}
   998  }
   999  
  1000  func TestUpdateOrder(t *testing.T) {
  1001  	if err := cleanTables(archie.db); err != nil {
  1002  		t.Fatalf("cleanTables: %v", err)
  1003  	}
  1004  
  1005  	var epochIdx, epochDur int64 = 13245678, 6000
  1006  
  1007  	// order ID for a cancel order
  1008  	orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d")
  1009  	var targetOrderID order.OrderID
  1010  	copy(targetOrderID[:], orderID0)
  1011  
  1012  	orderStatuses := []struct {
  1013  		ord       order.Order
  1014  		status    order.OrderStatus
  1015  		newStatus order.OrderStatus
  1016  		newFilled uint64
  1017  		wantErr   bool
  1018  	}{
  1019  		{
  1020  			newLimitOrder(false, 4900000, 1, order.StandingTiF, 0),
  1021  			order.OrderStatusEpoch,  // active
  1022  			order.OrderStatusBooked, // active
  1023  			0,
  1024  			false,
  1025  		},
  1026  		{
  1027  			newLimitOrder(false, 4100000, 1, order.StandingTiF, 0),
  1028  			order.OrderStatusBooked,   // active
  1029  			order.OrderStatusExecuted, // archived
  1030  			0,
  1031  			false,
  1032  		},
  1033  		{
  1034  			newLimitOrder(false, 4500000, 1, order.StandingTiF, 0),
  1035  			order.OrderStatusExecuted, // archived
  1036  			order.OrderStatusBooked,   // active, should err
  1037  			0,
  1038  			true,
  1039  		},
  1040  		{
  1041  			newMarketSellOrder(2, 0),
  1042  			order.OrderStatusEpoch,  // active
  1043  			order.OrderStatusBooked, // active, invalid for market
  1044  			0,
  1045  			false,
  1046  		},
  1047  		{
  1048  			newMarketSellOrder(1, 0),
  1049  			order.OrderStatusExecuted, // archived
  1050  			order.OrderStatusExecuted, // archived, no change
  1051  			0,
  1052  			false,
  1053  		},
  1054  		{
  1055  			newMarketBuyOrder(2000000000, 0),
  1056  			order.OrderStatusEpoch,    // active
  1057  			order.OrderStatusExecuted, // archived
  1058  			2000000000,
  1059  			false,
  1060  		},
  1061  		{
  1062  			newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1),
  1063  			order.OrderStatusEpoch,    // active
  1064  			order.OrderStatusExecuted, // archived
  1065  			0,
  1066  			false,
  1067  		},
  1068  		{
  1069  			newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 2),
  1070  			order.OrderStatusExecuted, // archived
  1071  			order.OrderStatusCanceled, // archived
  1072  			0,
  1073  			false,
  1074  		},
  1075  	}
  1076  
  1077  	for i := range orderStatuses {
  1078  		ordIn := orderStatuses[i].ord
  1079  		statusIn := orderStatuses[i].status
  1080  		err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
  1081  		if err != nil {
  1082  			t.Fatalf("StoreOrder failed: %v", err)
  1083  		}
  1084  
  1085  		switch ot := ordIn.(type) {
  1086  		case *order.LimitOrder:
  1087  			ot.FillAmt = orderStatuses[i].newFilled
  1088  		case *order.MarketOrder:
  1089  			ot.FillAmt = orderStatuses[i].newFilled
  1090  		}
  1091  
  1092  		newStatus := orderStatuses[i].newStatus
  1093  		err = archie.UpdateOrderStatus(ordIn, newStatus)
  1094  		if (err != nil) != orderStatuses[i].wantErr {
  1095  			t.Fatalf("UpdateOrderStatus(%d:%v, %s) failed: %v", i, ordIn, newStatus, err)
  1096  		}
  1097  	}
  1098  }
  1099  
  1100  func TestStorePreimage(t *testing.T) {
  1101  	if err := cleanTables(archie.db); err != nil {
  1102  		t.Fatalf("cleanTables: %v", err)
  1103  	}
  1104  
  1105  	var epochIdx, epochDur int64 = 13245678, 6000
  1106  
  1107  	orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d")
  1108  	var targetOrderID order.OrderID
  1109  	copy(targetOrderID[:], orderID0)
  1110  
  1111  	lo, pi := newLimitOrderRevealed(false, 4900000, 1, order.StandingTiF, 0)
  1112  	err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusEpoch)
  1113  	if err != nil {
  1114  		t.Fatalf("StoreOrder failed: %v", err)
  1115  	}
  1116  
  1117  	err = archie.StorePreimage(lo, pi)
  1118  	if err != nil {
  1119  		t.Fatalf("StoreOrder failed: %v", err)
  1120  	}
  1121  
  1122  	piOut, err := archie.OrderPreimage(lo)
  1123  	if err != nil {
  1124  		t.Fatalf("OrderPreimage failed: %v", err)
  1125  	}
  1126  
  1127  	if pi != piOut {
  1128  		t.Errorf("got preimage %v, expected %v", piOut, pi)
  1129  	}
  1130  
  1131  	// Now test OrderPreimage when preimage is NULL.
  1132  	lo2, _ := newLimitOrderRevealed(false, 4900000, 1, order.StandingTiF, 0)
  1133  	err = archie.StoreOrder(lo2, epochIdx, epochDur, order.OrderStatusEpoch)
  1134  	if err != nil {
  1135  		t.Fatalf("StoreOrder failed: %v", err)
  1136  	}
  1137  
  1138  	piOut2, err := archie.OrderPreimage(lo2)
  1139  	if err != nil {
  1140  		t.Fatalf("OrderPreimage failed: %v", err)
  1141  	}
  1142  	if !piOut2.IsZero() {
  1143  		t.Errorf("Preimage should have been the zero value, got %v", piOut2)
  1144  	}
  1145  }
  1146  
  1147  func TestFailCancelOrder(t *testing.T) {
  1148  	if err := cleanTables(archie.db); err != nil {
  1149  		t.Fatalf("cleanTables: %v", err)
  1150  	}
  1151  
  1152  	var epochIdx, epochDur int64 = 13245678, 6000
  1153  
  1154  	// order ID for a cancel order
  1155  	orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d")
  1156  	var targetOrderID order.OrderID
  1157  	copy(targetOrderID[:], orderID0)
  1158  
  1159  	co := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1)
  1160  	err := archie.StoreOrder(co, epochIdx, epochDur, order.OrderStatusEpoch)
  1161  	if err != nil {
  1162  		t.Fatalf("StoreOrder failed: %v", err)
  1163  	}
  1164  
  1165  	err = archie.FailCancelOrder(co)
  1166  	if err != nil {
  1167  		t.Fatalf("StoreOrder failed: %v", err)
  1168  	}
  1169  	_, status, err := loadCancelOrder(archie.db, archie.dbName, mktInfo.Name, co.ID())
  1170  	if err != nil {
  1171  		t.Errorf("loadCancelOrder failed: %v", err)
  1172  	}
  1173  
  1174  	if status != orderStatusFailed {
  1175  		t.Errorf("cancel order should have been %s, got %s", orderStatusFailed, status)
  1176  	}
  1177  }
  1178  
  1179  func TestUpdateOrderFilled(t *testing.T) {
  1180  	if err := cleanTables(archie.db); err != nil {
  1181  		t.Fatalf("cleanTables: %v", err)
  1182  	}
  1183  
  1184  	var epochIdx, epochDur int64 = 13245678, 6000
  1185  
  1186  	// order ID for a cancel order
  1187  	orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d")
  1188  	var targetOrderID order.OrderID
  1189  	copy(targetOrderID[:], orderID0)
  1190  
  1191  	orderStatuses := []struct {
  1192  		ord           *order.LimitOrder
  1193  		status        order.OrderStatus
  1194  		newFilled     uint64
  1195  		wantUpdateErr bool
  1196  	}{
  1197  		{
  1198  			newLimitOrder(false, 4900000, 1, order.StandingTiF, 0),
  1199  			order.OrderStatusBooked, // active
  1200  			0,
  1201  			false,
  1202  		},
  1203  		{
  1204  			newLimitOrder(false, 4100000, 1, order.StandingTiF, 0),
  1205  			order.OrderStatusBooked, // active
  1206  			0,
  1207  			false,
  1208  		},
  1209  		{
  1210  			newLimitOrder(false, 4500000, 1, order.StandingTiF, 0),
  1211  			order.OrderStatusExecuted, // archived
  1212  			0,
  1213  			false,
  1214  		},
  1215  	}
  1216  
  1217  	for i := range orderStatuses {
  1218  		ordIn := orderStatuses[i].ord
  1219  		statusIn := orderStatuses[i].status
  1220  		err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
  1221  		if err != nil {
  1222  			t.Fatalf("StoreOrder failed: %v", err)
  1223  		}
  1224  
  1225  		ordIn.FillAmt = orderStatuses[i].newFilled
  1226  
  1227  		err = archie.UpdateOrderFilled(ordIn)
  1228  		if (err != nil) != orderStatuses[i].wantUpdateErr {
  1229  			t.Fatalf("UpdateOrderFilled(%d:%v) failed: %v", i, ordIn, err)
  1230  		}
  1231  	}
  1232  }
  1233  
  1234  func TestUserOrders(t *testing.T) {
  1235  	if err := cleanTables(archie.db); err != nil {
  1236  		t.Fatalf("cleanTables: %v", err)
  1237  	}
  1238  
  1239  	var epochIdx, epochDur int64 = 13245678, 6000
  1240  
  1241  	limitSell := newLimitOrder(true, 4900000, 1, order.StandingTiF, 0)
  1242  	limitBuy := newLimitOrder(false, 4100000, 1, order.StandingTiF, 0)
  1243  	marketSell := newMarketSellOrder(2, 0)
  1244  	marketBuy := newMarketBuyOrder(2000000000, 0)
  1245  
  1246  	// Make all of the above orders belong to the same user.
  1247  	aid := limitSell.AccountID
  1248  	limitBuy.AccountID = aid
  1249  	limitBuy.AccountID = aid
  1250  	marketSell.AccountID = aid
  1251  	marketBuy.AccountID = aid
  1252  
  1253  	marketSellOtherGuy := newMarketSellOrder(2, 0)
  1254  	marketSellOtherGuy.Address = "1MUz4VMYui5qY1mxUiG8BQ1Luv6tqkvaiL"
  1255  
  1256  	orderStatuses := []struct {
  1257  		ord     order.Order
  1258  		status  order.OrderStatus
  1259  		ordType order.OrderType
  1260  		wantErr bool
  1261  	}{
  1262  		{
  1263  			limitSell,
  1264  			order.OrderStatusBooked, // active
  1265  			order.LimitOrderType,
  1266  			false,
  1267  		},
  1268  		{
  1269  			limitBuy,
  1270  			order.OrderStatusCanceled, // archived
  1271  			order.LimitOrderType,
  1272  			false,
  1273  		},
  1274  		{
  1275  			marketSell,
  1276  			order.OrderStatusEpoch, // active
  1277  			order.MarketOrderType,
  1278  			false,
  1279  		},
  1280  		{
  1281  			marketBuy,
  1282  			order.OrderStatusExecuted, // archived
  1283  			order.MarketOrderType,
  1284  			false,
  1285  		},
  1286  		{
  1287  			marketSellOtherGuy,
  1288  			order.OrderStatusExecuted, // archived
  1289  			order.MarketOrderType,
  1290  			false,
  1291  		},
  1292  	}
  1293  
  1294  	for i := range orderStatuses {
  1295  		ordIn := orderStatuses[i].ord
  1296  		statusIn := orderStatuses[i].status
  1297  		err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
  1298  		if err != nil {
  1299  			t.Fatalf("StoreOrder failed: %v", err)
  1300  		}
  1301  	}
  1302  
  1303  	ordersOut, statusesOut, err := archie.UserOrders(context.Background(), aid, mktInfo.Base, mktInfo.Quote)
  1304  	if err != nil {
  1305  		t.Error(err)
  1306  	}
  1307  
  1308  	if len(ordersOut) != len(statusesOut) {
  1309  		t.Errorf("UserOrders returned %d orders, but %d order status. Should be equal.",
  1310  			len(ordersOut), len(statusesOut))
  1311  	}
  1312  
  1313  	numOrdersForGuy0 := len(orderStatuses) - 1
  1314  	if len(ordersOut) != numOrdersForGuy0 {
  1315  		t.Errorf("incorrect number of orders for user %d retrieved. "+
  1316  			"got %d, expected %d", aid, len(ordersOut), numOrdersForGuy0)
  1317  	}
  1318  
  1319  	findExpected := func(ord order.Order) int {
  1320  		for i := range orderStatuses {
  1321  			if orderStatuses[i].ord.ID() == ord.ID() {
  1322  				return i
  1323  			}
  1324  		}
  1325  		return -1
  1326  	}
  1327  
  1328  	for i := range ordersOut {
  1329  		j := findExpected(ordersOut[i])
  1330  		if j == -1 {
  1331  			t.Errorf("failed to find order %v", ordersOut[i])
  1332  			continue
  1333  		}
  1334  		if ordersOut[i].Type() != orderStatuses[j].ordType {
  1335  			t.Errorf("wrong type %v, wanted %v", ordersOut[i].Type(), orderStatuses[j].ordType)
  1336  		}
  1337  		if statusesOut[i] != orderStatuses[j].status {
  1338  			t.Errorf("wrong status %v, wanted %v", statusesOut[i], orderStatuses[j].status)
  1339  		}
  1340  	}
  1341  }
  1342  func TestUserOrderStatuses(t *testing.T) {
  1343  	if err := cleanTables(archie.db); err != nil {
  1344  		t.Fatalf("cleanTables: %v", err)
  1345  	}
  1346  
  1347  	var epochIdx, epochDur int64 = 13245678, 6000
  1348  
  1349  	orderStatuses := []struct {
  1350  		ord    order.Order
  1351  		status order.OrderStatus
  1352  	}{
  1353  		{
  1354  			newLimitOrder(false, 4900000, 1, order.StandingTiF, 0),
  1355  			order.OrderStatusBooked, // active
  1356  		},
  1357  		{
  1358  			newLimitOrder(false, 4500000, 1, order.StandingTiF, 0),
  1359  			order.OrderStatusExecuted, // archived
  1360  		},
  1361  		{
  1362  			newMarketSellOrder(2, 0),
  1363  			order.OrderStatusEpoch, // active
  1364  		},
  1365  		{
  1366  			newMarketSellOrder(1, 0),
  1367  			order.OrderStatusExecuted, // archived
  1368  		},
  1369  		{
  1370  			newMarketBuyOrder(2000000000, 0),
  1371  			order.OrderStatusEpoch, // active
  1372  		},
  1373  		{
  1374  			newMarketBuyOrder(2100000000, 0),
  1375  			order.OrderStatusExecuted, // archived
  1376  		},
  1377  	}
  1378  
  1379  	unsavedOrder1 := newMarketBuyOrder(3000000000, 0)
  1380  	unsavedOrder2 := newMarketSellOrder(4, 0)
  1381  
  1382  	orders := make([]order.Order, 0, len(orderStatuses)+2)
  1383  	orderIDs := make([]order.OrderID, 0, len(orderStatuses)+2)
  1384  
  1385  	// Add unsaved orders
  1386  	orders = append(orders, unsavedOrder1, unsavedOrder2)
  1387  	orderIDs = append(orderIDs, unsavedOrder1.ID(), unsavedOrder2.ID())
  1388  
  1389  	accountID := randomAccountID()
  1390  	for i := range orderStatuses {
  1391  		ordIn := orderStatuses[i].ord
  1392  		statusIn := orderStatuses[i].status
  1393  		if lo, ok := ordIn.(*order.LimitOrder); ok {
  1394  			lo.BaseAsset, lo.QuoteAsset = AssetBTC, AssetLTC // swap the assets to test across different mkts
  1395  		}
  1396  		ordIn.Prefix().AccountID = accountID
  1397  		err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn)
  1398  		if err != nil {
  1399  			t.Fatalf("StoreOrder failed: %v", err)
  1400  		}
  1401  		orders = append(orders, ordIn)
  1402  		orderIDs = append(orderIDs, ordIn.ID())
  1403  	}
  1404  
  1405  	// All orders except the 2 limit orders are DCR-BTC.
  1406  	orderStatusesOut, err := archie.UserOrderStatuses(accountID, AssetDCR, AssetBTC, orderIDs)
  1407  	if err != nil {
  1408  		t.Fatalf("OrderStatuses failed: %v", err)
  1409  	}
  1410  	if len(orderStatusesOut) != len(orderStatuses)-2 /*the 2 limits*/ {
  1411  		t.Fatalf("OrderStatuses returned %d orders instead of %d", len(orderStatusesOut), len(orderStatuses)-2)
  1412  	}
  1413  	outMap := make(map[order.OrderID]*db.OrderStatus, len(orderStatusesOut))
  1414  	for _, orderStatus := range orderStatusesOut {
  1415  		outMap[orderStatus.ID] = orderStatus
  1416  	}
  1417  	for i := range orderStatuses {
  1418  		ordIn := orderStatuses[i].ord
  1419  		orderOut, found := outMap[ordIn.ID()]
  1420  		if !found {
  1421  			continue
  1422  		}
  1423  		statusIn := orderStatuses[i].status
  1424  		if orderOut.Status != statusIn {
  1425  			t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.",
  1426  				orderOut.Status, statusIn)
  1427  		}
  1428  	}
  1429  
  1430  	// Check statuses for the 2 limit orders that are BTC-LTC.
  1431  	orderStatusesOut, err = archie.UserOrderStatuses(accountID, AssetBTC, AssetLTC, orderIDs)
  1432  	if err != nil {
  1433  		t.Fatalf("OrderStatuses failed: %v", err)
  1434  	}
  1435  	if len(orderStatusesOut) != 2 /*the 2 limits*/ {
  1436  		t.Fatalf("OrderStatuses returned %d orders instead of %d", len(orderStatusesOut), 2)
  1437  	}
  1438  	outMap = make(map[order.OrderID]*db.OrderStatus, len(orderStatusesOut))
  1439  	for _, orderStatus := range orderStatusesOut {
  1440  		outMap[orderStatus.ID] = orderStatus
  1441  	}
  1442  	for i := range orderStatuses {
  1443  		ordIn := orderStatuses[i].ord
  1444  		orderOut, found := outMap[ordIn.ID()]
  1445  		if !found {
  1446  			continue
  1447  		}
  1448  		statusIn := orderStatuses[i].status
  1449  		if orderOut.Status != statusIn {
  1450  			t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.",
  1451  				orderOut.Status, statusIn)
  1452  		}
  1453  	}
  1454  
  1455  	// Expect nothing for wrong user ID.
  1456  	orderStatusesOut, err = archie.UserOrderStatuses(randomAccountID(), AssetDCR, AssetBTC, orderIDs)
  1457  	if err != nil {
  1458  		t.Fatalf("OrderStatuses failed: %v", err)
  1459  	}
  1460  	if len(orderStatusesOut) != 0 {
  1461  		t.Fatalf("OrderStatuses returned %d orders for wrong account ID", len(orderStatusesOut))
  1462  	}
  1463  }
  1464  func TestActiveUserOrderStatuses(t *testing.T) {
  1465  	if err := cleanTables(archie.db); err != nil {
  1466  		t.Fatalf("cleanTables: %v", err)
  1467  	}
  1468  
  1469  	// Two orders, different accounts, DCR-BTC.
  1470  	maker := newLimitOrder(false, 4500000, 1, order.StandingTiF, 0)
  1471  	taker := newLimitOrder(true, 4490000, 1, order.StandingTiF, 10)
  1472  
  1473  	var epochIdx, epochDur int64 = 13245678, 6000
  1474  	err := archie.StoreOrder(maker, epochIdx, epochDur, order.OrderStatusBooked)
  1475  	if err != nil {
  1476  		t.Fatalf("StoreOrder failed: %v", err)
  1477  	}
  1478  	err = archie.StoreOrder(taker, epochIdx, epochDur, order.OrderStatusBooked)
  1479  	if err != nil {
  1480  		t.Fatalf("StoreOrder failed: %v", err)
  1481  	}
  1482  
  1483  	// Second order from the same maker account.
  1484  	maker2 := newLimitOrder(false, 4500000, 1, order.ImmediateTiF, 20)
  1485  	maker2.AccountID = maker.AccountID
  1486  
  1487  	// Store it.
  1488  	err = archie.StoreOrder(maker2, epochIdx, epochDur, order.OrderStatusEpoch)
  1489  	if err != nil {
  1490  		t.Fatalf("StoreOrder failed: %v", err)
  1491  	}
  1492  
  1493  	// Same taker account, different market (BTC-LTC).
  1494  	taker2 := newLimitOrder(true, 4490000, 1, order.ImmediateTiF, 30)
  1495  	taker2.BaseAsset = AssetBTC
  1496  	taker2.QuoteAsset = AssetLTC
  1497  	taker2.AccountID = taker.AccountID
  1498  
  1499  	// Store it.
  1500  	err = archie.StoreOrder(taker2, epochIdx, epochDur, order.OrderStatusEpoch)
  1501  	if err != nil {
  1502  		t.Fatalf("StoreOrder failed: %v", err)
  1503  	}
  1504  
  1505  	// Store cancel order for taker account.
  1506  	taker2Incomplete := newLimitOrder(true, 4390000, 1, order.StandingTiF, 20)
  1507  	taker2Incomplete.AccountID = taker.AccountID
  1508  	err = archie.StoreOrder(taker2Incomplete, epochIdx, epochDur, order.OrderStatusCanceled)
  1509  	if err != nil {
  1510  		t.Fatalf("StoreOrder failed: %v", err)
  1511  	}
  1512  
  1513  	// Maker should have 2 active orders in 1 market.
  1514  	// Taker should have 2 active orders in 2 markets and 1 inactive (canceled) order.
  1515  
  1516  	tests := []struct {
  1517  		name              string
  1518  		acctID            account.AccountID
  1519  		numExpected       int
  1520  		wantOrderIDs      []order.OrderID
  1521  		wantOrderStatuses []order.OrderStatus
  1522  		wantedErr         error
  1523  	}{
  1524  		{
  1525  			"ok maker",
  1526  			maker.User(),
  1527  			2,
  1528  			[]order.OrderID{maker.ID(), maker2.ID()},
  1529  			[]order.OrderStatus{order.OrderStatusBooked, order.OrderStatusEpoch},
  1530  			nil,
  1531  		},
  1532  		{
  1533  			"ok taker",
  1534  			taker.User(),
  1535  			2,
  1536  			[]order.OrderID{taker.ID(), taker2.ID()},
  1537  			[]order.OrderStatus{order.OrderStatusBooked, order.OrderStatusEpoch},
  1538  			nil,
  1539  		},
  1540  		{
  1541  			"nope",
  1542  			randomAccountID(),
  1543  			0,
  1544  			nil,
  1545  			nil,
  1546  			nil,
  1547  		},
  1548  	}
  1549  
  1550  	idInSlice := func(oid order.OrderID, oids []order.OrderID) int {
  1551  		for i := range oids {
  1552  			if oids[i] == oid {
  1553  				return i
  1554  			}
  1555  		}
  1556  		return -1
  1557  	}
  1558  
  1559  	for _, tt := range tests {
  1560  		t.Run(tt.name, func(t *testing.T) {
  1561  			orderStatuses, err := archie.ActiveUserOrderStatuses(tt.acctID)
  1562  			if err != tt.wantedErr {
  1563  				t.Fatal(err)
  1564  			}
  1565  			if len(orderStatuses) != tt.numExpected {
  1566  				t.Errorf("Retrieved %d active orders for user %v, expected %d.", len(orderStatuses), tt.acctID, tt.numExpected)
  1567  			}
  1568  			for _, ord := range orderStatuses {
  1569  				wantId := idInSlice(ord.ID, tt.wantOrderIDs)
  1570  				if wantId == -1 {
  1571  					t.Errorf("Unexpected order ID %v retrieved.", ord.ID)
  1572  					continue
  1573  				}
  1574  				if ord.Status != tt.wantOrderStatuses[wantId] {
  1575  					t.Errorf("Incorrect order status for order %v. Got %d, want %d.",
  1576  						ord.ID, ord.Status, tt.wantOrderStatuses[wantId])
  1577  				}
  1578  			}
  1579  		})
  1580  	}
  1581  }
  1582  
  1583  func TestCompletedUserOrders(t *testing.T) {
  1584  	if err := cleanTables(archie.db); err != nil {
  1585  		t.Fatalf("cleanTables: %v", err)
  1586  	}
  1587  
  1588  	nowMs := func() int64 {
  1589  		return time.Now().UnixMilli()
  1590  	}
  1591  
  1592  	// Two orders, different accounts, DCR-BTC.
  1593  	maker := newLimitOrder(false, 4500000, 1, order.StandingTiF, 0)
  1594  	taker := newLimitOrder(true, 4490000, 1, order.ImmediateTiF, 10)
  1595  
  1596  	var epochIdx, epochDur int64 = 13245678, 6000
  1597  	err := archie.StoreOrder(maker, epochIdx, epochDur, order.OrderStatusExecuted)
  1598  	if err != nil {
  1599  		t.Fatalf("StoreOrder failed: %v", err)
  1600  	}
  1601  	err = archie.StoreOrder(taker, epochIdx, epochDur, order.OrderStatusExecuted)
  1602  	if err != nil {
  1603  		t.Fatalf("StoreOrder failed: %v", err)
  1604  	}
  1605  
  1606  	// Set the orders' swap completion times.
  1607  	tSwapDoneMaker := nowMs()
  1608  	if err = archie.SetOrderCompleteTime(maker, tSwapDoneMaker); err != nil {
  1609  		t.Fatalf("SetOrderCompleteTime failed: %v", err)
  1610  	}
  1611  
  1612  	tSwapDoneTaker := tSwapDoneMaker + 10
  1613  	if err = archie.SetOrderCompleteTime(taker, tSwapDoneTaker); err != nil {
  1614  		t.Fatalf("SetOrderCompleteTime failed: %v", err)
  1615  	}
  1616  
  1617  	// Second order from the same maker account.
  1618  	maker2 := newLimitOrder(false, 4500000, 1, order.StandingTiF, 20)
  1619  	maker2.AccountID = maker.AccountID
  1620  
  1621  	// Store it.
  1622  	err = archie.StoreOrder(maker2, epochIdx, epochDur, order.OrderStatusExecuted)
  1623  	if err != nil {
  1624  		t.Fatalf("StoreOrder failed: %v", err)
  1625  	}
  1626  	// Set swap complete time.
  1627  	tSwapDoneMaker2 := nowMs()
  1628  	if err = archie.SetOrderCompleteTime(maker2, tSwapDoneMaker2); err != nil {
  1629  		t.Fatalf("SetOrderCompleteTime failed: %v", err)
  1630  	}
  1631  
  1632  	// Same taker account, different market (BTC-LTC).
  1633  	taker2 := newLimitOrder(true, 4490000, 1, order.ImmediateTiF, 30)
  1634  	taker2.BaseAsset = AssetBTC
  1635  	taker2.QuoteAsset = AssetLTC
  1636  	taker2.AccountID = taker.AccountID
  1637  
  1638  	// Store it.
  1639  	err = archie.StoreOrder(taker2, epochIdx, epochDur, order.OrderStatusExecuted)
  1640  	if err != nil {
  1641  		t.Fatalf("StoreOrder failed: %v", err)
  1642  	}
  1643  
  1644  	// Set swap complete time.
  1645  	tSwapDoneTaker2 := nowMs()
  1646  	if err = archie.SetOrderCompleteTime(taker2, tSwapDoneTaker2); err != nil {
  1647  		t.Fatalf("SetOrderCompleteTime failed: %v", err)
  1648  	}
  1649  
  1650  	// Order without completion time set.
  1651  	taker2Incomplete := newLimitOrder(true, 4390000, 1, order.StandingTiF, 20)
  1652  	taker2Incomplete.AccountID = taker.AccountID
  1653  	err = archie.StoreOrder(taker2Incomplete, epochIdx, epochDur, order.OrderStatusCanceled) // archived, but not complete
  1654  	if err != nil {
  1655  		t.Fatalf("StoreOrder failed: %v", err)
  1656  	}
  1657  	// NO SetOrderCompleteTime, BUT in an orders_archived table.
  1658  
  1659  	// Try and fail to set completion time for an order not in executed status.
  1660  	taker3 := newLimitOrder(true, 4390000, 1, order.StandingTiF, 20)
  1661  	err = archie.StoreOrder(taker3, epochIdx, epochDur, order.OrderStatusBooked)
  1662  	if err != nil {
  1663  		t.Fatalf("StoreOrder failed: %v", err)
  1664  	}
  1665  
  1666  	// Set swap complete time.
  1667  	tSwapDoneTaker3 := nowMs()
  1668  	if err = archie.SetOrderCompleteTime(taker3, tSwapDoneTaker3); !db.IsErrOrderNotExecuted(err) {
  1669  		t.Fatalf("SetOrderCompleteTime should have returned a ErrOrderNotExecuted error for booked (not executed) order")
  1670  	}
  1671  
  1672  	// Maker should have 2 completed orders in 1 market.
  1673  	// Taker should have 2 completed orders in 2 markets.
  1674  
  1675  	tests := []struct {
  1676  		name          string
  1677  		acctID        account.AccountID
  1678  		numExpected   int
  1679  		wantOrderIDs  []order.OrderID
  1680  		wantCompTimes []int64
  1681  		wantedErr     error
  1682  	}{
  1683  		{
  1684  			"ok maker",
  1685  			maker.User(),
  1686  			2,
  1687  			[]order.OrderID{maker.ID(), maker2.ID()},
  1688  			[]int64{tSwapDoneMaker, tSwapDoneMaker2},
  1689  			nil,
  1690  		},
  1691  		{
  1692  			"ok taker",
  1693  			taker.User(),
  1694  			2,
  1695  			[]order.OrderID{taker.ID(), taker2.ID()},
  1696  			[]int64{tSwapDoneTaker, tSwapDoneTaker2},
  1697  			nil,
  1698  		},
  1699  		{
  1700  			"nope",
  1701  			randomAccountID(),
  1702  			0,
  1703  			nil,
  1704  			nil,
  1705  			nil,
  1706  		},
  1707  	}
  1708  
  1709  	idInSlice := func(mid order.OrderID, mids []order.OrderID) int {
  1710  		for i := range mids {
  1711  			if mids[i] == mid {
  1712  				return i
  1713  			}
  1714  		}
  1715  		return -1
  1716  	}
  1717  
  1718  	for _, tt := range tests {
  1719  		t.Run(tt.name, func(t *testing.T) {
  1720  			oids, compTimes, err := archie.CompletedUserOrders(tt.acctID, cancelThreshWindow)
  1721  			if err != tt.wantedErr {
  1722  				t.Fatal(err)
  1723  			}
  1724  			if len(oids) != tt.numExpected {
  1725  				t.Errorf("Retrieved %d completed orders for user %v, expected %d.", len(oids), tt.acctID, tt.numExpected)
  1726  			}
  1727  			for i := range oids {
  1728  				loc := idInSlice(oids[i], tt.wantOrderIDs)
  1729  				if loc == -1 {
  1730  					t.Errorf("Unexpected order ID %v retrieved.", oids[i])
  1731  					continue
  1732  				}
  1733  				if compTimes[i] != tt.wantCompTimes[loc] {
  1734  					t.Errorf("Incorrect order completion time. Got %d, want %d.",
  1735  						compTimes[loc], tt.wantCompTimes[i])
  1736  				}
  1737  			}
  1738  		})
  1739  	}
  1740  }
  1741  
  1742  func TestExecutedCancelsForUser(t *testing.T) {
  1743  	if err := cleanTables(archie.db); err != nil {
  1744  		t.Fatalf("cleanTables: %v", err)
  1745  	}
  1746  
  1747  	var epochIdx, epochDur int64 = 13245678, 6000
  1748  
  1749  	// order ID for a cancel order
  1750  	orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d")
  1751  	var targetOrderID order.OrderID
  1752  	copy(targetOrderID[:], orderID0)
  1753  
  1754  	co := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1)
  1755  	err := archie.StoreOrder(co, epochIdx, epochDur, order.OrderStatusEpoch)
  1756  	if err != nil {
  1757  		t.Fatalf("StoreOrder failed: %v", err)
  1758  	}
  1759  
  1760  	// Mark the cancel order executed.
  1761  	err = archie.ExecuteOrder(co)
  1762  	if err != nil {
  1763  		t.Fatalf("ExecuteOrder failed: %v", err)
  1764  	}
  1765  	_, status, err := loadCancelOrder(archie.db, archie.dbName, mktInfo.Name, co.ID())
  1766  	if err != nil {
  1767  		t.Errorf("loadCancelOrder failed: %v", err)
  1768  	}
  1769  	if status != orderStatusExecuted {
  1770  		t.Fatalf("cancel order should have been %s, got %s", orderStatusFailed, status)
  1771  	}
  1772  
  1773  	// order ID for a revoked order
  1774  	lo := newLimitOrder(true, 4900000, 1, order.StandingTiF, 0)
  1775  	lo.AccountID = co.AccountID // same user
  1776  	lo.BaseAsset, lo.QuoteAsset = mktInfo.Base, mktInfo.Quote
  1777  	err = archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusBooked)
  1778  	if err != nil {
  1779  		t.Fatalf("StoreOrder failed: %v", err)
  1780  	}
  1781  
  1782  	// Revoke the order.
  1783  	time.Sleep(time.Millisecond * 10) // ensure the resulting cancel order is newer than the other cancel order above.
  1784  	coID, coTime, err := archie.RevokeOrder(lo)
  1785  	if err != nil {
  1786  		t.Fatalf("StoreOrder failed: %v", err)
  1787  	}
  1788  	coOut, coStatusOut, err := loadCancelOrder(archie.db, archie.dbName, mktInfo.Name, coID)
  1789  	// loadCancelOrder does not set base and quote
  1790  	coOut.BaseAsset, coOut.QuoteAsset = mktInfo.Base, mktInfo.Quote
  1791  	if err != nil {
  1792  		t.Errorf("loadCancelOrder failed: %v", err)
  1793  	}
  1794  	if coStatusOut != orderStatusRevoked {
  1795  		t.Fatalf("cancel order should have been %s, got %s", orderStatusRevoked, status)
  1796  	}
  1797  	if coOut.ID() != coID {
  1798  		t.Errorf("incorrect cancel order ID. got %v, expected %v", coOut.ID(), coID)
  1799  	}
  1800  	if coTimeMs := coTime.UnixMilli(); coOut.Time() != coTimeMs {
  1801  		t.Errorf("incorrect cancel time. got %d, expected %d", coOut.Time(), coTimeMs)
  1802  	}
  1803  
  1804  	// Store the epoch.
  1805  	matchTime := time.Now().UnixMilli()
  1806  	err = archie.InsertEpoch(&db.EpochResults{
  1807  		MktBase:        mktInfo.Base,
  1808  		MktQuote:       mktInfo.Quote,
  1809  		Idx:            epochIdx,
  1810  		Dur:            epochDur,
  1811  		MatchTime:      matchTime,
  1812  		OrdersRevealed: []order.OrderID{co.ID()}, // not needed, but would be the case if it were executed in this epoch
  1813  	})
  1814  	if err != nil {
  1815  		t.Errorf("InsertEpoch failed: %v", err)
  1816  	}
  1817  
  1818  	// A revoked order (exempt cancel), which should NOT be found with
  1819  	// ExecutedCancelsForUser.
  1820  	lo2 := newLimitOrder(true, 4900000, 1, order.StandingTiF, 1)
  1821  	lo2.AccountID = co.AccountID // same user
  1822  	lo2.BaseAsset, lo2.QuoteAsset = mktInfo.Base, mktInfo.Quote
  1823  	err = archie.StoreOrder(lo2, epochIdx, epochDur, order.OrderStatusBooked)
  1824  	if err != nil {
  1825  		t.Fatalf("StoreOrder failed: %v", err)
  1826  	}
  1827  
  1828  	// Revoke the order.
  1829  	time.Sleep(time.Millisecond * 10)
  1830  	_, _, err = archie.RevokeOrderUncounted(lo2)
  1831  	if err != nil {
  1832  		t.Fatalf("StoreOrder failed: %v", err)
  1833  	}
  1834  
  1835  	user := co.User()
  1836  	cancels, err := archie.ExecutedCancelsForUser(user, cancelThreshWindow)
  1837  	if err != nil {
  1838  		t.Errorf("ExecutedCancelsForUser failed: %v", err)
  1839  	}
  1840  	if len(cancels) != 2 {
  1841  		t.Fatalf("found %d orders, expected 1", len(cancels))
  1842  	}
  1843  	if cancels[0].ID != co.ID() {
  1844  		t.Errorf("incorrect executed cancel %v, expected %v", cancels[0].ID, co.ID())
  1845  	}
  1846  	if cancels[0].TargetID != targetOrderID {
  1847  		t.Errorf("incorrect target for executed cancel %v, expected %v", cancels[0].TargetID, targetOrderID)
  1848  	}
  1849  	if cancels[0].MatchTime != matchTime {
  1850  		t.Errorf("incorrect exec time for executed cancel %v, expected %v", cancels[0].MatchTime, matchTime)
  1851  	}
  1852  	if cancels[1].ID != coID {
  1853  		t.Errorf("incorrect executed cancel %v, expected %v", cancels[1].ID, coID)
  1854  	}
  1855  	if cancels[1].TargetID != lo.ID() {
  1856  		t.Errorf("incorrect target for executed cancel %v, expected %v", cancels[1].TargetID, lo.ID())
  1857  	}
  1858  	if coTimeMs := coTime.UnixMilli(); cancels[1].MatchTime != coTimeMs {
  1859  		t.Errorf("incorrect exec time for executed cancel %v, expected %v", cancels[1].MatchTime, coTimeMs)
  1860  	}
  1861  
  1862  	// test the limit
  1863  	cancels, err = archie.ExecutedCancelsForUser(user, 0)
  1864  	if err != nil {
  1865  		t.Errorf("ExecutedCancelsForUser failed: %v", err)
  1866  	}
  1867  	if len(cancels) > 0 {
  1868  		t.Errorf("found executed orders for user")
  1869  	}
  1870  
  1871  	// Cancel order in epoch status, and with no epochs table entry.
  1872  	co2 := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1)
  1873  	co2.AccountID = randomAccountID() // different user
  1874  	epochIdx++
  1875  	err = archie.StoreOrder(co2, epochIdx, epochDur, order.OrderStatusEpoch)
  1876  	if err != nil {
  1877  		t.Fatalf("StoreOrder failed: %v", err)
  1878  	}
  1879  	err = archie.FailCancelOrder(co2)
  1880  	if err != nil {
  1881  		t.Fatalf("FailCancelOrder failed: %v", err)
  1882  	}
  1883  
  1884  	user2 := co2.User()
  1885  	cancels, err = archie.ExecutedCancelsForUser(user2, cancelThreshWindow)
  1886  	if err != nil {
  1887  		t.Errorf("ExecutedCancelsForUser failed: %v", err)
  1888  	}
  1889  	if len(cancels) > 0 {
  1890  		t.Errorf("found executed orders for user")
  1891  	}
  1892  
  1893  	// Cancel order in failed status, with an epochs table entry.
  1894  	co3 := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1)
  1895  	co3.AccountID = randomAccountID() // different user
  1896  	epochIdx++
  1897  	err = archie.StoreOrder(co3, epochIdx, epochDur, order.OrderStatusEpoch)
  1898  	if err != nil {
  1899  		t.Fatalf("StoreOrder failed: %v", err)
  1900  	}
  1901  	err = archie.FailCancelOrder(co3)
  1902  	if err != nil {
  1903  		t.Fatalf("ExecuteOrder failed: %v", err)
  1904  	}
  1905  
  1906  	// Store the epoch.
  1907  	matchTime3 := time.Now().UnixMilli()
  1908  	err = archie.InsertEpoch(&db.EpochResults{
  1909  		MktBase:        mktInfo.Base,
  1910  		MktQuote:       mktInfo.Quote,
  1911  		Idx:            epochIdx,
  1912  		Dur:            epochDur,
  1913  		MatchTime:      matchTime3,
  1914  		OrdersRevealed: []order.OrderID{co3.ID()}, // not needed, but would be the case if it were executed in this epoch
  1915  	})
  1916  	if err != nil {
  1917  		t.Errorf("InsertEpoch failed: %v", err)
  1918  	}
  1919  
  1920  	user3 := co3.User()
  1921  	cancels, err = archie.ExecutedCancelsForUser(user3, cancelThreshWindow)
  1922  	if err != nil {
  1923  		t.Errorf("ExecutedCancelsForUser failed: %v", err)
  1924  	}
  1925  	if len(cancels) > 0 {
  1926  		t.Errorf("found executed orders for user")
  1927  	}
  1928  
  1929  	// Cancel order in executed status, but with no epochs table entry.
  1930  	co4 := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1)
  1931  	co4.AccountID = randomAccountID() // different user
  1932  	epochIdx++
  1933  	err = archie.StoreOrder(co4, epochIdx, epochDur, order.OrderStatusEpoch)
  1934  	if err != nil {
  1935  		t.Fatalf("StoreOrder failed: %v", err)
  1936  	}
  1937  	err = archie.ExecuteOrder(co4)
  1938  	if err != nil {
  1939  		t.Fatalf("ExecuteOrder failed: %v", err)
  1940  	}
  1941  
  1942  	user4 := co4.User()
  1943  	cancels, err = archie.ExecutedCancelsForUser(user4, cancelThreshWindow)
  1944  	if err != nil {
  1945  		t.Errorf("ExecutedCancelsForUser failed: %v", err)
  1946  	}
  1947  	if len(cancels) > 0 {
  1948  		t.Errorf("found executed orders for user")
  1949  	}
  1950  }