feat(wallets): show created date + sort the wallet list
Parse the earliest keymeta nCreateTime out of the wallet.dat btree (the daemon's
"wallet birthday") and surface it in each row's metadata line ("created Aug 2025").
Add a sort control above the list — Date created / Address count / Transaction
count / Wallet size, with an ascending/descending toggle (descending default:
newest / most / largest first). Sorting reorders a display-index array
(s_order) rather than s_rows, so the async, index-aligned probe batch is
untouched; sort keys read a single frame-consistent snapshot of the probe
results. Address count prefers the authoritative cached index value, else the
btree key count.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -790,27 +790,39 @@ void testWalletFileProbe()
|
||||
EXPECT_TRUE(p.isBerkeleyDB); EXPECT_TRUE(p.scanComplete); EXPECT_FALSE(p.encrypted); EXPECT_FALSE(p.hdSeed); }
|
||||
|
||||
// 7) Btree record counting (tier 2): hand-build a minimal single-database BDB btree — a 512-byte
|
||||
// metapage (magic/pagesize/root) + one leaf page holding a "tx" record and a "key" record — and
|
||||
// confirm parseWalletBtree walks it and tallies them. Also that a non-BDB file yields parsed=false.
|
||||
// metapage (magic/pagesize/root) + one leaf page holding "tx", "key", and a "keymeta" record — and
|
||||
// confirm parseWalletBtree walks it, tallies the counts, and reads the keymeta nCreateTime (wallet
|
||||
// birthday). Also that a non-BDB file yields parsed=false.
|
||||
{
|
||||
std::string w(1024, '\0');
|
||||
auto put16 = [&](std::size_t o, unsigned v) { w[o] = (char)(v & 0xff); w[o+1] = (char)((v >> 8) & 0xff); };
|
||||
auto put32 = [&](std::size_t o, unsigned v) { for (int k=0;k<4;k++) w[o+k] = (char)((v >> (8*k)) & 0xff); };
|
||||
put32(12, 0x00053162u); put32(20, 512); w[25] = (char)9; put32(88, 1); // page 0: metapage, root = page 1
|
||||
const std::size_t P = 512; // page 1: a btree leaf
|
||||
put16(P + 20, 4); w[P + 24] = (char)1; w[P + 25] = (char)5; // entries=4 (2 pairs), level 1, P_LBTREE
|
||||
put16(P + 26, 500); put16(P + 28, 496); put16(P + 30, 488); put16(P + 32, 484); // index array → item offsets
|
||||
put16(P + 20, 6); w[P + 24] = (char)1; w[P + 25] = (char)5; // entries=6 (3 pairs), level 1, P_LBTREE
|
||||
// index array → (key,data) item offsets, laid out high→low without overlap:
|
||||
put16(P + 26, 500); put16(P + 28, 496); // tx
|
||||
put16(P + 30, 488); put16(P + 32, 484); // key
|
||||
put16(P + 34, 472); put16(P + 36, 456); // keymeta
|
||||
auto keyItem = [&](std::size_t off, const std::string& type) { // BKEYDATA holding CompactSize(len)+type
|
||||
std::string kd; kd += (char)type.size(); kd += type;
|
||||
put16(P + off, (unsigned)kd.size()); w[P + off + 2] = (char)1;
|
||||
for (std::size_t k = 0; k < kd.size(); ++k) w[P + off + 3 + k] = kd[k];
|
||||
};
|
||||
auto dataItem = [&](std::size_t off) { put16(P + off, 1); w[P + off + 2] = (char)1; w[P + off + 3] = 0; };
|
||||
keyItem(500, "tx"); dataItem(496); keyItem(488, "key"); dataItem(484);
|
||||
keyItem(500, "tx"); dataItem(496);
|
||||
keyItem(488, "key"); dataItem(484);
|
||||
keyItem(472, "keymeta");
|
||||
// keymeta data = CKeyMetadata: nVersion(int32=10) + nCreateTime(int64) — little-endian.
|
||||
const long long kCreated = 1700000000LL;
|
||||
put16(P + 456, 12); w[P + 456 + 2] = (char)1; // BKEYDATA len=12, type=1, data@459
|
||||
put32(456 + 3 + P, 10); // nVersion
|
||||
for (int k = 0; k < 8; ++k) w[P + 456 + 3 + 4 + k] = (char)((kCreated >> (8 * k)) & 0xff);
|
||||
writef(dir / "btree.dat", w);
|
||||
auto s = dragonx::util::parseWalletBtree((dir / "btree.dat").string());
|
||||
EXPECT_TRUE(s.parsed); EXPECT_TRUE(s.complete);
|
||||
EXPECT_EQ(s.txCount, 1); EXPECT_EQ(s.transparentKeys, 1); EXPECT_EQ(s.addresses(), 1);
|
||||
EXPECT_EQ(s.createdEpoch, kCreated);
|
||||
EXPECT_FALSE(dragonx::util::parseWalletBtree((dir / "junk.dat").string()).parsed);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user