diff --git a/lib/orderbook/OrderBook.ts b/lib/orderbook/OrderBook.ts index ed8b42de7..a9c663326 100644 --- a/lib/orderbook/OrderBook.ts +++ b/lib/orderbook/OrderBook.ts @@ -304,6 +304,11 @@ class OrderBook extends EventEmitter { this.pairInstances.set(pairInstance.id, pairInstance); this.addTradingPair(pairInstance.id); + this.pool.rawPeers().forEach(async (peer) => { + this.checkPeerCurrencies(peer); + await this.verifyPeerPairs(peer); + }); + this.pool.updatePairs(this.pairIds); return pairInstance; } @@ -332,7 +337,7 @@ class OrderBook extends EventEmitter { } const currencyInstance = await this.repository.addCurrency({ ...currency, decimalPlaces: currency.decimalPlaces || 8 }); this.currencyInstances.set(currencyInstance.id, currencyInstance); - this.swaps.swapClientManager.add(currencyInstance); + await this.swaps.swapClientManager.add(currencyInstance); } public removeCurrency = async (currencyId: string) => { @@ -344,7 +349,6 @@ class OrderBook extends EventEmitter { } } this.currencyInstances.delete(currencyId); - this.swaps.swapClientManager.remove(currencyId); await currency.destroy(); } else { throw errors.CURRENCY_DOES_NOT_EXIST(currencyId); @@ -360,6 +364,11 @@ class OrderBook extends EventEmitter { this.pairInstances.delete(pairId); this.tradingPairs.delete(pairId); + this.pool.rawPeers().forEach(async (peer) => { + this.checkPeerCurrencies(peer); + await this.verifyPeerPairs(peer); + }); + this.pool.updatePairs(this.pairIds); return pair.destroy(); } @@ -1006,7 +1015,11 @@ class OrderBook extends EventEmitter { } private removePeerPair = (peerPubKey: string, pairId: string) => { - const tp = this.getTradingPair(pairId); + const tp = this.tradingPairs.get(pairId); + if (!tp) { + return; + } + const orders = tp.removePeerOrders(peerPubKey); orders.forEach((order) => { this.emit('peerOrder.invalidation', order); diff --git a/lib/p2p/Pool.ts b/lib/p2p/Pool.ts index d4cc7c0c2..dc41b5867 100644 --- a/lib/p2p/Pool.ts +++ b/lib/p2p/Pool.ts @@ -238,7 +238,9 @@ class Pool extends EventEmitter { * packet to currently connected peers to notify them of the change. */ public updateConnextState = (tokenAddresses: Map, pubKey?: string) => { - this.nodeState.connextIdentifier = pubKey || ''; + if (pubKey) { + this.nodeState.connextIdentifier = pubKey; + } tokenAddresses.forEach((tokenAddress, currency) => { this.nodeState.tokenIdentifiers[currency] = tokenAddress; }); @@ -476,6 +478,10 @@ class Pool extends EventEmitter { return peerInfos; } + public rawPeers = (): Map => { + return this.peers; + } + private addressIsSelf = (address: Address): boolean => { if (address.port === this.listenPort) { switch (address.host) { diff --git a/lib/swaps/SwapClientManager.ts b/lib/swaps/SwapClientManager.ts index 4e0877287..b2bba6d37 100644 --- a/lib/swaps/SwapClientManager.ts +++ b/lib/swaps/SwapClientManager.ts @@ -338,19 +338,21 @@ class SwapClientManager extends EventEmitter { * @param currency a currency that should be linked with a swap client. * @returns Nothing upon success, throws otherwise. */ - public add = (currency: Currency): void => { - if (currency.swapClient === SwapClientType.Lnd) { - // in case of lnd we check if the configuration includes swap client - // for the specified currency - let isCurrencyConfigured = false; - for (const lndCurrency in this.config.lnd) { - if (lndCurrency === currency.id) { - isCurrencyConfigured = true; - break; + public add = async (currency: Currency) => { + if (currency.tokenAddress) { + if (currency.swapClient === SwapClientType.Connext) { + if (!this.connextClient) { + throw errors.SWAP_CLIENT_NOT_CONFIGURED(currency.id); } + this.swapClients.set(currency.id, this.connextClient); + this.connextClient.tokenAddresses.set(currency.id, currency.tokenAddress); + this.emit('connextUpdate', this.connextClient.tokenAddresses); } - // adding a new lnd client at runtime is currently not supported - if (!isCurrencyConfigured) { + } else if (currency.swapClient === SwapClientType.Lnd) { + // in case of lnd we check if the configuration includes swap client + // for the specified currency + const config = this.config.lnd[currency.id]; + if (!config) { throw errors.SWAP_CLIENT_NOT_CONFIGURED(currency.id); } } diff --git a/test/simulation/README.md b/test/simulation/README.md index c838f0625..68542a131 100644 --- a/test/simulation/README.md +++ b/test/simulation/README.md @@ -117,6 +117,7 @@ Below is a list of implemented (checked) and planned (unchecked) test cases. - [x] Placed order should get broadcasted over the network, and added to connected peers' order books. - [x] Removed order should get invalidated over the network, and removed from connected peers' order books. +- [x] Added trading pairs and currencies should trigger broadcast of active orders from connected peers' order books. - [ ] Placed order should get internal matches, and trigger order invalidation over the network. - [ ] Peer disconnection should trigger orders removal to all his orders. diff --git a/test/simulation/actions.go b/test/simulation/actions.go index d3937098d..dac411f9d 100644 --- a/test/simulation/actions.go +++ b/test/simulation/actions.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/ExchangeUnion/xud-simulation/connexttest" "math/big" "time" @@ -27,69 +28,89 @@ type actions struct { } func (a *actions) init(node *xudtest.HarnessNode) { - // Verify connectivity. - timeout := time.Now().Add(10 * time.Second) - for { - req := &xudrpc.GetInfoRequest{} - res, err := node.Client.GetInfo(a.ctx, req) + var info *xudrpc.GetInfoResponse + isReady := func() bool { + var err error + info, err = node.Client.GetInfo(context.Background(), &xudrpc.GetInfoRequest{}) a.assert.NoError(err) - a.assert.NotNil(res.Lnd["BTC"]) - a.assert.NotNil(res.Lnd["LTC"]) - if len(res.Lnd["BTC"].Chains) == 1 && len(res.Lnd["LTC"].Chains) == 1 { - a.assert.Equal(res.Lnd["BTC"].Chains[0].Chain, "bitcoin") - a.assert.Equal(res.Lnd["BTC"].Chains[0].Network, "simnet") - a.assert.Equal(res.Lnd["LTC"].Chains[0].Chain, "litecoin") - a.assert.Equal(res.Lnd["LTC"].Chains[0].Network, "simnet") + return len(info.Lnd["BTC"].Chains) == 1 && len(info.Lnd["LTC"].Chains) == 1 + } - // Set the node public key. - node.SetPubKey(res.NodePubKey) + timeout := time.After(10 * time.Second) + for !isReady() { + select { + case <-timeout: + a.assert.Fail("timeout waiting for LND synced chains") + case <-time.After(1 * time.Second): + } + } - // Add currencies - a.addCurrency(node, "BTC", xudrpc.Currency_LND, "", 8) - a.addCurrency(node, "LTC", xudrpc.Currency_LND, "", 8) + a.assert.Equal(info.Lnd["BTC"].Chains[0].Chain, "bitcoin") + a.assert.Equal(info.Lnd["BTC"].Chains[0].Network, "simnet") + a.assert.Equal(info.Lnd["LTC"].Chains[0].Chain, "litecoin") + a.assert.Equal(info.Lnd["LTC"].Chains[0].Network, "simnet") - // Add pairs to the node. - a.addPair(node, "LTC", "BTC") + // Set the node public key. + node.SetPubKey(info.NodePubKey) - break - } - a.assert.False(time.Now().After(timeout), "waiting for synced chains timeout") - // retry interval - time.Sleep(100 * time.Millisecond) - } + // Add currencies. + a.addCurrency(node, "BTC", xudrpc.Currency_LND, "", 8) + a.addCurrency(node, "LTC", xudrpc.Currency_LND, "", 8) + a.addCurrency(node, "ETH", xudrpc.Currency_CONNEXT, connexttest.ETHTokenAddress, 18) + + // Add pairs. + a.addPair(node, "LTC", "BTC") + a.addPair(node, "BTC", "ETH") } -func (a *actions) initConnext(net *xudtest.NetworkHarness, node *xudtest.HarnessNode, fund bool) { +func (a *actions) FundETH(net *xudtest.NetworkHarness, node *xudtest.HarnessNode) { // Wait for node's connext connection to catch-up. - err := waitConnextReady(node) - a.assert.NoError(err) + a.waitConnextReady(node) // Fund node's wallet. - if fund { - resInfo, err := node.Client.GetInfo(context.Background(), &xudrpc.GetInfoRequest{}) - a.assert.NoError(err) - amount := big.NewInt(2000000000000000000) - err = net.ConnextNetwork.Wallet.SendEth(resInfo.Connext.Address, amount) - a.assert.NoError(err) + resInfo, err := node.Client.GetInfo(context.Background(), &xudrpc.GetInfoRequest{}) + a.assert.NoError(err) + amount := big.NewInt(2000000000000000000) + err = net.ConnextNetwork.Wallet.SendEth(resInfo.Connext.Address, amount) + a.assert.NoError(err) - time.Sleep(15 * time.Second) - } + time.Sleep(15 * time.Second) - // Init node. - ETHTokenAddress := "0x0000000000000000000000000000000000000000" - a.addCurrency(node, "ETH", 2, ETHTokenAddress, 18) - a.addPair(node, "BTC", "ETH") - err = net.RestartNode(node) + // Verify node's ETH balance. + bal, err := node.Client.GetBalance(a.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) + ethBal := bal.Balances["ETH"] a.assert.NoError(err) + a.assert.Equal(uint64(200000000), ethBal.TotalBalance) + a.assert.Equal(uint64(200000000), ethBal.WalletBalance) + a.assert.Equal(uint64(0), ethBal.ChannelBalance) +} - // Verify node's ETH balance. - if fund { - resBal, err := node.Client.GetBalance(a.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) - a.assert.NoError(err) - a.assert.Equal(uint64(200000000), resBal.Balances["ETH"].WalletBalance) +func (a *actions) waitConnextReady(node *xudtest.HarnessNode) { + isReady := func() bool { + info, err := node.Client.GetInfo(context.Background(), &xudrpc.GetInfoRequest{}) + if err != nil { + return false + } + + return info.Connext.Address != "" + } + + timeout := time.After(30 * time.Second) + for !isReady() { + select { + case <-timeout: + a.assert.Fail("timeout waiting for connext to be ready") + case <-time.After(1 * time.Second): + } } } +func (a *actions) removeCurrency(node *xudtest.HarnessNode, currency string) { + req := &xudrpc.RemoveCurrencyRequest{Currency: currency} + _, err := node.Client.RemoveCurrency(a.ctx, req) + a.assert.NoError(err) +} + func (a *actions) addCurrency(node *xudtest.HarnessNode, currency string, swapClient xudrpc.Currency_SwapClient, tokenAddress string, decimalPlaces uint32) { if len(tokenAddress) > 0 { req := &xudrpc.Currency{Currency: currency, SwapClient: swapClient, TokenAddress: tokenAddress, DecimalPlaces: decimalPlaces} @@ -102,6 +123,24 @@ func (a *actions) addCurrency(node *xudtest.HarnessNode, currency string, swapCl } } +func (a *actions) removePair(node *xudtest.HarnessNode, pairId string) { + // Check the current number of pairs. + res, err := node.Client.GetInfo(a.ctx, &xudrpc.GetInfoRequest{}) + a.assert.NoError(err) + + prevNumPairs := res.NumPairs + + // Remove the pair. + req := &xudrpc.RemovePairRequest{PairId: pairId} + _, err = node.Client.RemovePair(a.ctx, req) + a.assert.NoError(err) + + // Verify that the pair was removed. + res, err = node.Client.GetInfo(a.ctx, &xudrpc.GetInfoRequest{}) + a.assert.NoError(err) + a.assert.Equal(res.NumPairs, prevNumPairs-1) +} + func (a *actions) addPair(node *xudtest.HarnessNode, baseCurrency string, quoteCurrency string) { // Check the current number of pairs. res, err := node.Client.GetInfo(a.ctx, &xudrpc.GetInfoRequest{}) @@ -232,10 +271,16 @@ func (a *actions) placeOrderAndBroadcast(srcNode, destNode *xudtest.HarnessNode, a.assert.Equal(res.RemainingOrder.LocalId, req.OrderId) // Retrieve and verify the added order event on destNode. - e := <-destNodeOrderChan - a.assert.NoError(e.err) - a.assert.NotNil(e.orderUpdate) - peerOrder := e.orderUpdate.GetOrder() + + var peerOrder *xudrpc.Order + select { + case e := <-destNodeOrderChan: + a.assert.NoError(e.err) + a.assert.NotNil(e.orderUpdate) + peerOrder = e.orderUpdate.GetOrder() + case <-time.After(5 * time.Second): + a.assert.Fail("timeout waiting for broadcasted order on destNode") + } // Verify the peer order. a.assert.NotEqual(peerOrder.Id, req.OrderId) // Local id should not equal the global id. diff --git a/test/simulation/connexttest/harness.go b/test/simulation/connexttest/harness.go index 15163a637..6823f561c 100644 --- a/test/simulation/connexttest/harness.go +++ b/test/simulation/connexttest/harness.go @@ -17,6 +17,8 @@ var ( // the `localhost` of the host machine. EthProviderURL = "http://172.17.0.1:8545" NodeURL = "http://172.17.0.1:8888" + + ETHTokenAddress = "0x0000000000000000000000000000000000000000" ) type NetworkHarness struct { diff --git a/test/simulation/tests-instability.go b/test/simulation/tests-instability.go index f6a787eab..7c2ece779 100644 --- a/test/simulation/tests-instability.go +++ b/test/simulation/tests-instability.go @@ -61,7 +61,7 @@ func testMakerCrashedAfterSendBeforePreimageResolved(net *xudtest.NetworkHarness } func testMakerCrashedAfterSendBeforePreimageResolvedConnextIn(net *xudtest.NetworkHarness, ht *harnessTest) { - ht.act.initConnext(net, net.Bob, true) + ht.act.FundETH(net, net.Bob) testMakerCrashedDuringSwapConnextIn(net, ht, []string{"CUSTOM_SCENARIO=INSTABILITY::MAKER_CRASH_AFTER_SEND_BEFORE_PREIMAGE_RESOLVED"}) } @@ -130,7 +130,7 @@ func testMakerCrashedDuringSwapConnextIn(net *xudtest.NetworkHarness, ht *harnes net.Alice, err = net.SetCustomXud(ht.ctx, ht, net.Alice, makerEnvArgs) ht.assert.NoError(err) ht.act.init(net.Alice) - ht.act.initConnext(net, net.Alice, false) + ht.act.waitConnextReady(net.Alice) // Connect Alice to Bob. ht.act.connect(net.Alice, net.Bob) @@ -173,9 +173,7 @@ func testMakerCrashedDuringSwapConnextIn(net *xudtest.NetworkHarness, ht *harnes err = net.Alice.Start(nil) ht.assert.NoError(err) - - err = waitConnextReady(net.Alice) - ht.assert.NoError(err) + ht.act.waitConnextReady(net.Alice) // Brief delay to allow for swap to be recovered consistently. time.Sleep(3 * time.Second) @@ -260,9 +258,8 @@ func testMakerConnextClientCrashedBeforeSettlement(net *xudtest.NetworkHarness, ht.assert.NoError(err) ht.act.init(net.Alice) - - ht.act.initConnext(net, net.Alice, false) - ht.assert.NoError(waitConnextReady(net.Bob)) + ht.act.waitConnextReady(net.Alice) + ht.act.waitConnextReady(net.Bob) // Connect Alice to Bob. ht.act.connect(net.Alice, net.Bob) @@ -312,9 +309,7 @@ func testMakerConnextClientCrashedBeforeSettlement(net *xudtest.NetworkHarness, // Restart Alice's connext-client. err = net.Alice.ConnextClient.Start(nil) ht.assert.NoError(err) - - err = waitConnextReady(net.Alice) - ht.assert.NoError(err) + ht.act.waitConnextReady(net.Alice) // Brief delay to allow for swap to be recovered consistently. // The pending swap recheck interval is usually 5m, but was adjusted in @@ -406,10 +401,10 @@ func testMakerCrashedAfterSendDelayedSettlementConnextOut(net *xudtest.NetworkHa ht.assert.NoError(err) ht.act.init(net.Alice) - ht.act.initConnext(net, net.Alice, true) + ht.act.FundETH(net, net.Alice) ht.act.init(net.Bob) - ht.act.initConnext(net, net.Bob, false) + ht.act.waitConnextReady(net.Bob) // Connect Alice to Bob. ht.act.connect(net.Alice, net.Bob) @@ -451,9 +446,7 @@ func testMakerCrashedAfterSendDelayedSettlementConnextOut(net *xudtest.NetworkHa err = net.Alice.Start(nil) ht.assert.NoError(err) - - err = waitConnextReady(net.Alice) - ht.assert.NoError(err) + ht.act.waitConnextReady(net.Alice) // Verify that alice hasn't claimed her BTC yet. The incoming BTC payment // cannot be settled until the outgoing ETH payment is settled by bob, @@ -494,10 +487,10 @@ func testMakerCrashedAfterSendDelayedSettlementConnextIn(net *xudtest.NetworkHar ht.assert.NoError(err) ht.act.init(net.Alice) - ht.act.initConnext(net, net.Alice, false) + ht.act.waitConnextReady(net.Alice) ht.act.init(net.Bob) - ht.act.initConnext(net, net.Bob, true) + ht.act.FundETH(net, net.Bob) // Connect Alice to Bob. ht.act.connect(net.Alice, net.Bob) @@ -542,9 +535,7 @@ func testMakerCrashedAfterSendDelayedSettlementConnextIn(net *xudtest.NetworkHar err = net.Alice.Start(nil) ht.assert.NoError(err) - - err = waitConnextReady(net.Alice) - ht.assert.NoError(err) + ht.act.waitConnextReady(net.Alice) // Verify that alice hasn't claimed her ETH yet. The incoming ETH payment // cannot be settled until the outgoing BTC payment is settled by bob, diff --git a/test/simulation/tests-integration.go b/test/simulation/tests-integration.go index 1f1a97b51..8c19a6eb2 100644 --- a/test/simulation/tests-integration.go +++ b/test/simulation/tests-integration.go @@ -1,9 +1,8 @@ package main import ( - "context" "fmt" - "math/big" + "github.com/ExchangeUnion/xud-simulation/connexttest" "time" "github.com/ExchangeUnion/xud-simulation/xudrpc" @@ -55,6 +54,10 @@ var integrationTestCases = []*testCase{ name: "order broadcast and invalidation", test: testOrderBroadcastAndInvalidation, }, + { + name: "runtime add pair of active orders", + test: testRuntimeAddPairActiveOrders, + }, { name: "multiple hop swap", test: testMultiHopSwap, @@ -236,6 +239,95 @@ func testInternalMatchAndInvalidation(net *xudtest.NetworkHarness, ht *harnessTe ht.act.disconnect(net.Alice, net.Bob) } +// testRuntimeAddPairActiveOrders implements: +// Added trading pairs and currencies should trigger broadcast of active orders from already-connected peers. +func testRuntimeAddPairActiveOrders(net *xudtest.NetworkHarness, ht *harnessTest) { + // Remove previously-added pairs/currencies from both Alice and Bob. + ht.act.removePair(net.Alice, "LTC/BTC") + ht.act.removePair(net.Alice, "BTC/ETH") + ht.act.removeCurrency(net.Alice, "LTC") + ht.act.removeCurrency(net.Alice, "BTC") + ht.act.removeCurrency(net.Alice, "ETH") + ht.act.removePair(net.Bob, "LTC/BTC") + ht.act.removePair(net.Bob, "BTC/ETH") + ht.act.removeCurrency(net.Bob, "LTC") + ht.act.removeCurrency(net.Bob, "BTC") + ht.act.removeCurrency(net.Bob, "ETH") + + // Connect Alice to Bob. + ht.act.connect(net.Alice, net.Bob) + ht.act.verifyConnectivity(net.Alice, net.Bob) + + // Re-add the pairs/currencies to Alice after peer connection was already established. + ht.act.addCurrency(net.Alice, "BTC", xudrpc.Currency_LND, "", 8) + ht.act.addCurrency(net.Alice, "LTC", xudrpc.Currency_LND, "", 8) + ht.act.addCurrency(net.Alice, "ETH", xudrpc.Currency_CONNEXT, connexttest.ETHTokenAddress, 18) + ht.act.addPair(net.Alice, "LTC", "BTC") + ht.act.addPair(net.Alice, "BTC", "ETH") + + // Place LTC/BTC order on Alice. + req := &xudrpc.PlaceOrderRequest{ + OrderId: "maker_order_id", + Price: 0.02, + Quantity: 1000000, + PairId: "LTC/BTC", + Side: xudrpc.OrderSide_BUY, + } + res, err := net.Alice.Client.PlaceOrderSync(ht.ctx, req) + ht.assert.NoError(err) + ht.assert.Len(res.InternalMatches, 0) + ht.assert.Len(res.SwapSuccesses, 0) + ht.assert.Len(res.SwapFailures, 0) + ht.assert.NotNil(res.RemainingOrder) + + // Bob should receive the order once his LTC/BTC pair is re-added. + bobOrdersChan := subscribeOrders(ht.ctx, net.Bob) + ht.act.addCurrency(net.Bob, "BTC", xudrpc.Currency_LND, "", 8) + ht.act.addCurrency(net.Bob, "LTC", xudrpc.Currency_LND, "", 8) + ht.act.addPair(net.Bob, "LTC", "BTC") + + e := <-bobOrdersChan + ht.assert.NoError(e.err) + ht.assert.NotNil(e.orderUpdate) + peerOrder := e.orderUpdate.GetOrder() + ht.assert.Equal(peerOrder.Id, res.RemainingOrder.Id) + ht.assert.Equal(peerOrder.PairId, req.PairId) + ht.assert.Equal(peerOrder.NodeIdentifier.NodePubKey, net.Alice.PubKey()) + ht.act.removeOrderAndInvalidate(net.Alice, net.Bob, res.RemainingOrder) + + // Place BTC/ETH order on Alice. + req = &xudrpc.PlaceOrderRequest{ + OrderId: "maker_order_id", + Price: 40, + Quantity: 100, + PairId: "BTC/ETH", + Side: xudrpc.OrderSide_BUY, + } + res, err = net.Alice.Client.PlaceOrderSync(ht.ctx, req) + ht.assert.NoError(err) + ht.assert.Len(res.InternalMatches, 0) + ht.assert.Len(res.SwapSuccesses, 0) + ht.assert.Len(res.SwapFailures, 0) + ht.assert.NotNil(res.RemainingOrder) + + // Bob should receive the order once his BTC/ETH pair is re-added. + bobOrdersChan = subscribeOrders(ht.ctx, net.Bob) + ht.act.addCurrency(net.Bob, "ETH", xudrpc.Currency_CONNEXT, connexttest.ETHTokenAddress, 18) + ht.act.addPair(net.Bob, "BTC", "ETH") + + e = <-bobOrdersChan + ht.assert.NoError(e.err) + ht.assert.NotNil(e.orderUpdate) + peerOrder = e.orderUpdate.GetOrder() + ht.assert.Equal(peerOrder.Id, res.RemainingOrder.Id) + ht.assert.Equal(peerOrder.PairId, req.PairId) + ht.assert.Equal(peerOrder.NodeIdentifier.NodePubKey, net.Alice.PubKey()) + ht.act.removeOrderAndInvalidate(net.Alice, net.Bob, res.RemainingOrder) + + // Cleanup. + ht.act.disconnect(net.Alice, net.Bob) +} + func testOrderMatchingAndSwap(net *xudtest.NetworkHarness, ht *harnessTest) { // Connect Alice to Bob. ht.act.connect(net.Alice, net.Bob) @@ -399,70 +491,16 @@ func testOrderReplacement(net *xudtest.NetworkHarness, ht *harnessTest) { ht.act.disconnect(net.Alice, net.Bob) } -func waitConnextReady(node *xudtest.HarnessNode) error { - isReady := func() bool { - info, err := node.Client.GetInfo(context.Background(), &xudrpc.GetInfoRequest{}) - if err != nil { - return false - } - - return info.Connext.Address != "" - } - - timeout := time.After(30 * time.Second) - for !isReady() { - select { - case <-timeout: - return fmt.Errorf("timeout waiting for connext to be ready") - case <-time.After(1 * time.Second): - } - } - - return nil -} - func testOrderMatchingAndSwapConnext(net *xudtest.NetworkHarness, ht *harnessTest) { - // Wait for Alice's connext connection to catch-up. - err := waitConnextReady(net.Alice) - ht.assert.NoError(err) - - // Fund Alice's wallet. - resInfo, err := net.Alice.Client.GetInfo(context.Background(), &xudrpc.GetInfoRequest{}) - ht.assert.NoError(err) - amount := big.NewInt(2000000000000000000) - err = net.ConnextNetwork.Wallet.SendEth(resInfo.Connext.Address, amount) - ht.assert.NoError(err) - - time.Sleep(15 * time.Second) - - // Init Alice. - ETHTokenAddress := "0x0000000000000000000000000000000000000000" - ht.act.addCurrency(net.Alice, "ETH", 2, ETHTokenAddress, 18) - ht.act.addPair(net.Alice, "BTC", "ETH") - err = net.RestartNode(net.Alice) - ht.assert.NoError(err) + // Connect Alice to Bob. + ht.act.connect(net.Alice, net.Bob) + ht.act.verifyConnectivity(net.Alice, net.Bob) - // Verify Alice ETH balance. - resBal, err := net.Alice.Client.GetBalance(ht.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) - ht.assert.Equal(uint64(200000000), resBal.Balances["ETH"].TotalBalance) - ht.assert.Equal(uint64(200000000), resBal.Balances["ETH"].WalletBalance) - ht.assert.Equal(uint64(0), resBal.Balances["ETH"].ChannelBalance) + ht.act.FundETH(net, net.Alice) - // Wait for Bob's connext connection to catch-up. - err = waitConnextReady(net.Bob) + preChanAliceBal, err := net.Alice.Client.GetBalance(ht.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) ht.assert.NoError(err) - - // Init Bob. - ht.act.addCurrency(net.Bob, "ETH", 2, ETHTokenAddress, 18) - ht.act.addPair(net.Bob, "BTC", "ETH") - err = net.RestartNode(net.Bob) - ht.assert.NoError(err) - - // Verify Bob ETH balance. - resBal, err = net.Bob.Client.GetBalance(ht.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) - ht.assert.Equal(uint64(0), resBal.Balances["ETH"].TotalBalance) - ht.assert.Equal(uint64(0), resBal.Balances["ETH"].WalletBalance) - ht.assert.Equal(uint64(0), resBal.Balances["ETH"].ChannelBalance) + preChanAliceEthBal := preChanAliceBal.Balances["ETH"] // Open channel from Alice. err = openETHChannel(ht.ctx, net.Alice, 40000, 0) @@ -471,10 +509,12 @@ func testOrderMatchingAndSwapConnext(net *xudtest.NetworkHarness, ht *harnessTes time.Sleep(15 * time.Second) // Verify Alice ETH balance. - resBal, err = net.Alice.Client.GetBalance(ht.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) - ht.assert.Equal(uint64(199997900), resBal.Balances["ETH"].TotalBalance) - ht.assert.Equal(resBal.Balances["ETH"].TotalBalance-40000, resBal.Balances["ETH"].WalletBalance) - ht.assert.Equal(uint64(40000), resBal.Balances["ETH"].ChannelBalance) + chanFeesThreshold := uint64(2100) + preSwapAliceBal, err := net.Alice.Client.GetBalance(ht.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) + preSwapAliceEthBal := preSwapAliceBal.Balances["ETH"] + ht.assert.True(preChanAliceEthBal.TotalBalance-preSwapAliceEthBal.TotalBalance <= chanFeesThreshold) + ht.assert.Equal(preSwapAliceEthBal.TotalBalance-preSwapAliceEthBal.ChannelBalance, preSwapAliceEthBal.WalletBalance) + ht.assert.Equal(uint64(40000), preSwapAliceEthBal.ChannelBalance) // wait for 1 block for node to collateralize ETH channel time.Sleep(15 * time.Second) @@ -502,16 +542,19 @@ func testOrderMatchingAndSwapConnext(net *xudtest.NetworkHarness, ht *harnessTes time.Sleep(5 * time.Second) // Verify Alice ETH balance. - resBal, err = net.Alice.Client.GetBalance(ht.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) - ht.assert.Equal(uint64(199993900), resBal.Balances["ETH"].TotalBalance) - ht.assert.Equal(resBal.Balances["ETH"].TotalBalance-36000, resBal.Balances["ETH"].WalletBalance) - ht.assert.Equal(uint64(36000), resBal.Balances["ETH"].ChannelBalance) + amt := uint64(req.Price * float64(req.Quantity)) + aliceBal, err := net.Alice.Client.GetBalance(ht.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) + aliceEthBal := aliceBal.Balances["ETH"] + ht.assert.Equal(preSwapAliceEthBal.TotalBalance-amt, aliceEthBal.TotalBalance) + ht.assert.Equal(aliceEthBal.TotalBalance-aliceEthBal.ChannelBalance, aliceEthBal.WalletBalance) + ht.assert.Equal(preSwapAliceEthBal.ChannelBalance-amt, aliceEthBal.ChannelBalance) // Verify Bob ETH balance. - resBal, err = net.Bob.Client.GetBalance(ht.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) - ht.assert.Equal(uint64(4000), resBal.Balances["ETH"].TotalBalance) - ht.assert.Equal(uint64(0), resBal.Balances["ETH"].WalletBalance) - ht.assert.Equal(uint64(4000), resBal.Balances["ETH"].ChannelBalance) + bobBalance, err := net.Bob.Client.GetBalance(ht.ctx, &xudrpc.GetBalanceRequest{Currency: "ETH"}) + bobEthBalance := bobBalance.Balances["ETH"] + ht.assert.Equal(amt, bobEthBalance.TotalBalance) + ht.assert.Equal(uint64(0), bobEthBalance.WalletBalance) + ht.assert.Equal(amt, bobEthBalance.ChannelBalance) // Cleanup. ht.act.disconnect(net.Alice, net.Bob)