diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..24171656 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +language: python +python: + - "2.7_with_system_site_packages" +before_install: + - sudo apt-add-repository ppa:bitcoin/bitcoin -y + - sudo apt-get update -q + - sudo apt-get install --no-install-recommends --no-upgrade -qq bitcoind + - sudo apt-get install -y build-essential + - sudo apt-get install -y automake +install: + - pip install numpy + - pip install pexpect +script: + - git clone git://github.com/jedisct1/libsodium.git + - cd libsodium + - git checkout tags/1.0.3 + - ./autogen.sh + - ./configure + - make check + - sudo make install + - cd .. +#set up joinmarket.cfg + - cp test/regtest_joinmarket.cfg joinmarket.cfg +#E2E encryption (libnacl) tests + - python lib/enc_wrapper.py +#start bitcoin regtest daemon and 101 blocks + - mkdir /home/travis/.bitcoin + - cp test/bitcoin.conf /home/travis/.bitcoin/. + - chmod 600 /home/travis/.bitcoin/bitcoin.conf + - bitcoind -regtest -daemon + - sleep 5 + - bitcoin-cli -regtest setgenerate true 101 + - cd test + - python regtest.py + - python wallet-test.py +branches: + only: + - master diff --git a/lib/enc_wrapper.py b/lib/enc_wrapper.py index bc346944..d251a25e 100644 --- a/lib/enc_wrapper.py +++ b/lib/enc_wrapper.py @@ -69,7 +69,9 @@ def decode_decrypt(msg, box): return box.decrypt(decoded) def test_case(case_name, alice_box, bob_box, ab_message, ba_message, num_iterations=1): - for i in range(num_iterations): + for i in range(num_iterations): + ab_message = ''.join(random.choice(string.ascii_letters) for x in range(100)) if ab_message == 'rand' else ab_message + ba_message = ''.join(random.choice(string.ascii_letters) for x in range(100)) if ba_message == 'rand' else ba_message otw_amsg = alice_box.encrypt(ab_message) bob_ptext = bob_box.decrypt(otw_amsg) assert bob_ptext == ab_message, "Encryption test: FAILED. Alice sent: "\ @@ -82,8 +84,7 @@ def test_case(case_name, alice_box, bob_box, ab_message, ba_message, num_iterati print "Encryption test PASSED for case: "+case_name -#to test the encryption functionality -if __name__ == "__main__": +def test_keypair_setup(): alice_kp = init_keypair() bob_kp = init_keypair() @@ -98,13 +99,25 @@ def test_case(case_name, alice_box, bob_box, ab_message, ba_message, num_iterati #now Alice and Bob can use their 'box' #constructs (both of which utilise the same - #shared secret) to perform encryption/decryption + #shared secret) to perform encryption/decryption + #to test the encryption functionality + return (alice_box, bob_box) + +if __name__ == "__main__": + alice_box, bob_box = test_keypair_setup() test_case("short ascii", alice_box, bob_box,"Attack at dawn","Not tonight Josephine!",5) import base64, string, random + alice_box, bob_box = test_keypair_setup() longb641 = base64.b64encode(''.join(random.choice(string.ascii_letters) for x in range(5000))) longb642 = base64.b64encode(''.join(random.choice(string.ascii_letters) for x in range(5000))) test_case("long b64", alice_box, bob_box, longb641, longb642,5) - + #test a large number of messages on the same connection + alice_box, bob_box = test_keypair_setup() + test_case("endless_wittering", alice_box, bob_box,'rand','rand',40000) + #edge cases + #1 character + alice_box, bob_box = test_keypair_setup() + test_case("1 char",alice_box, bob_box,'\x00','\x00',5) print "All test cases passed - encryption and decryption should work correctly." diff --git a/test/bitcoin.conf b/test/bitcoin.conf new file mode 100644 index 00000000..ca22075f --- /dev/null +++ b/test/bitcoin.conf @@ -0,0 +1,5 @@ +rpcuser=bitcoinrpc +rpcpassword=123456abcdef +walletnotify=/usr/bin/wget -q --spider --timeout=0.5 --tries=1 http://localhost:62612/walletnotify?%s +alertnotify=/usr/bin/wget -q --spider --timeout=0.5 --tries=1 http://localhost:62612/alertnotify?%s + diff --git a/regtest.py b/test/regtest.py similarity index 98% rename from regtest.py rename to test/regtest.py index 30bb5664..222096f6 100644 --- a/regtest.py +++ b/test/regtest.py @@ -1,6 +1,6 @@ import sys import os, time -data_dir = os.path.dirname(os.path.realpath(__file__)) +data_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) sys.path.insert(0, os.path.join(data_dir, 'lib')) import subprocess import unittest @@ -173,10 +173,8 @@ def run_nparty_join(self): def main(): + os.chdir(data_dir) common.load_program_config() - if not common.bc_interface: - print 'not there' - exit() unittest.main() if __name__ == '__main__': diff --git a/test/regtest_joinmarket.cfg b/test/regtest_joinmarket.cfg new file mode 100644 index 00000000..55621321 --- /dev/null +++ b/test/regtest_joinmarket.cfg @@ -0,0 +1,14 @@ +[BLOCKCHAIN] +blockchain_source = regtest +network = testnet +bitcoin_cli_cmd = bitcoin-cli +notify_port = 62612 +[MESSAGING] +host = chat.freenode.net +channel = joinmarket-pit +port = 6667 +usessl = false +socks5 = false +socks5_host = localhost +socks5_port = 9150 + diff --git a/test/wallet-test.py b/test/wallet-test.py new file mode 100644 index 00000000..56682bd1 --- /dev/null +++ b/test/wallet-test.py @@ -0,0 +1,103 @@ +import sys +import os, time +data_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +sys.path.insert(0, os.path.join(data_dir, 'lib')) +import subprocess +import unittest +import common +from blockchaininterface import * +import bitcoin as btc +import binascii +import pexpect + +def interact(process, inputs, expected): + if len(inputs) != len(expected): + raise Exception("Invalid inputs to interact()") + for i, inp in enumerate(inputs): + process.expect(expected[i]) + process.sendline(inp) + + +class TestWalletCreation(unittest.TestCase): + + def test_generate(self): + print 'wallet generation and encryption password tests' + #testing a variety of passwords + self.failUnless(self.run_generate('abc123')) + self.failUnless(self.run_generate('dddddddddddddddddddddddddddddddddddddddddddd')) + #null password is accepted + self.failUnless(self.run_generate('')) + #binary password is accepted; good luck with that! + self.failUnless(self.run_generate('\x01'*10)) + #password with NULL bytes is *not* accepted + self.failIf(self.run_generate('\x00'*10)) + + def run_generate(self, pwd): + try: + test_in = [pwd,pwd,'testwallet.json'] + expected = ['Enter wallet encryption passphrase:', + 'Reenter wallet encryption passphrase:', + 'Input wallet file name'] + testlog = open('test/testlog-'+pwd, 'wb') + p = pexpect.spawn('python wallet-tool.py generate', logfile=testlog) + interact(p, test_in, expected) + p.expect('saved to') + #time.sleep(2) + p.close() + testlog.close() + #anything to check in the log? + if p.exitstatus != 0: + print 'failed due to exit status: '+str(p.exitstatus) + return False + #check the wallet exists (and contains appropriate json?) + if not os.path.isfile('wallets/testwallet.json'): + print 'failed due to wallet missing' + return False + os.remove('wallets/testwallet.json') + except: + return False + return True + + +class TestWalletRecovery(unittest.TestCase): + + def setUp(self): + self.testseed = 'earth gentle mouth circle despite pocket adore student board dress blanket worthless' + + def test_recover(self): + print 'wallet recovery from seed test' + self.failUnless(self.run_recover(self.testseed)) + #try using an invalid word list; can add more variants + wrongseed = 'oops '+self.testseed + self.failIf(self.run_recover(wrongseed)) + + def run_recover(self, seed): + try: + testlog = open('test_recover','wb') + p = pexpect.spawn('python wallet-tool.py recover', logfile = testlog) + expected = ['Input 12 word recovery seed', + 'Enter wallet encryption passphrase:', + 'Reenter wallet encryption passphrase:', + 'Input wallet file name'] + test_in = [seed, 'abc123', 'abc123', 'test_recover_wallet.json'] + interact(p, test_in, expected) + p.expect('saved to') + p.close() + testlog.close() + #anything to check in the log? + if p.exitstatus != 0: + print 'failed due to exit status: '+str(p.exitstatus) + return False + #check the wallet exists (and contains appropriate json? todo) + if not os.path.isfile('wallets/test_recover_wallet.json'): + print 'failed due to wallet missing' + return False + os.remove('wallets/test_recover_wallet.json') + except: + return False + return True + +if __name__ == '__main__': + os.chdir(data_dir) + common.load_program_config() + unittest.main() \ No newline at end of file