aboutsummaryrefslogtreecommitdiff
path: root/test/functional/wallet_descriptor.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/wallet_descriptor.py')
-rwxr-xr-xtest/functional/wallet_descriptor.py40
1 files changed, 40 insertions, 0 deletions
diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py
index e9321b72e2..cbd3898f92 100755
--- a/test/functional/wallet_descriptor.py
+++ b/test/functional/wallet_descriptor.py
@@ -9,7 +9,10 @@ try:
except ImportError:
pass
+import concurrent.futures
+
from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.descriptors import descsum_create
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -33,6 +36,41 @@ class WalletDescriptorTest(BitcoinTestFramework):
self.skip_if_no_sqlite()
self.skip_if_no_py_sqlite3()
+ def test_concurrent_writes(self):
+ self.log.info("Test sqlite concurrent writes are in the correct order")
+ self.restart_node(0, extra_args=["-unsafesqlitesync=0"])
+ self.nodes[0].createwallet(wallet_name="concurrency", blank=True)
+ wallet = self.nodes[0].get_wallet_rpc("concurrency")
+ # First import a descriptor that uses hardened dervation so that topping up
+ # Will require writing a ton to db
+ wallet.importdescriptors([{"desc":descsum_create("wpkh(tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg/0h/0h/*h)"), "timestamp": "now", "active": True}])
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as thread:
+ topup = thread.submit(wallet.keypoolrefill, newsize=1000)
+
+ # Then while the topup is running, we need to do something that will call
+ # ChainStateFlushed which will trigger a write to the db, hopefully at the
+ # same time that the topup still has an open db transaction.
+ self.nodes[0].cli.gettxoutsetinfo()
+ assert_equal(topup.result(), None)
+
+ wallet.unloadwallet()
+
+ # Check that everything was written
+ wallet_db = self.nodes[0].wallets_path / "concurrency" / self.wallet_data_filename
+ conn = sqlite3.connect(wallet_db)
+ with conn:
+ # Retrieve the bestblock_nomerkle record
+ bestblock_rec = conn.execute("SELECT value FROM main WHERE hex(key) = '1262657374626C6F636B5F6E6F6D65726B6C65'").fetchone()[0]
+ # Retrieve the number of descriptor cache records
+ # Since we store binary data, sqlite's comparison operators don't work everywhere
+ # so just retrieve all records and process them ourselves.
+ db_keys = conn.execute("SELECT key FROM main").fetchall()
+ cache_records = len([k[0] for k in db_keys if b"walletdescriptorcache" in k[0]])
+ conn.close()
+
+ assert_equal(bestblock_rec[5:37][::-1].hex(), self.nodes[0].getbestblockhash())
+ assert_equal(cache_records, 1000)
+
def run_test(self):
if self.is_bdb_compiled():
# Make a legacy wallet and check it is BDB
@@ -240,6 +278,8 @@ class WalletDescriptorTest(BitcoinTestFramework):
conn.close()
assert_raises_rpc_error(-4, "Unexpected legacy entry in descriptor wallet found.", self.nodes[0].loadwallet, "crashme")
+ self.test_concurrent_writes()
+
if __name__ == '__main__':
WalletDescriptorTest().main ()