change(state): Set upper bound when reading from deleting column family tx_loc_by_transparent_addr_loc (#7732)
* Uses range_iter in address_transaction_locations * Uses range_iter in address_transaction_locations * uses u16::MAX instead of usize::MAX * Moves limit code into method * adds allow(dead_code) * Simplifies address_iterator_range * Moves test state init out of loop * Updates docs
This commit is contained in:
parent
fc0133e886
commit
01168c8571
|
|
@ -16,6 +16,7 @@ use zebra_chain::{
|
||||||
};
|
};
|
||||||
use zebra_node_services::BoxError;
|
use zebra_node_services::BoxError;
|
||||||
|
|
||||||
|
use zebra_state::{LatestChainTip, ReadStateService};
|
||||||
use zebra_test::mock_service::MockService;
|
use zebra_test::mock_service::MockService;
|
||||||
|
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
|
|
@ -674,19 +675,36 @@ async fn rpc_getaddresstxids_response() {
|
||||||
.address(network)
|
.address(network)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// Create a populated state service
|
||||||
|
let (_state, read_state, latest_chain_tip, _chain_tip_change) =
|
||||||
|
zebra_state::populated_state(blocks.to_owned(), network).await;
|
||||||
|
|
||||||
if network == Mainnet {
|
if network == Mainnet {
|
||||||
// Exhaustively test possible block ranges for mainnet.
|
// Exhaustively test possible block ranges for mainnet.
|
||||||
//
|
//
|
||||||
// TODO: if it takes too long on slower machines, turn this into a proptest with 10-20 cases
|
// TODO: if it takes too long on slower machines, turn this into a proptest with 10-20 cases
|
||||||
for start in 1..=10 {
|
for start in 1..=10 {
|
||||||
for end in start..=10 {
|
for end in start..=10 {
|
||||||
rpc_getaddresstxids_response_with(network, start..=end, &blocks, &address)
|
rpc_getaddresstxids_response_with(
|
||||||
.await;
|
network,
|
||||||
|
start..=end,
|
||||||
|
&address,
|
||||||
|
&read_state,
|
||||||
|
&latest_chain_tip,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Just test the full range for testnet.
|
// Just test the full range for testnet.
|
||||||
rpc_getaddresstxids_response_with(network, 1..=10, &blocks, &address).await;
|
rpc_getaddresstxids_response_with(
|
||||||
|
network,
|
||||||
|
1..=10,
|
||||||
|
&address,
|
||||||
|
&read_state,
|
||||||
|
&latest_chain_tip,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -694,13 +712,11 @@ async fn rpc_getaddresstxids_response() {
|
||||||
async fn rpc_getaddresstxids_response_with(
|
async fn rpc_getaddresstxids_response_with(
|
||||||
network: Network,
|
network: Network,
|
||||||
range: RangeInclusive<u32>,
|
range: RangeInclusive<u32>,
|
||||||
blocks: &[Arc<Block>],
|
|
||||||
address: &transparent::Address,
|
address: &transparent::Address,
|
||||||
|
read_state: &ReadStateService,
|
||||||
|
latest_chain_tip: &LatestChainTip,
|
||||||
) {
|
) {
|
||||||
let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
|
let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
|
||||||
// Create a populated state service
|
|
||||||
let (_state, read_state, latest_chain_tip, _chain_tip_change) =
|
|
||||||
zebra_state::populated_state(blocks.to_owned(), network).await;
|
|
||||||
|
|
||||||
let (rpc, rpc_tx_queue_task_handle) = RpcImpl::new(
|
let (rpc, rpc_tx_queue_task_handle) = RpcImpl::new(
|
||||||
"RPC test",
|
"RPC test",
|
||||||
|
|
@ -710,7 +726,7 @@ async fn rpc_getaddresstxids_response_with(
|
||||||
true,
|
true,
|
||||||
Buffer::new(mempool.clone(), 1),
|
Buffer::new(mempool.clone(), 1),
|
||||||
Buffer::new(read_state.clone(), 1),
|
Buffer::new(read_state.clone(), 1),
|
||||||
latest_chain_tip,
|
latest_chain_tip.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// call the method with valid arguments
|
// call the method with valid arguments
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,6 @@ pub struct TransactionLocation {
|
||||||
|
|
||||||
impl TransactionLocation {
|
impl TransactionLocation {
|
||||||
/// Creates a transaction location from a block height and transaction index.
|
/// Creates a transaction location from a block height and transaction index.
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn from_index(height: Height, transaction_index: u16) -> TransactionLocation {
|
pub fn from_index(height: Height, transaction_index: u16) -> TransactionLocation {
|
||||||
TransactionLocation {
|
TransactionLocation {
|
||||||
height,
|
height,
|
||||||
|
|
|
||||||
|
|
@ -416,36 +416,37 @@ impl AddressTransaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an [`AddressTransaction`] which starts iteration for the supplied
|
/// Create a range of [`AddressTransaction`]s which starts iteration for the supplied
|
||||||
/// address. Starts at the first UTXO, or at the `query_start` height,
|
/// address. Starts at the first UTXO, or at the `query` start height, whichever is greater.
|
||||||
/// whichever is greater.
|
/// Ends at the maximum possible transaction index for the end height.
|
||||||
///
|
///
|
||||||
/// Used to look up the first transaction with
|
/// Used to look up transactions with [`DiskDb::zs_range_iter`][1].
|
||||||
/// [`ReadDisk::zs_next_key_value_from`][1].
|
|
||||||
///
|
///
|
||||||
/// The transaction location might be invalid, if it is based on the
|
/// The transaction locations in the:
|
||||||
/// `query_start` height. But this is not an issue, since
|
/// - start bound might be invalid, if it is based on the `query` start height.
|
||||||
/// [`ReadDisk::zs_next_key_value_from`][1] will fetch the next existing
|
/// - end bound will always be invalid.
|
||||||
/// (valid) value.
|
|
||||||
///
|
///
|
||||||
/// [1]: super::super::disk_db::ReadDisk::zs_next_key_value_from
|
/// But this is not an issue, since [`DiskDb::zs_range_iter`][1] will fetch all existing
|
||||||
pub fn address_iterator_start(
|
/// (valid) values in the range.
|
||||||
|
///
|
||||||
|
/// [1]: super::super::disk_db::DiskDb
|
||||||
|
pub fn address_iterator_range(
|
||||||
address_location: AddressLocation,
|
address_location: AddressLocation,
|
||||||
query_start: Height,
|
query: std::ops::RangeInclusive<Height>,
|
||||||
) -> AddressTransaction {
|
) -> std::ops::RangeInclusive<AddressTransaction> {
|
||||||
// Iterating from the lowest possible transaction location gets us the first transaction.
|
// Iterating from the lowest possible transaction location gets us the first transaction.
|
||||||
//
|
//
|
||||||
// The address location is the output location of the first UTXO sent to the address,
|
// The address location is the output location of the first UTXO sent to the address,
|
||||||
// and addresses can not spend funds until they receive their first UTXO.
|
// and addresses can not spend funds until they receive their first UTXO.
|
||||||
let first_utxo_location = address_location.transaction_location();
|
let first_utxo_location = address_location.transaction_location();
|
||||||
|
|
||||||
// Iterating from the start height filters out transactions that aren't needed.
|
// Iterating from the start height to the end height filters out transactions that aren't needed.
|
||||||
let query_start_location = TransactionLocation::from_usize(query_start, 0);
|
let query_start_location = TransactionLocation::from_index(*query.start(), 0);
|
||||||
|
let query_end_location = TransactionLocation::from_index(*query.end(), u16::MAX);
|
||||||
|
|
||||||
AddressTransaction {
|
let addr_tx = |tx_loc| AddressTransaction::new(address_location, tx_loc);
|
||||||
address_location,
|
|
||||||
transaction_location: max(first_utxo_location, query_start_location),
|
addr_tx(max(first_utxo_location, query_start_location))..=addr_tx(query_end_location)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the transaction location to the next possible transaction for the
|
/// Update the transaction location to the next possible transaction for the
|
||||||
|
|
@ -457,6 +458,7 @@ impl AddressTransaction {
|
||||||
/// existing (valid) value.
|
/// existing (valid) value.
|
||||||
///
|
///
|
||||||
/// [1]: super::super::disk_db::ReadDisk::zs_next_key_value_from
|
/// [1]: super::super::disk_db::ReadDisk::zs_next_key_value_from
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn address_iterator_next(&mut self) {
|
pub fn address_iterator_next(&mut self) {
|
||||||
// Iterating from the next possible output location gets us the next output,
|
// Iterating from the next possible output location gets us the next output,
|
||||||
// even if it is in a later block or transaction.
|
// even if it is in a later block or transaction.
|
||||||
|
|
|
||||||
|
|
@ -235,44 +235,15 @@ impl ZebraDb {
|
||||||
let tx_loc_by_transparent_addr_loc =
|
let tx_loc_by_transparent_addr_loc =
|
||||||
self.db.cf_handle("tx_loc_by_transparent_addr_loc").unwrap();
|
self.db.cf_handle("tx_loc_by_transparent_addr_loc").unwrap();
|
||||||
|
|
||||||
// Manually fetch the entire addresses' transaction locations
|
|
||||||
let mut addr_transactions = BTreeSet::new();
|
|
||||||
|
|
||||||
// A potentially invalid key representing the first UTXO send to the address,
|
// A potentially invalid key representing the first UTXO send to the address,
|
||||||
// or the query start height.
|
// or the query start height.
|
||||||
let mut transaction_location = AddressTransaction::address_iterator_start(
|
let transaction_location_range =
|
||||||
address_location,
|
AddressTransaction::address_iterator_range(address_location, query_height_range);
|
||||||
*query_height_range.start(),
|
|
||||||
);
|
|
||||||
|
|
||||||
loop {
|
self.db
|
||||||
// Seek to a valid entry for this address, or the first entry for the next address
|
.zs_range_iter(&tx_loc_by_transparent_addr_loc, transaction_location_range)
|
||||||
transaction_location = match self
|
.map(|(tx_loc, ())| tx_loc)
|
||||||
.db
|
.collect()
|
||||||
.zs_next_key_value_from(&tx_loc_by_transparent_addr_loc, &transaction_location)
|
|
||||||
{
|
|
||||||
Some((transaction_location, ())) => transaction_location,
|
|
||||||
// We're finished with the final address in the column family
|
|
||||||
None => break,
|
|
||||||
};
|
|
||||||
|
|
||||||
// We found the next address, so we're finished with this address
|
|
||||||
if transaction_location.address_location() != address_location {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're past the end height, so we're finished with this query
|
|
||||||
if transaction_location.transaction_location().height > *query_height_range.end() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
addr_transactions.insert(transaction_location);
|
|
||||||
|
|
||||||
// A potentially invalid key representing the next possible output
|
|
||||||
transaction_location.address_iterator_next();
|
|
||||||
}
|
|
||||||
|
|
||||||
addr_transactions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address index queries
|
// Address index queries
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue