decred.org/dcrdex@v1.0.5/server/coinlock/coinlocker_test.go (about) 1 package coinlock 2 3 import ( 4 crand "crypto/rand" 5 "math/rand" 6 "testing" 7 8 "decred.org/dcrdex/dex/order" 9 "decred.org/dcrdex/dex/order/test" 10 ) 11 12 func randomBytes(len int) []byte { 13 bytes := make([]byte, len) 14 crand.Read(bytes) 15 return bytes 16 } 17 18 func randCoinID() CoinID { 19 return CoinID(randomBytes(72)) 20 } 21 22 func randomOrderID() order.OrderID { 23 pk := randomBytes(order.OrderIDSize) 24 var id order.OrderID 25 copy(id[:], pk) 26 return id 27 } 28 29 // utxo implements order.Outpoint 30 type utxo struct { 31 txHash []byte 32 vout uint32 33 } 34 35 func (u *utxo) TxHash() []byte { return u.txHash } 36 func (u *utxo) Vout() uint32 { return u.vout } 37 38 func randcomCoinID() order.CoinID { 39 return randomBytes(36) 40 } 41 42 func verifyLocked(cl CoinLockChecker, coins []CoinID, wantLocked bool, t *testing.T) bool { 43 for _, coin := range coins { 44 locked := cl.CoinLocked(coin) 45 if locked != wantLocked { 46 t.Errorf("Coin %v locked=%v, wanted=%v.", coin, locked, wantLocked) 47 return false 48 } 49 } 50 return true 51 } 52 53 func Test_swapLocker_LockOrderCoins(t *testing.T) { 54 w := &test.Writer{ 55 Addr: "asdf", 56 Acct: test.NextAccount(), 57 Sell: true, 58 Market: &test.Market{ 59 Base: 2, 60 Quote: 0, 61 LotSize: 100, 62 }, 63 } 64 65 lo0, _ := test.WriteLimitOrder(w, 1000, 1, order.StandingTiF, 0) 66 lo0.Coins = []order.CoinID{randcomCoinID(), randcomCoinID()} 67 lo1, _ := test.WriteLimitOrder(w, 1000, 2, order.StandingTiF, 0) 68 lo1.Coins = []order.CoinID{randcomCoinID(), randcomCoinID(), randcomCoinID()} 69 70 orders := []order.Order{lo0, lo1} 71 oid0, oid1 := lo0.ID(), lo1.ID() 72 73 masterLock := NewMasterCoinLocker() 74 swapLock := masterLock.Swap() 75 bookLock := masterLock.Book() 76 77 emptyCoins := masterLock.OrderCoinsLocked(oid0) 78 if len(emptyCoins) != 0 { 79 t.Fatalf("found coins that were not yet locked") 80 } 81 82 swapLock.LockOrdersCoins(orders) 83 84 lo0Coins := masterLock.OrderCoinsLocked(oid0) 85 if len(lo0Coins) != len(lo0.Coins) { 86 t.Fatalf("Expected %d coins for order %v, got %d", len(lo0.Coins), lo0, len(lo0Coins)) 87 } 88 89 lo1Coins := masterLock.OrderCoinsLocked(oid1) 90 if len(lo1Coins) != len(lo1.Coins) { 91 t.Fatalf("Expected %d coins for order %v, got %d", len(lo1.Coins), lo0, len(lo1Coins)) 92 } 93 94 for _, coin := range lo1Coins { 95 if !masterLock.CoinLocked(coin) { 96 t.Errorf("masterLock said coin %v wasn't locked", coin) 97 } 98 if !swapLock.CoinLocked(coin) { 99 t.Errorf("swapLock said coin %v wasn't locked", coin) 100 } 101 if !bookLock.CoinLocked(coin) { 102 t.Errorf("bookLocker said coin %v wasn't locked", coin) 103 } 104 } 105 106 // Try and fail to relock coins. 107 failed := swapLock.LockOrdersCoins(orders) 108 if len(failed) != len(orders) { 109 t.Fatalf("should have failed to lock %d coins, got %d failed", len(orders), len(failed)) 110 } 111 112 // Now lock some in the book lock. 113 bookLock.LockOrdersCoins([]order.Order{lo0}) 114 // unlock them in swap lock 115 swapLock.UnlockOrderCoins(oid0) 116 // verify they are still locked 117 for _, coin := range lo0Coins { 118 if !masterLock.CoinLocked(coin) { 119 t.Errorf("masterLock said coin %v wasn't locked", coin) 120 } 121 if !swapLock.CoinLocked(coin) { 122 t.Errorf("swapLock said coin %v wasn't locked", coin) 123 } 124 if !bookLock.CoinLocked(coin) { 125 t.Errorf("bookLocker said coin %v wasn't locked", coin) 126 } 127 } 128 // now unlock them in book lock too 129 bookLock.UnlockOrderCoins(oid0) 130 // verify they are now unlocked 131 for _, coin := range lo0Coins { 132 if masterLock.CoinLocked(coin) { 133 t.Errorf("masterLock said coin %v was locked", coin) 134 } 135 if swapLock.CoinLocked(coin) { 136 t.Errorf("swapLock said coin %v was locked", coin) 137 } 138 if bookLock.CoinLocked(coin) { 139 t.Errorf("bookLocker said coin %v was locked", coin) 140 } 141 } 142 } 143 144 func Test_bookLocker_LockCoins(t *testing.T) { 145 masterLock := NewMasterCoinLocker() 146 bookLock := masterLock.Book() 147 148 coinMap := make(map[order.OrderID][]CoinID) 149 var allCoins []CoinID 150 numOrders := 99 151 allOrderIDs := make([]order.OrderID, numOrders) 152 for i := 0; i < numOrders; i++ { 153 coins := make([]CoinID, rand.Int63n(8)+1) 154 for j := range coins { 155 coins[j] = randCoinID() 156 } 157 oid := randomOrderID() 158 coinMap[oid] = coins 159 allCoins = append(allCoins, coins...) 160 allOrderIDs[i] = oid 161 } 162 163 bookLock.LockCoins(coinMap) 164 165 verifyLocked := func(cl CoinLockChecker, coins []CoinID, wantLocked bool) (ok bool) { 166 for _, coin := range coins { 167 locked := cl.CoinLocked(coin) 168 if locked != wantLocked { 169 t.Errorf("Coin %v locked=%v, wanted=%v.", coin, locked, wantLocked) 170 return false 171 } 172 } 173 return true 174 } 175 176 // Make sure the BOOK locker say they are locked. 177 if !verifyLocked(bookLock, allCoins, true) { 178 t.Errorf("bookLock indicated coins were unlocked that should have been locked") 179 } 180 181 // Make sure the MASTER locker say they are locked too. 182 if !verifyLocked(masterLock, allCoins, true) { 183 t.Errorf("masterLock indicated coins were unlocked that should have been locked") 184 } 185 186 // Make sure the SWAP lockers sways they are locked too. 187 swapLock := masterLock.Swap() 188 if !verifyLocked(swapLock, allCoins, true) { 189 t.Errorf("swapLock indicated coins were unlocked that should have been locked") 190 } 191 192 // try and fail to unlock coins via the swap lock 193 oid := allOrderIDs[0] 194 swapLock.UnlockOrderCoins(oid) 195 if !verifyLocked(swapLock, allCoins, true) { 196 t.Errorf("swapLock indicated coins were unlocked that should have been locked") 197 } 198 199 // unlock properly via the book lock 200 bookLock.UnlockOrderCoins(oid) 201 orderCoins := coinMap[oid] 202 if !verifyLocked(bookLock, orderCoins, false) { 203 t.Errorf("bookLock indicated coins were locked that should have been unlocked") 204 } 205 if !verifyLocked(swapLock, orderCoins, false) { 206 t.Errorf("swapLock indicated coins were locked that should have been unlocked") 207 } 208 209 // Attempt relock of the already-locked coins. 210 delete(coinMap, oid) 211 failed := bookLock.LockCoins(coinMap) 212 if len(failed) != len(coinMap) { 213 t.Fatalf("should have failed to lock %d coins, got %d failed", len(coinMap), len(failed)) 214 } 215 216 // Relock the coins for the removed order. 217 bookLock.LockCoins(map[order.OrderID][]CoinID{ 218 oid: orderCoins, 219 }) 220 221 // Make sure the BOOK locker say they are locked. 222 if !verifyLocked(bookLock, allCoins, true) { 223 t.Errorf("bookLock indicated coins were unlocked that should have been locked") 224 } 225 226 bookLock.UnlockAll() 227 228 if !verifyLocked(bookLock, orderCoins, false) { 229 t.Errorf("bookLock indicated coins were locked that should have been unlocked") 230 } 231 }