Initial pruning commits the pruning seed at its first checkpoint. If it is interrupted after that point, startup selects incremental pruning and leaves the historical scan unfinished.
Store the last processed transaction hash in the same transaction as each checkpoint. On restart, prune newly matured tip records, continue after the marker, and clear it with the final commit.
Databases interrupted by older binaries have no marker and still require the existing manual prune_blockchain command.
Use the file size to distinguish a clean EOF from an incomplete trailing chunk before resuming an export
Truncate only that incomplete tail and keep malformed chunks or I/O failures non-destructive
estimate_rct_tx_size() and estimate_tx_weight() compute the Bulletproof padded
output count with `while ((1<<log_padded_outputs) < n_outputs)`, shifting a
signed int. For n_outputs > 2^30 this reaches `1 << 31` (signed overflow, UB),
and once the shift count exceeds the int width the value cycles and never
reaches n_outputs, so the loop never terminates. estimate_tx_size_and_weight()
accepts n_outputs up to INT_MAX (only negative is rejected), so a single
wallet-rpc call spins the handling thread forever.
Shift an unsigned 64-bit one and compare in uint64_t at all three shift sites,
and accumulate the estimated size in size_t (cast the per-input and per-output
terms) so the size arithmetic cannot overflow for large counts.
The <cctype> functions require their argument to be representable as
unsigned char or to equal EOF. char is signed on the platforms Monero
targets, so every byte >= 0x80 reaches them negative, which is undefined
behaviour.
All six call sites take bytes that come from outside the process:
hex_to_dec_2bytes() percent-escapes in a payment URI
http_client.h the status line of an HTTP response
updates.cpp the hash field of a DNS update record
glibc and the macOS libc happen to tolerate -128..-1 because their tables
carry padding below zero, so this is quiet in practice on those, but it is
still out of contract and other libcs index without that cushion.
Cast to unsigned char at each call. The conversion is behaviour-preserving
for the ASCII these actually test: for a byte such as 0xC3, toupper()
returns it unchanged in the C locale, it narrows back to char, memchr()
misses it in the hex table, and hex_to_dec_2bytes() falls through to the
existing literal "%XY" output exactly as before.
epee::misc_utils::parse::isdigit/isalpha/isspace are separate char-taking
helpers and are already well defined, so they are untouched, as is
is_base64(), which already takes unsigned char.
In RPC calls `get_blocks.bin` and `get_transactions`:
1. Skip deserializing then re-serializing tx blobs
2. Provide full unpruned transaction from mempool in `get_transactions` when `!req.prune`
3. Change `on_get_transactions()` response sort time from O(N^2) to O(N)
4. Generally improve memory usage and lookups in `on_get_transactions()`
Reviewed-by: selsta <selsta@sent.at>
Reviewed-by: j-berman <justinberman@protonmail.com>
Reviewed-by: jpk68 <jpk68@tutanota.com>