diff --git a/test/lit_test_framework.py b/test/lit_test_framework.py index d6da52a8d..fd7abfa2b 100755 --- a/test/lit_test_framework.py +++ b/test/lit_test_framework.py @@ -6,6 +6,7 @@ import subprocess import sys import tempfile +import time import traceback class LitTest(): @@ -49,5 +50,17 @@ def cleanup(self): except subprocess.TimeoutExpired: litnode.process.kill() -if __name__ == "__main__": - exit(LitTest().main()) +def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf')): + if attempts == float('inf') and timeout == float('inf'): + timeout = 60 + attempt = 0 + elapsed = 0 + + while attempt < attempts and elapsed < timeout: + if predicate(): + return True + attempt += 1 + elapsed += 0.05 + time.sleep(0.05) + + raise AssertionError("wait_until() timed out") diff --git a/test/litnode.py b/test/litnode.py index 86311d26d..8ab2534e3 100755 --- a/test/litnode.py +++ b/test/litnode.py @@ -36,3 +36,11 @@ def add_rpc_connection(self, ip, port): def __getattr__(self, name): return self.rpc.__getattr__(name) + + def get_balance(self, coin_type): + # convenience method for grabbing the node balance + balances = self.rpc.Balance()['result']['Balances'] + for balance in balances: + if balance['CoinType'] == coin_type: + return balance['TxoTotal'] + raise AssertionError("No balance for coin_type %s" % coin_type) diff --git a/test/test_basic.py b/test/test_basic.py index c37079fcd..492f51dc1 100755 --- a/test/test_basic.py +++ b/test/test_basic.py @@ -12,12 +12,18 @@ - listen on litnode0 - connect from litnode1 to litnode0 - send funds from bitcoind process to litnode0 address +- open channel between litnode0 and litnode1 +- push funds from litnode0 to litnode1 +- push funds back +- close channel co-operatively - stop""" import time from bcnode import BCNode from litnode import LitNode -from lit_test_framework import LitTest +from lit_test_framework import LitTest, wait_until + +BC_REGTEST = 257 class TestBasic(LitTest): def run_test(self): @@ -25,7 +31,12 @@ def run_test(self): # Start a bitcoind node self.bcnodes = [BCNode(0, self.tmpdir)] self.bcnodes[0].start_node() - print("generate response: %s" % self.bcnodes[0].generate(nblocks=150).text) + print("generating 500 blocks") + self.bcnodes[0].generate(nblocks=500) + # We need segwit to have activated + network_info = self.bcnodes[0].getblockchaininfo().json()['result'] + print(network_info) + assert network_info['bip9_softforks']['segwit']['status'] == 'active' # Start lit node 0 and open websocket connection self.litnodes.append(LitNode(0, self.tmpdir)) @@ -58,24 +69,95 @@ def run_test(self): assert len(self.litnodes[1].ListConnections()['result']['Connections']) == 1 # Send funds from the bitcoin node to litnode0 - print(self.litnodes[0].Balance()['result']) - bal = self.litnodes[0].Balance()['result']['Balances'][0]['TxoTotal'] - print("previous bal: " + str(bal)) + balance = self.litnodes[0].get_balance(BC_REGTEST) + print("previous bal: " + str(balance)) addr = self.litnodes[0].rpc.new_address() self.bcnodes[0].sendtoaddress(address=addr["result"]["LegacyAddresses"][0], amount=12.34) - print("generate response: %s" % self.bcnodes[0].generate(nblocks=1).text) + print("New block mined: %s" % self.bcnodes[0].generate(nblocks=1).text) print("waiting to receive transaction") - # wait for transaction to be received (5 seconds timeout) - for i in range(50): - time.sleep(0.1) - balNew = self.litnodes[0].Balance()['result']["Balances"][0]["TxoTotal"] - if balNew - bal == 1234000000: - print("Transaction received. Current balance = %s" % balNew) - break - else: - print("Test failed. No transaction received") - raise AssertionError + # Wait for transaction to be received (15 seconds timeout) + wait_until(lambda: self.litnodes[0].get_balance(BC_REGTEST) - balance == 1234000000) + balance = self.litnodes[0].get_balance(BC_REGTEST) + + # Send money from litnode0 to its own segwit address and confirm + print("sending 1000000000 satoshis to litnode1 address") + self.bcnodes[0].generate(nblocks=1) + self.litnodes[0].Send(DestAddrs=[self.litnodes[0].Address()['result']['WitAddresses'][0]], Amts=[1000000000]) + self.bcnodes[0].generate(nblocks=1) + wait_until(lambda: self.litnodes[0].Balance()['result']["Balances"][0]["SyncHeight"] == 503) + # We'll lose some money to fees. Hopefully not too much! + assert balance - self.litnodes[0].get_balance(BC_REGTEST) < 50000 + balance = self.litnodes[0].get_balance(BC_REGTEST) + print("New balance: %s" % balance) + + # Now let's open some channels! + print("Opening channel from litnode0 to litnode1") + assert self.litnodes[0].ChannelList()['result']['Channels'] == [] + assert self.litnodes[1].ChannelList()['result']['Channels'] == [] + + resp = self.litnodes[0].FundChannel(Peer=1, CoinType=257, Capacity=1000000000) + print("FundChannel response: %s" % resp) + + # Wait for channel to open + wait_until(lambda: len(self.litnodes[0].ChannelList()['result']['Channels']) > 0) + + assert abs(balance - self.litnodes[0].get_balance(BC_REGTEST) - 1000000000) < 50000 + balance = self.litnodes[0].get_balance(BC_REGTEST) + print("New balance: %s" % balance) + + litnode0_channel = self.litnodes[0].ChannelList()['result']['Channels'][0] + litnode1_channel = self.litnodes[1].ChannelList()['result']['Channels'][0] + print(litnode0_channel) + print(litnode1_channel) + + assert litnode0_channel['Capacity'] == 1000000000 + assert litnode0_channel['StateNum'] == 0 + assert not litnode0_channel['Closed'] + assert litnode0_channel['MyBalance'] == 1000000000 + + assert litnode1_channel['Capacity'] == 1000000000 + assert litnode1_channel['StateNum'] == 0 + assert not litnode1_channel['Closed'] + assert litnode1_channel['MyBalance'] == 0 + + print("Now push some funds from litnode0 to litnode1") + + resp = self.litnodes[0].Push(ChanIdx=1, Amt=100000000) + print("Push response: %s" % resp) + + litnode0_channel = self.litnodes[0].ChannelList()['result']['Channels'][0] + litnode1_channel = self.litnodes[1].ChannelList()['result']['Channels'][0] + print(litnode0_channel) + print(litnode1_channel) + + assert litnode0_channel['MyBalance'] == 900000000 + assert litnode1_channel['MyBalance'] == 100000000 + + print("push some funds back") + + resp = self.litnodes[1].Push(ChanIdx=1, Amt=50000000) + print("Push response: %s" % resp) + + litnode0_channel = self.litnodes[0].ChannelList()['result']['Channels'][0] + litnode1_channel = self.litnodes[1].ChannelList()['result']['Channels'][0] + print(litnode0_channel) + print(litnode1_channel) + + assert litnode0_channel['MyBalance'] == 950000000 + assert litnode1_channel['MyBalance'] == 50000000 + + print("Close channel") + resp = self.litnodes[0].CloseChannel(ChanIdx=1) + print("Close channel response: %s" % resp) + self.bcnodes[0].generate(nblocks=10) + wait_until(lambda: self.litnodes[0].Balance()['result']["Balances"][0]["SyncHeight"] == 513) + + assert abs(self.litnodes[1].get_balance(BC_REGTEST) - 50000000) < 50000 + + assert abs(balance + 950000000 - self.litnodes[0].get_balance(BC_REGTEST)) < 50000 + balance = self.litnodes[0].get_balance(BC_REGTEST) + print("New balance: %s" % balance) if __name__ == "__main__": exit(TestBasic().main())