test_bitcoin.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import os
  2. import time
  3. import infra.basetest
  4. class TestBitcoin(infra.basetest.BRTest):
  5. # infra.basetest.BASIC_TOOLCHAIN_CONFIG cannot be used as it does
  6. # not include BR2_TOOLCHAIN_SUPPORTS_ALWAYS_LOCKFREE_ATOMIC_INTS
  7. # needed by bitcoin. This config also uses an ext4 rootfs as
  8. # bitcoind needs some free disk space to start (so we avoid having
  9. # a larger initrd in RAM).
  10. config = \
  11. """
  12. BR2_aarch64=y
  13. BR2_TOOLCHAIN_EXTERNAL=y
  14. BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
  15. BR2_LINUX_KERNEL=y
  16. BR2_LINUX_KERNEL_CUSTOM_VERSION=y
  17. BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.1.81"
  18. BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
  19. BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
  20. BR2_PACKAGE_BITCOIN=y
  21. BR2_PACKAGE_BITCOIN_WALLET=y
  22. BR2_TARGET_ROOTFS_EXT2=y
  23. BR2_TARGET_ROOTFS_EXT2_4=y
  24. BR2_TARGET_ROOTFS_EXT2_SIZE="256M"
  25. # BR2_TARGET_ROOTFS_TAR is not set
  26. """
  27. # Command prefix for the bitcoin command line interface.
  28. cli_cmd = "bitcoin-cli -regtest"
  29. def create_btc_wallet(self, wallet_name):
  30. """Create an empty wallet."""
  31. cmd = f"{self.cli_cmd} -named createwallet wallet_name={wallet_name}"
  32. self.assertRunOk(cmd)
  33. def gen_btc_address(self, wallet_name):
  34. """Generate an address in a wallet."""
  35. cmd = f"{self.cli_cmd} -rpcwallet={wallet_name} getnewaddress"
  36. out, ret = self.emulator.run(cmd)
  37. self.assertEqual(ret, 0)
  38. return out[0]
  39. def init_wallet(self, wallet_name):
  40. """Create a wallet and generate an address in it."""
  41. self.create_btc_wallet(wallet_name)
  42. return self.gen_btc_address(wallet_name)
  43. def get_wallet_balance(self, wallet):
  44. """Return the (confirmed) balance of a wallet."""
  45. cmd = f"{self.cli_cmd} -rpcwallet={wallet} getbalance"
  46. out, ret = self.emulator.run(cmd)
  47. self.assertEqual(ret, 0)
  48. return float(out[0])
  49. def get_wallet_unconfirmed_balance(self, wallet):
  50. """Return the unconfirmed balance of a wallet."""
  51. cmd = f"{self.cli_cmd} -rpcwallet={wallet} getunconfirmedbalance"
  52. out, ret = self.emulator.run(cmd)
  53. self.assertEqual(ret, 0)
  54. return float(out[0])
  55. def get_block_count(self):
  56. """Returns the height of the most-work fully-validated chain."""
  57. cmd = f"{self.cli_cmd} getblockcount"
  58. out, ret = self.emulator.run(cmd)
  59. self.assertEqual(ret, 0)
  60. return int(out[0])
  61. def test_run(self):
  62. drive = os.path.join(self.builddir, "images", "rootfs.ext4")
  63. kern = os.path.join(self.builddir, "images", "Image")
  64. self.emulator.boot(arch="aarch64",
  65. kernel=kern,
  66. kernel_cmdline=["root=/dev/vda console=ttyAMA0"],
  67. options=["-M", "virt",
  68. "-cpu", "cortex-a53",
  69. "-m", "256M",
  70. "-drive", f"file={drive},if=virtio,format=raw"])
  71. self.emulator.login()
  72. # Values for the test.
  73. wallet1 = "AliceWallet"
  74. wallet2 = "BobWallet"
  75. btc_test_amount = 10
  76. btc_fee = 0.00001
  77. req_blk_count = 101
  78. # Check the binary can execute.
  79. self.assertRunOk("bitcoind --version")
  80. # This cleanup is useful when run-test -k is used. It makes
  81. # this test idempotent. Since the drive storage is preserved
  82. # between reboots, this cleanup will make sure the test always
  83. # starts from a clean state.
  84. cmd = "rm -rf ~/.bitcoin"
  85. self.assertRunOk(cmd)
  86. # The bitcoin daemon is not started. A client ping is expected
  87. # to fail.
  88. ping_cmd = f"{self.cli_cmd} ping"
  89. _, ret = self.emulator.run(ping_cmd)
  90. self.assertNotEqual(ret, 0)
  91. # Start the daemon.
  92. cmd = f"bitcoind -regtest -daemonwait -fallbackfee={btc_fee:f}"
  93. self.assertRunOk(cmd)
  94. time.sleep(2 * self.timeout_multiplier)
  95. # Now the daemon is started, the ping is expected to succeed.
  96. self.assertRunOk(ping_cmd)
  97. # We create two wallets and addresses.
  98. btc_addr1 = self.init_wallet(wallet1)
  99. btc_addr2 = self.init_wallet(wallet2)
  100. # Since the regression test block chain is at its genesis
  101. # block, we expect a height of zero.
  102. cur_blk_cnt = self.get_block_count()
  103. self.assertEqual(cur_blk_cnt, 0)
  104. # We also expect our wallets to be empty.
  105. for wallet in [wallet1, wallet2]:
  106. balance = self.get_wallet_balance(wallet)
  107. self.assertAlmostEqual(balance, 0.0)
  108. # We request the generation of several blocks for address
  109. # #1. We should receive the 50 BTC reward at this address.
  110. cmd = self.cli_cmd
  111. cmd += f" generatetoaddress {req_blk_count} {btc_addr1}"
  112. self.assertRunOk(cmd)
  113. # We should now see the previously created blocks.
  114. cur_blk_cnt = self.get_block_count()
  115. self.assertEqual(cur_blk_cnt, req_blk_count)
  116. # We should also see the 50 BTC reward in the wallet #1.
  117. balance = self.get_wallet_balance(wallet1)
  118. self.assertAlmostEqual(balance, 50.0)
  119. # The wallet #2 should still be empty.
  120. balance = self.get_wallet_balance(wallet2)
  121. self.assertAlmostEqual(balance, 0.0)
  122. # We send an amount from wallet #1 to #2.
  123. cmd = f"{self.cli_cmd} -rpcwallet={wallet1}"
  124. cmd += f" sendtoaddress {btc_addr2} {btc_test_amount}"
  125. self.assertRunOk(cmd)
  126. # The wallet #1 balance is expected to be subtracted by the
  127. # spent amount and the transaction fees.
  128. expected_balance = 50 - btc_test_amount - btc_fee
  129. balance = self.get_wallet_balance(wallet1)
  130. self.assertAlmostEqual(balance, expected_balance, places=4)
  131. # The transaction is sent, but not confirmed yet. So we should
  132. # still see a (confirmed) balance of zero.
  133. balance = self.get_wallet_balance(wallet2)
  134. self.assertAlmostEqual(balance, 0.0)
  135. # We should see the transferred amount in the unconfirmed
  136. # balance.
  137. balance = self.get_wallet_unconfirmed_balance(wallet2)
  138. self.assertAlmostEqual(balance, btc_test_amount)
  139. # We generate 1 block to address #2. This action will confirm
  140. # the previous transaction (but this will not give the 50 BTC
  141. # reward).
  142. cmd = f"{self.cli_cmd} generatetoaddress 1 {btc_addr2}"
  143. self.assertRunOk(cmd)
  144. # We should see one more block.
  145. cur_blk_cnt = self.get_block_count()
  146. self.assertEqual(cur_blk_cnt, req_blk_count + 1)
  147. # We should now see the amount in the confirmed balance.
  148. balance = self.get_wallet_balance(wallet2)
  149. self.assertAlmostEqual(balance, btc_test_amount)
  150. # The unconfirmed balance should now be zero.
  151. balance = self.get_wallet_unconfirmed_balance(wallet2)
  152. self.assertAlmostEqual(balance, 0.0)