The lite-wallet tree carried a second, unused refresh+readiness architecture
that never reached the shipping binary — exactly the churn CLAUDE.md warns
against. The live refresh path is controller -> gateway.refresh ->
mapLiteWalletRefreshResult -> applyLiteRefreshModelToWalletState; this parallel
stack was dead weight.
Verified unused (their public types/functions are referenced only within the
cluster), then deleted (8 files / 16 incl. headers):
- lite_wallet_refresh_service (LiteWalletRefreshService + gateway adapters)
- lite_wallet_app_refresh_coordinator
- lite_wallet_app_refresh_orchestrator
- lite_wallet_refresh_readiness_policy
- lite_wallet_state_apply_plan
- lite_wallet_state_apply_executor
- lite_wallet_sync_app_refresh_integration
- lite_wallet_sync_execution_readiness
Severed three thin couplings into the cluster from live files:
- state_mapper: dropped the dead mapLiteWalletRefreshServiceResult and switched
its include from refresh_service.h to gateway.h (where the live
LiteWalletRefreshResult/Bundle DTOs actually live).
- server_lifecycle_readiness: dropped the unused syncLifecycleInput member +
converter and the sync_app_refresh_integration include.
- artifact_resolver: relocated the three LIVE artifact-input structs
(LiteWalletSdxlArtifact{Symbols,}Input, LiteWalletLinkedBackendReadinessInput)
out of sync_execution_readiness.h — their only real consumers — into
artifact_resolver.h, then dropped the include.
Also removed the dead DRAGONX_LONG_LITE_BATCH CMake machinery (its source var
was empty; on Windows it generated a broken lite_batch90_receipt_plan.cpp that
#included an empty path) and the stale .cpp/.h entries in CMakeLists.
Lite source files: 44 -> 30. Lite + full-node configure, both targets build,
test suite passes, source-hygiene clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
212 lines
8.0 KiB
C++
212 lines
8.0 KiB
C++
#include "wallet/lite_wallet_state_mapper.h"
|
|
|
|
#include <utility>
|
|
|
|
namespace dragonx::wallet {
|
|
namespace {
|
|
|
|
LiteWalletAppAddressModel mapAddress(const std::string& address, LiteWalletAppAddressKind kind)
|
|
{
|
|
LiteWalletAppAddressModel model;
|
|
model.address = address;
|
|
model.kind = kind;
|
|
return model;
|
|
}
|
|
|
|
LiteWalletAppSpendableOutputModel mapSpendableOutput(const LiteSpendableOutput& output)
|
|
{
|
|
LiteWalletAppSpendableOutputModel model;
|
|
model.kind = output.kind;
|
|
model.address = output.address;
|
|
model.createdInTxid = output.createdInTxid;
|
|
model.createdInBlock = output.createdInBlock;
|
|
model.valueZatoshis = output.value;
|
|
model.spent = output.spent;
|
|
model.unconfirmedSpent = output.unconfirmedSpent;
|
|
model.pending = output.pending;
|
|
model.spendable = output.spendable;
|
|
return model;
|
|
}
|
|
|
|
LiteWalletAppTransactionKind mapTransactionKind(LiteTransactionDirection direction)
|
|
{
|
|
switch (direction) {
|
|
case LiteTransactionDirection::Send: return LiteWalletAppTransactionKind::Send;
|
|
case LiteTransactionDirection::Receive: return LiteWalletAppTransactionKind::Receive;
|
|
case LiteTransactionDirection::Unknown: return LiteWalletAppTransactionKind::Unknown;
|
|
}
|
|
return LiteWalletAppTransactionKind::Unknown;
|
|
}
|
|
|
|
LiteWalletAppTransactionOutputModel mapTransactionOutput(const LiteTransactionOutput& output)
|
|
{
|
|
LiteWalletAppTransactionOutputModel model;
|
|
model.address = output.address;
|
|
model.valueZatoshis = output.value;
|
|
model.memo = output.memo;
|
|
return model;
|
|
}
|
|
|
|
LiteWalletAppTransactionModel mapTransaction(const LiteTransactionRecord& transaction)
|
|
{
|
|
LiteWalletAppTransactionModel model;
|
|
model.txid = transaction.txid;
|
|
model.kind = mapTransactionKind(transaction.direction);
|
|
model.timestamp = transaction.datetime;
|
|
model.blockHeight = transaction.blockHeight;
|
|
model.unconfirmed = transaction.unconfirmed;
|
|
model.address = transaction.address;
|
|
model.amountZatoshis = transaction.amount;
|
|
model.signedAmountZatoshis = model.kind == LiteWalletAppTransactionKind::Send
|
|
? -transaction.amount
|
|
: transaction.amount;
|
|
model.memo = transaction.memo;
|
|
model.position = transaction.position;
|
|
|
|
model.outgoingOutputs.reserve(transaction.outgoingMetadata.size());
|
|
for (const auto& output : transaction.outgoingMetadata) {
|
|
model.outgoingOutputs.push_back(mapTransactionOutput(output));
|
|
}
|
|
return model;
|
|
}
|
|
|
|
bool modelHasAnyMappedField(const LiteWalletAppRefreshModel& model)
|
|
{
|
|
return model.hasChainInfo || model.hasHeight || model.hasBalance || model.hasAddresses ||
|
|
model.hasSpendableOutputs || model.hasTransactions || model.hasSyncStatus;
|
|
}
|
|
|
|
void addIssue(LiteWalletStateMapResult& result, LiteWalletStateMapIssue issue, std::string message)
|
|
{
|
|
result.issues.push_back(LiteWalletStateMapIssueInfo{issue, std::move(message)});
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const char* liteWalletAppAddressKindName(LiteWalletAppAddressKind kind)
|
|
{
|
|
switch (kind) {
|
|
case LiteWalletAppAddressKind::Shielded: return "shielded";
|
|
case LiteWalletAppAddressKind::Transparent: return "transparent";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char* liteWalletAppTransactionKindName(LiteWalletAppTransactionKind kind)
|
|
{
|
|
switch (kind) {
|
|
case LiteWalletAppTransactionKind::Unknown: return "unknown";
|
|
case LiteWalletAppTransactionKind::Send: return "send";
|
|
case LiteWalletAppTransactionKind::Receive: return "receive";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char* liteWalletStateMapIssueName(LiteWalletStateMapIssue issue)
|
|
{
|
|
switch (issue) {
|
|
case LiteWalletStateMapIssue::EmptyBundle: return "EmptyBundle";
|
|
case LiteWalletStateMapIssue::IncompleteBundle: return "IncompleteBundle";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
LiteWalletStateMapResult mapLiteWalletRefreshBundle(const LiteWalletRefreshBundle& bundle)
|
|
{
|
|
LiteWalletStateMapResult result;
|
|
result.stateMutationAllowed = false;
|
|
result.model.complete = bundle.complete;
|
|
result.model.successfulCommandCount = bundle.successfulCommandCount;
|
|
|
|
if (bundle.hasInfo) {
|
|
result.model.hasChainInfo = true;
|
|
result.model.chain.chainName = bundle.info.chainName;
|
|
result.model.chain.version = bundle.info.version;
|
|
result.model.chain.vendor = bundle.info.vendor;
|
|
result.model.chain.latestBlockHeight = bundle.info.latestBlockHeight;
|
|
result.model.chain.difficulty = bundle.info.difficulty;
|
|
result.model.chain.longestChain = bundle.info.longestChain;
|
|
result.model.chain.notarized = bundle.info.notarized;
|
|
}
|
|
|
|
if (bundle.hasHeight) {
|
|
result.model.hasHeight = true;
|
|
result.model.height.height = bundle.height.height;
|
|
}
|
|
|
|
if (bundle.hasBalance) {
|
|
result.model.hasBalance = true;
|
|
result.model.balance.transparentZatoshis = bundle.balance.transparentBalance;
|
|
result.model.balance.shieldedZatoshis = bundle.balance.shieldedBalance;
|
|
result.model.balance.unconfirmedZatoshis = bundle.balance.unconfirmedBalance;
|
|
result.model.balance.verifiedShieldedZatoshis = bundle.balance.verifiedShieldedBalance;
|
|
result.model.balance.spendableShieldedZatoshis = bundle.balance.spendableShieldedBalance;
|
|
result.model.balance.totalZatoshis = bundle.balance.transparentBalance + bundle.balance.shieldedBalance;
|
|
}
|
|
|
|
if (bundle.hasAddresses) {
|
|
result.model.hasAddresses = true;
|
|
result.model.addresses.reserve(bundle.addresses.zAddresses.size() + bundle.addresses.tAddresses.size());
|
|
for (const auto& address : bundle.addresses.zAddresses) {
|
|
result.model.addresses.push_back(mapAddress(address, LiteWalletAppAddressKind::Shielded));
|
|
}
|
|
for (const auto& address : bundle.addresses.tAddresses) {
|
|
result.model.addresses.push_back(mapAddress(address, LiteWalletAppAddressKind::Transparent));
|
|
}
|
|
}
|
|
|
|
if (bundle.hasNotes) {
|
|
result.model.hasSpendableOutputs = true;
|
|
const auto outputCount = bundle.notes.unspentNotes.size() + bundle.notes.utxos.size() +
|
|
bundle.notes.pendingNotes.size() + bundle.notes.pendingUtxos.size();
|
|
result.model.spendableOutputs.reserve(outputCount);
|
|
for (const auto& output : bundle.notes.unspentNotes) {
|
|
result.model.spendableOutputs.push_back(mapSpendableOutput(output));
|
|
}
|
|
for (const auto& output : bundle.notes.utxos) {
|
|
result.model.spendableOutputs.push_back(mapSpendableOutput(output));
|
|
}
|
|
for (const auto& output : bundle.notes.pendingNotes) {
|
|
result.model.spendableOutputs.push_back(mapSpendableOutput(output));
|
|
}
|
|
for (const auto& output : bundle.notes.pendingUtxos) {
|
|
result.model.spendableOutputs.push_back(mapSpendableOutput(output));
|
|
}
|
|
}
|
|
|
|
if (bundle.hasTransactions) {
|
|
result.model.hasTransactions = true;
|
|
result.model.transactions.reserve(bundle.transactions.transactions.size());
|
|
for (const auto& transaction : bundle.transactions.transactions) {
|
|
result.model.transactions.push_back(mapTransaction(transaction));
|
|
}
|
|
}
|
|
|
|
if (bundle.hasSyncStatus) {
|
|
result.model.hasSyncStatus = true;
|
|
result.model.sync.walletHeight = bundle.syncStatus.syncedBlocks;
|
|
result.model.sync.chainHeight = bundle.syncStatus.totalBlocks;
|
|
result.model.sync.progress = bundle.syncStatus.progress;
|
|
result.model.sync.complete = bundle.syncStatus.complete;
|
|
}
|
|
|
|
if (!modelHasAnyMappedField(result.model)) {
|
|
addIssue(result, LiteWalletStateMapIssue::EmptyBundle, "lite refresh bundle has no mappable fields");
|
|
result.error = result.issues.back().message;
|
|
return result;
|
|
}
|
|
|
|
if (!bundle.complete) {
|
|
addIssue(result, LiteWalletStateMapIssue::IncompleteBundle, "lite refresh bundle is partial");
|
|
}
|
|
|
|
result.ok = true;
|
|
return result;
|
|
}
|
|
|
|
LiteWalletStateMapResult mapLiteWalletRefreshResult(const LiteWalletRefreshResult& result)
|
|
{
|
|
return mapLiteWalletRefreshBundle(result.bundle);
|
|
}
|
|
|
|
} // namespace dragonx::wallet
|