WalletDescriptor stores the descriptor range in int32_t fields, with range_end
exclusive. ProcessDescriptorImport computes that end in int64_t and passes it to the
constructor with no bound in between. ParseDescriptorRange accepts an inclusive
endpoint of 2^31 - 1, so the exclusive end can be 2^31. That does not fit in
int32_t and truncates to INT32_MIN, which leaves the descriptor with an inverted
range. The node then aborts while filling the keypool:
$ bitcoin-cli -named createwallet wallet_name=w disable_private_keys=true
$ bitcoin-cli -rpcwallet=w importdescriptors '[{"desc":"wpkh([728986fc/84h/1h/0h]tpubDDLEJ5Q3Tu8hn4BBEn5bnd2cYecWFwPpse1o9rx8uds47cP2bfr62sZM9LaYuNYPGnWmSKmiPeXinV2YxjHC4kFf3UUShMziLazstZbGRXU/0/*)#0gv2ddmk","range":[2147483647,2147483647],"timestamp":"now"}]'
error: Error while attempting to communicate with server 127.0.0.1:8332 (EOF)
Assertion failed: (m_wallet_descriptor.range_end - 1 == m_max_cached_index),
function TopUpWithDB, file scriptpubkeyman.cpp, line 1109.
The same crash happens with no range argument at all when -keypool is set above
INT32_MAX, because that branch takes the end from m_keypool_size, which is only
bounded from below (wallet.cpp:3092). Guarding the range argument alone is not
enough.
By the time the assert fires the descriptor has already been written to disk.
CreateFromImport calls TopUpWithDB directly and skips the transaction wrapper that
TopUp has, so the record survives the abort. The wallet still loads afterwards, but
listdescriptors reports the inverted range:
"range": [2147483647, -2147482650]
The first commit bounds the end after both branches, so every field that reaches the
WalletDescriptor constructor is representable. I did not put the check in
ParseDescriptorRange. #35872 made an endpoint of 2^31 - 1 valid for the scanning
RPCs and added a test asserting it succeeds, so tightening the shared parser would
regress that. This limit comes from how the wallet stores the range, not from descriptor
ranges in general.