|
| 1 | +from bitcoin.rpc import RawProxy |
1 | 2 | from collections import OrderedDict |
2 | 3 | from datetime import datetime |
3 | 4 | from fixtures import * # noqa: F401,F403 |
@@ -1991,6 +1992,68 @@ def test_bcli(node_factory, bitcoind, chainparams): |
1991 | 1992 | assert not resp["success"] and "decode failed" in resp["errmsg"] |
1992 | 1993 |
|
1993 | 1994 |
|
| 1995 | +def test_bcli_concurrent(node_factory, bitcoind, executor): |
| 1996 | + """Test bcli under a concurrent load with a retry path.""" |
| 1997 | + retry_count = 5 |
| 1998 | + getblockfrompeer_count = 0 |
| 1999 | + |
| 2000 | + def mock_getblock(r): |
| 2001 | + nonlocal getblockfrompeer_count |
| 2002 | + nonlocal retry_count |
| 2003 | + |
| 2004 | + if getblockfrompeer_count >= retry_count: |
| 2005 | + conf_file = os.path.join(bitcoind.bitcoin_dir, "bitcoin.conf") |
| 2006 | + brpc = RawProxy(btc_conf_file=conf_file) |
| 2007 | + return { |
| 2008 | + "result": brpc._call(r["method"], *r["params"]), |
| 2009 | + "error": None, |
| 2010 | + "id": r["id"] |
| 2011 | + } |
| 2012 | + return { |
| 2013 | + "id": r["id"], |
| 2014 | + "result": None, |
| 2015 | + "error": {"code": -1, "message": "Block not available (pruned data)"} |
| 2016 | + } |
| 2017 | + |
| 2018 | + def mock_getpeerinfo(r): |
| 2019 | + return {"id": r["id"], "result": [{"id": 1, "services": "000000000000040d"}]} |
| 2020 | + |
| 2021 | + def mock_getblockfrompeer(r): |
| 2022 | + nonlocal getblockfrompeer_count |
| 2023 | + getblockfrompeer_count += 1 |
| 2024 | + return {"id": r["id"], "result": {}} |
| 2025 | + |
| 2026 | + l1 = node_factory.get_node(start=False) |
| 2027 | + l1.daemon.rpcproxy.mock_rpc("getblock", mock_getblock) |
| 2028 | + l1.daemon.rpcproxy.mock_rpc("getpeerinfo", mock_getpeerinfo) |
| 2029 | + l1.daemon.rpcproxy.mock_rpc("getblockfrompeer", mock_getblockfrompeer) |
| 2030 | + l1.start(wait_for_bitcoind_sync=False) |
| 2031 | + |
| 2032 | + # Submit concurrent bcli requests, `getrawblockbyheight` hits a retry path |
| 2033 | + block_future = executor.submit(l1.rpc.call, "getrawblockbyheight", {"height": 1}) |
| 2034 | + chaininfo_futures = [] |
| 2035 | + fees_futures = [] |
| 2036 | + for _ in range(5): |
| 2037 | + chaininfo_futures.append(executor.submit(l1.rpc.call, "getchaininfo", {"last_height": 0})) |
| 2038 | + fees_futures.append(executor.submit(l1.rpc.call, "estimatefees")) |
| 2039 | + |
| 2040 | + block_result = block_future.result(TIMEOUT) |
| 2041 | + assert "blockhash" in block_result |
| 2042 | + assert "block" in block_result |
| 2043 | + |
| 2044 | + for fut in chaininfo_futures: |
| 2045 | + result = fut.result(TIMEOUT) |
| 2046 | + assert "chain" in result |
| 2047 | + assert "blockcount" in result |
| 2048 | + |
| 2049 | + for fut in fees_futures: |
| 2050 | + result = fut.result(TIMEOUT) |
| 2051 | + assert "feerates" in result |
| 2052 | + assert "feerate_floor" in result |
| 2053 | + |
| 2054 | + assert getblockfrompeer_count == retry_count, "getblockfrompeer should have been called" |
| 2055 | + |
| 2056 | + |
1994 | 2057 | @unittest.skipIf(TEST_NETWORK != 'regtest', 'p2tr addresses not supported by elementsd') |
1995 | 2058 | def test_hook_crash(node_factory, executor, bitcoind): |
1996 | 2059 | """Verify that we fail over if a plugin crashes while handling a hook. |
|
0 commit comments