Skip to content

Commit d4356d4

Browse files
committed
rpc: Block until synced if coinstatsindex is used in gettxoutsetinfo
During initial sync after startup the gettxoutsetinfo RPC will still return an error while catching up. However, after the initial sync the index will not error immediately anymore when it's in the process of syncing to the tip while being called. Instead it will block until synced and then return the response.
1 parent a5f6791 commit d4356d4

File tree

2 files changed

+13
-20
lines changed

2 files changed

+13
-20
lines changed

src/rpc/blockchain.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,18 @@ static RPCHelpMan gettxoutsetinfo()
11751175
pindex = ParseHashOrHeight(request.params[1], chainman);
11761176
}
11771177

1178+
if (stats.index_requested && g_coin_stats_index) {
1179+
if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) {
1180+
const IndexSummary summary{g_coin_stats_index->GetSummary()};
1181+
1182+
// If a specific block was requested and the index has already synced past that height, we can return the
1183+
// data already even though the index is not fully synced yet.
1184+
if (pindex->nHeight > summary.best_block_height) {
1185+
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to get data because coinstatsindex is still syncing. Current height: %d", summary.best_block_height));
1186+
}
1187+
}
1188+
}
1189+
11781190
if (GetUTXOStats(coins_view, *blockman, stats, node.rpc_interruption_point, pindex)) {
11791191
ret.pushKV("height", (int64_t)stats.nHeight);
11801192
ret.pushKV("bestblock", stats.hashBlock.GetHex());
@@ -1215,13 +1227,6 @@ static RPCHelpMan gettxoutsetinfo()
12151227
ret.pushKV("block_info", block_info);
12161228
}
12171229
} else {
1218-
if (g_coin_stats_index) {
1219-
const IndexSummary summary{g_coin_stats_index->GetSummary()};
1220-
1221-
if (!summary.synced) {
1222-
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to read UTXO set because coinstatsindex is still syncing. Current height: %d", summary.best_block_height));
1223-
}
1224-
}
12251230
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
12261231
}
12271232
return ret;

test/functional/feature_coinstatsindex.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
from test_framework.util import (
3333
assert_equal,
3434
assert_raises_rpc_error,
35-
try_rpc,
3635
)
3736

3837
class CoinStatsIndexTest(BitcoinTestFramework):
@@ -76,13 +75,11 @@ def _test_coin_stats_index(self):
7675
self.sync_blocks(timeout=120)
7776

7877
self.log.info("Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option")
79-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", node.gettxoutsetinfo))
8078
res0 = node.gettxoutsetinfo('none')
8179

8280
# The fields 'disk_size' and 'transactions' do not exist on the index
8381
del res0['disk_size'], res0['transactions']
8482

85-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
8683
for hash_option in index_hash_options:
8784
res1 = index_node.gettxoutsetinfo(hash_option)
8885
# The fields 'block_info' and 'total_unspendable_amount' only exist on the index
@@ -97,7 +94,6 @@ def _test_coin_stats_index(self):
9794
# Generate a new tip
9895
node.generate(5)
9996

100-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
10197
for hash_option in index_hash_options:
10298
# Fetch old stats by height
10399
res2 = index_node.gettxoutsetinfo(hash_option, 102)
@@ -176,7 +172,6 @@ def _test_coin_stats_index(self):
176172
self.nodes[0].generate(1)
177173
self.sync_all()
178174

179-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
180175
for hash_option in index_hash_options:
181176
# Check all amounts were registered correctly
182177
res6 = index_node.gettxoutsetinfo(hash_option, 108)
@@ -209,7 +204,6 @@ def _test_coin_stats_index(self):
209204
self.nodes[0].submitblock(ToHex(block))
210205
self.sync_all()
211206

212-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
213207
for hash_option in index_hash_options:
214208
res7 = index_node.gettxoutsetinfo(hash_option, 109)
215209
assert_equal(res7['total_unspendable_amount'], Decimal('80.98999999'))
@@ -235,7 +229,6 @@ def _test_coin_stats_index(self):
235229
assert_equal(res8, res9)
236230

237231
index_node.generate(1)
238-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
239232
res10 = index_node.gettxoutsetinfo('muhash')
240233
assert(res8['txouts'] < res10['txouts'])
241234

@@ -256,14 +249,12 @@ def _test_reorg_index(self):
256249
index_node = self.nodes[1]
257250
reorg_blocks = index_node.generatetoaddress(2, index_node.getnewaddress())
258251
reorg_block = reorg_blocks[1]
259-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
260252
res_invalid = index_node.gettxoutsetinfo('muhash')
261253
index_node.invalidateblock(reorg_blocks[0])
262254
assert_equal(index_node.gettxoutsetinfo('muhash')['height'], 110)
263255

264256
# Add two new blocks
265257
block = index_node.generate(2)[1]
266-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
267258
res = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=False)
268259

269260
# Test that the result of the reorged block is not returned for its old block height
@@ -284,9 +275,7 @@ def _test_reorg_index(self):
284275
# Ensure that removing and re-adding blocks yields consistent results
285276
block = index_node.getblockhash(99)
286277
index_node.invalidateblock(block)
287-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
288278
index_node.reconsiderblock(block)
289-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", index_node.gettxoutsetinfo, 'muhash'))
290279
res3 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=112)
291280
assert_equal(res2, res3)
292281

@@ -296,8 +285,7 @@ def _test_reorg_index(self):
296285
node.getblock(reorg_block)
297286

298287
self.restart_node(0, ["-coinstatsindex"])
299-
self.wait_until(lambda: not try_rpc(-32603, "Unable to read UTXO set", node.gettxoutsetinfo, 'muhash'))
300-
assert_raises_rpc_error(-32603, "Unable to read UTXO set", node.gettxoutsetinfo, 'muhash', reorg_block)
288+
assert_raises_rpc_error(-32603, "Unable to get data because coinstatsindex is still syncing.", node.gettxoutsetinfo, 'muhash', reorg_block)
301289

302290
def _test_index_rejects_hash_serialized(self):
303291
self.log.info("Test that the rpc raises if the legacy hash is passed with the index")

0 commit comments

Comments
 (0)