With almost 2k transaction per block, it will take a lot of queries per block.
The getblock
RPC supports a verbosity=2
argument which returns all the transactions in the block as JSON objects, so you can make do with a single query per block.
With RPC batching (i.e. sending multiple commands in a single request), you can do even better! You can query the transactions of n
blocks with just 2 RPC requests: one to get all the block hashes, and one to get the blocks (with transactions). The below code snippet shows how you can implement both approaches, and includes a simple performance benchmark. On my machine, the batch approach is ~13x faster when querying the first 2000 blocks.
An example Python implementation, which should work out of the box if:
requests
is installed (pip3 install requests
)
bitcoind -signet -rpcuser=user -rpcpassword=pass
import json
import time
from typing import List
import requests
def get_n_blockhashes(n: int, start_height: int = 0):
data = [{
"method": "getblockhash",
"params": [height]
} for height in range(start_height, start_height + n)]
hashes = [item["result"] for item in make_request(data)]
return hashes
def get_block_transactions_single(block_hashes: List[str]):
transactions = []
for block_hash in block_hashes:
data = {
"method": "getblock",
"params": [block_hash, 2]
}
block_data = make_request(data)["result"]
transactions.append(block_data["tx"])
return transactions
def get_block_transactions_batch(block_hashes: List[str]):
data = [
{
"method": "getblock",
"params": [block_hash, 2]
} for block_hash in block_hashes
]
transactions = [item["result"]["tx"] for item in make_request(data)]
return transactions
def make_request(data):
url = "http://user:[email protected]:38332/"
r = requests.post(url, data=json.dumps(data))
assert r.status_code == 200
return r.json()
def time_function(fn, *args: str, **kwargs) -> float:
"""Return average fn time execution and check that the last obtained blockheader hash matches last_hash_check """
iters = 5
start = time.perf_counter()
for i in range(iters):
fn(*args, **kwargs)
avg_duration = (time.perf_counter() - start) / iters
return avg_duration
if __name__ == '__main__':
block_hashes = get_n_blockhashes(2000)
print(f"single: {time_function(get_block_transactions_single, block_hashes):.4f}s")
print(f"batch: {time_function(get_block_transactions_batch, block_hashes):.4f}s")
blocks
folder or parse the blk*.dat files, calculate the TxId for each encountered transaction and write my own index of file and offset to block containing transaction. I think I would find the second idea easier and faster to implement.