decred.org/dcrdex@v1.0.5/server/asset/eth/coiner_test.go (about) 1 //go:build !harness 2 3 // These tests will not be run if the harness build tag is set. 4 5 package eth 6 7 import ( 8 "context" 9 "errors" 10 "math/big" 11 "testing" 12 13 "decred.org/dcrdex/dex/encode" 14 dexeth "decred.org/dcrdex/dex/networks/eth" 15 "github.com/ethereum/go-ethereum" 16 "github.com/ethereum/go-ethereum/common" 17 "github.com/ethereum/go-ethereum/core/types" 18 ) 19 20 func randomAddress() *common.Address { 21 var addr common.Address 22 copy(addr[:], encode.RandomBytes(20)) 23 return &addr 24 } 25 26 func TestNewRedeemCoin(t *testing.T) { 27 contractAddr := randomAddress() 28 var secret, secretHash, txHash [32]byte 29 copy(txHash[:], encode.RandomBytes(32)) 30 copy(secret[:], redeemSecretB) 31 copy(secretHash[:], redeemSecretHashB) 32 contract := dexeth.EncodeContractData(0, secretHash) 33 const gasPrice = 30 34 const gasTipCap = 2 35 const value = 5e9 36 const wantGas = 30 37 const wantGasTipCap = 2 38 tests := []struct { 39 name string 40 contract []byte 41 tx *types.Transaction 42 swpErr, txErr error 43 swap *dexeth.SwapState 44 wantErr bool 45 }{{ 46 name: "ok redeem", 47 tx: tTx(gasPrice, gasTipCap, 0, contractAddr, redeemCalldata), 48 contract: contract, 49 }, { 50 name: "non zero value with redeem", 51 tx: tTx(gasPrice, gasTipCap, value, contractAddr, redeemCalldata), 52 contract: contract, 53 wantErr: true, 54 }, { 55 name: "unable to decode redeem data, must be redeem for redeem coin type", 56 tx: tTx(gasPrice, gasTipCap, 0, contractAddr, initCalldata), 57 contract: contract, 58 wantErr: true, 59 }, { 60 name: "tx coin id for redeem - contract not in tx", 61 tx: tTx(gasPrice, gasTipCap, value, contractAddr, redeemCalldata), 62 contract: encode.RandomBytes(32), 63 wantErr: true, 64 }, { 65 name: "tx not found, redeemed", 66 txErr: ethereum.NotFound, 67 swap: tSwap(97, initLocktime, 1000, secret, dexeth.SSRedeemed, &initParticipantAddr), 68 contract: contract, 69 }, { 70 name: "tx not found, not redeemed", 71 txErr: ethereum.NotFound, 72 swap: tSwap(97, initLocktime, 1000, secret, dexeth.SSInitiated, &initParticipantAddr), 73 contract: contract, 74 wantErr: true, 75 }, { 76 name: "tx not found, swap err", 77 txErr: ethereum.NotFound, 78 swpErr: errors.New("swap not found"), 79 contract: contract, 80 wantErr: true, 81 }} 82 for _, test := range tests { 83 node := &testNode{ 84 tx: test.tx, 85 txErr: test.txErr, 86 swp: test.swap, 87 swpErr: test.swpErr, 88 } 89 eth := &AssetBackend{ 90 baseBackend: &baseBackend{ 91 node: node, 92 baseLogger: tLogger, 93 }, 94 contractAddr: *contractAddr, 95 assetID: BipID, 96 log: tLogger, 97 } 98 rc, err := eth.newRedeemCoin(txHash[:], test.contract) 99 if test.wantErr { 100 if err == nil { 101 t.Fatalf("expected error for test %q", test.name) 102 } 103 continue 104 } 105 if err != nil { 106 t.Fatalf("unexpected error for test %q: %v", test.name, err) 107 } 108 109 if test.txErr == nil && (rc.secretHash != secretHash || 110 rc.secret != secret || 111 rc.value.Uint64() != 0 || 112 dexeth.WeiToGwei(rc.gasFeeCap) != wantGas || 113 dexeth.WeiToGwei(rc.gasTipCap) != wantGasTipCap) { 114 t.Fatalf("returns do not match expected for test %q / %v", test.name, rc) 115 } 116 } 117 } 118 119 func TestNewSwapCoin(t *testing.T) { 120 contractAddr, randomAddr := randomAddress(), randomAddress() 121 var secret, secretHash, txHash [32]byte 122 copy(txHash[:], encode.RandomBytes(32)) 123 copy(secret[:], redeemSecretB) 124 copy(secretHash[:], redeemSecretHashB) 125 txCoinIDBytes := txHash[:] 126 badCoinIDBytes := encode.RandomBytes(39) 127 const gasPrice = 30 128 const value = 5e9 129 const gasTipCap = 2 130 wantGas := dexeth.WeiToGwei(big.NewInt(3e10)) 131 wantVal := dexeth.WeiToGwei(big.NewInt(5e18)) 132 wantGasTipCap := dexeth.WeiToGweiCeil(big.NewInt(2e9)) 133 tests := []struct { 134 name string 135 coinID []byte 136 contract []byte 137 tx *types.Transaction 138 swpErr, txErr error 139 wantErr bool 140 }{{ 141 name: "ok init", 142 tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), 143 coinID: txCoinIDBytes, 144 contract: dexeth.EncodeContractData(0, secretHash), 145 }, { 146 name: "contract incorrect length", 147 tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), 148 coinID: txCoinIDBytes, 149 contract: initSecretHashA[:31], 150 wantErr: true, 151 }, { 152 name: "tx has no data", 153 tx: tTx(gasPrice, gasTipCap, value, contractAddr, nil), 154 coinID: txCoinIDBytes, 155 contract: initSecretHashA, 156 wantErr: true, 157 }, { 158 name: "unable to decode init data, must be init for init coin type", 159 tx: tTx(gasPrice, gasTipCap, value, contractAddr, redeemCalldata), 160 coinID: txCoinIDBytes, 161 contract: initSecretHashA, 162 wantErr: true, 163 }, { 164 name: "unable to decode CoinID", 165 tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), 166 contract: initSecretHashA, 167 wantErr: true, 168 }, { 169 name: "invalid coinID", 170 tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), 171 coinID: badCoinIDBytes, 172 contract: initSecretHashA, 173 wantErr: true, 174 }, { 175 name: "transaction error", 176 tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), 177 coinID: txCoinIDBytes, 178 contract: initSecretHashA, 179 txErr: errors.New(""), 180 wantErr: true, 181 }, { 182 name: "transaction not found error", 183 tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), 184 coinID: txCoinIDBytes, 185 contract: initSecretHashA, 186 txErr: ethereum.NotFound, 187 wantErr: true, 188 }, { 189 name: "wrong contract", 190 tx: tTx(gasPrice, gasTipCap, value, randomAddr, initCalldata), 191 coinID: txCoinIDBytes, 192 contract: initSecretHashA, 193 wantErr: true, 194 }, { 195 name: "tx coin id for swap - contract not in tx", 196 tx: tTx(gasPrice, gasTipCap, value, contractAddr, initCalldata), 197 coinID: txCoinIDBytes, 198 contract: encode.RandomBytes(32), 199 wantErr: true, 200 }} 201 for _, test := range tests { 202 node := &testNode{ 203 tx: test.tx, 204 txErr: test.txErr, 205 } 206 eth := &AssetBackend{ 207 baseBackend: &baseBackend{ 208 node: node, 209 baseLogger: tLogger, 210 }, 211 contractAddr: *contractAddr, 212 atomize: dexeth.WeiToGwei, 213 } 214 sc, err := eth.newSwapCoin(test.coinID, test.contract) 215 if test.wantErr { 216 if err == nil { 217 t.Fatalf("expected error for test %q", test.name) 218 } 219 continue 220 } 221 if err != nil { 222 t.Fatalf("unexpected error for test %q: %v", test.name, err) 223 } 224 225 if sc.init.Participant != initParticipantAddr || 226 sc.secretHash != secretHash || 227 dexeth.WeiToGwei(sc.value) != wantVal || 228 dexeth.WeiToGwei(sc.gasFeeCap) != wantGas || 229 dexeth.WeiToGwei(sc.gasTipCap) != wantGasTipCap || 230 sc.init.LockTime.Unix() != initLocktime { 231 t.Fatalf("returns do not match expected for test %q / %v", test.name, sc) 232 } 233 } 234 } 235 236 type Confirmer interface { 237 Confirmations(context.Context) (int64, error) 238 String() string 239 } 240 241 func TestConfirmations(t *testing.T) { 242 contractAddr, nullAddr := new(common.Address), new(common.Address) 243 copy(contractAddr[:], encode.RandomBytes(20)) 244 var secret, secretHash, txHash [32]byte 245 copy(txHash[:], encode.RandomBytes(32)) 246 copy(secret[:], redeemSecretB) 247 copy(secretHash[:], redeemSecretHashB) 248 const gasPrice = 30 249 const gasTipCap = 2 250 const swapVal = 25e8 251 const txVal = swapVal * 2 252 const oneGweiMore = swapVal + 1 253 tests := []struct { 254 name string 255 swap *dexeth.SwapState 256 bn uint64 257 value uint64 258 wantConfs int64 259 swapErr, bnErr error 260 wantErr, redeem bool 261 }{{ 262 name: "ok has confs value not verified", 263 bn: 100, 264 swap: tSwap(97, initLocktime, swapVal, secret, dexeth.SSInitiated, &initParticipantAddr), 265 value: txVal, 266 wantConfs: 4, 267 }, { 268 name: "ok no confs", 269 swap: tSwap(0, 0, 0, secret, dexeth.SSNone, nullAddr), 270 value: txVal, 271 }, { 272 name: "ok redeem swap status redeemed", 273 bn: 97, 274 swap: tSwap(97, initLocktime, swapVal, secret, dexeth.SSRedeemed, &initParticipantAddr), 275 value: 0, 276 wantConfs: 1, 277 redeem: true, 278 }, { 279 name: "ok redeem swap status initiated", 280 swap: tSwap(97, initLocktime, swapVal, secret, dexeth.SSInitiated, &initParticipantAddr), 281 value: 0, 282 redeem: true, 283 }, { 284 name: "redeem bad swap state None", 285 swap: tSwap(0, 0, 0, secret, dexeth.SSNone, nullAddr), 286 value: 0, 287 wantErr: true, 288 redeem: true, 289 }, { 290 name: "error getting swap", 291 swapErr: errors.New(""), 292 value: txVal, 293 wantErr: true, 294 }, { 295 name: "value differs from initial transaction", 296 swap: tSwap(99, initLocktime, oneGweiMore, secret, dexeth.SSInitiated, &initParticipantAddr), 297 value: txVal, 298 wantErr: true, 299 }, { 300 name: "participant differs from initial transaction", 301 swap: tSwap(99, initLocktime, swapVal, secret, dexeth.SSInitiated, nullAddr), 302 value: txVal, 303 wantErr: true, 304 // }, { 305 // name: "locktime not an int64", 306 // swap: tSwap(99, new(big.Int).SetUint64(^uint64(0)), value, secret, dexeth.SSInitiated, &initParticipantAddr), 307 // value: value, 308 // ct: sctInit, 309 // wantErr: true, 310 }, { 311 name: "locktime differs from initial transaction", 312 swap: tSwap(99, 0, swapVal, secret, dexeth.SSInitiated, &initParticipantAddr), 313 value: txVal, 314 wantErr: true, 315 }, { 316 name: "block number error", 317 swap: tSwap(97, initLocktime, swapVal, secret, dexeth.SSInitiated, &initParticipantAddr), 318 value: txVal, 319 bnErr: errors.New(""), 320 wantErr: true, 321 }} 322 for _, test := range tests { 323 node := &testNode{ 324 swp: test.swap, 325 swpErr: test.swapErr, 326 blkNum: test.bn, 327 blkNumErr: test.bnErr, 328 } 329 eth := &AssetBackend{ 330 baseBackend: &baseBackend{ 331 node: node, 332 baseLogger: tLogger, 333 }, 334 contractAddr: *contractAddr, 335 atomize: dexeth.WeiToGwei, 336 } 337 338 swapData := dexeth.EncodeContractData(0, secretHash) 339 340 var confirmer Confirmer 341 var err error 342 if test.redeem { 343 node.tx = tTx(gasPrice, gasTipCap, test.value, contractAddr, redeemCalldata) 344 confirmer, err = eth.newRedeemCoin(txHash[:], swapData) 345 } else { 346 node.tx = tTx(gasPrice, gasTipCap, test.value, contractAddr, initCalldata) 347 confirmer, err = eth.newSwapCoin(txHash[:], swapData) 348 } 349 if err != nil { 350 t.Fatalf("unexpected error for test %q: %v", test.name, err) 351 } 352 353 _ = confirmer.String() // unrelated panic test 354 355 confs, err := confirmer.Confirmations(nil) 356 if test.wantErr { 357 if err == nil { 358 t.Fatalf("expected error for test %q", test.name) 359 } 360 continue 361 } 362 if err != nil { 363 t.Fatalf("unexpected error for test %q: %v", test.name, err) 364 } 365 if confs != test.wantConfs { 366 t.Fatalf("want %d but got %d confs for test: %v", test.wantConfs, confs, test.name) 367 } 368 } 369 } 370 371 // func TestGeneratePackedInits(t *testing.T) { 372 // hexToHash := func(s string) (h [32]byte) { 373 // b, _ := hex.DecodeString(s) 374 // copy(h[:], b) 375 // return 376 // } 377 // inits := []swapv0.ETHSwapInitiation{ 378 // { 379 // RefundTimestamp: big.NewInt(1632112916), 380 // SecretHash: hexToHash("8b3e4acc53b664f9cf6fcac0adcd328e95d62ba1f4379650ae3e1460a0f9d1a1"), 381 // Value: dexeth.GweiToWei(25e8), 382 // Participant: common.HexToAddress("0x345853e21b1d475582e71cc269124ed5e2dd3422"), 383 // }, 384 // { 385 // RefundTimestamp: big.NewInt(1632112916), 386 // SecretHash: hexToHash("ebdc4c31b88d0c8f4d644591a8e00e92b607f920ad8050deb7c7469767d9c561"), 387 // Value: dexeth.GweiToWei(25e8), 388 // Participant: common.HexToAddress("0x345853e21b1d475582e71cc269124ed5e2dd3422"), 389 // }, 390 // } 391 // data, err := dexeth.ABIs[0].Pack("initiate", inits) 392 // if err != nil { 393 // t.Fatalf("Pack error: %v", err) 394 // } 395 396 // fmt.Printf("tx data: %x \n", data) 397 // }