Files
ObsidianDragon/src/wallet/lite_wallet_refresh_readiness_policy.cpp
DanS 863d015628 feat(lite): lite wallet foundation (inherited working-tree state)
Preserve the previously-uncommitted lite wallet implementation and related dev WIP
under version control:
- src/wallet/ lite services: client bridge, bridge runtime, connection, lifecycle,
  sync, gateway, result parsers, state mapper, artifact contract/resolver, refresh
  services, UI adapters, wallet_backend/capabilities. (Includes two small M1 fixes:
  lifecycle walletReady now parses the response; default chain name -> "main".)
- src/chat/ chat protocol; tests/fixtures/ (lite + hushchat); tools/hushchat_fixture_check.cpp;
  scripts/build-lite-backend-artifact.sh.
- Pre-existing modified app_network/security/wizard, network_refresh_service, sidebar,
  mining_tab, bootstrap dialog, and version headers captured as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 21:15:28 -05:00

205 lines
8.4 KiB
C++

#include "wallet/lite_wallet_refresh_readiness_policy.h"
#include <utility>
namespace dragonx::wallet {
namespace {
void addIssue(LiteWalletRefreshReadinessResult& result,
LiteWalletRefreshReadinessIssue issue,
std::string message)
{
result.issues.push_back(LiteWalletRefreshReadinessIssueInfo{issue, std::move(message)});
}
std::string statusMessageOrDefault(const WalletBackendStatus& status,
const std::string& fallback)
{
return status.message.empty() ? fallback : status.message;
}
LiteWalletRefreshReadinessResult failReadiness(
LiteWalletRefreshReadinessResult result,
LiteWalletRefreshReadinessStatus status,
LiteWalletRefreshReadinessIssue issue,
std::string message)
{
result.status = status;
addIssue(result, issue, std::move(message));
result.error = result.issues.back().message;
return result;
}
bool coordinatorReportIsUnsafe(const LiteWalletAppRefreshCoordinationResult& coordination)
{
return !coordination.dryRunOnly ||
!coordination.noNetwork ||
coordination.stateMutationRequested ||
coordination.stateMutationAllowed ||
coordination.stateMutated ||
coordination.walletStateWritten;
}
} // namespace
const char* liteWalletRefreshReadinessStatusName(LiteWalletRefreshReadinessStatus status)
{
switch (status) {
case LiteWalletRefreshReadinessStatus::EligibleDryRun: return "EligibleDryRun";
case LiteWalletRefreshReadinessStatus::CoordinatorRejected: return "CoordinatorRejected";
case LiteWalletRefreshReadinessStatus::UnsafeReport: return "UnsafeReport";
case LiteWalletRefreshReadinessStatus::WaitingForLifecycle: return "WaitingForLifecycle";
case LiteWalletRefreshReadinessStatus::WaitingForSync: return "WaitingForSync";
case LiteWalletRefreshReadinessStatus::Stale: return "Stale";
}
return "Unknown";
}
const char* liteWalletRefreshReadinessIssueName(LiteWalletRefreshReadinessIssue issue)
{
switch (issue) {
case LiteWalletRefreshReadinessIssue::CoordinatorReportRejected: return "CoordinatorReportRejected";
case LiteWalletRefreshReadinessIssue::CoordinatorReportIncomplete: return "CoordinatorReportIncomplete";
case LiteWalletRefreshReadinessIssue::CoordinatorReportUnsafe: return "CoordinatorReportUnsafe";
case LiteWalletRefreshReadinessIssue::FullNodeRouteRejected: return "FullNodeRouteRejected";
case LiteWalletRefreshReadinessIssue::LifecycleNotReady: return "LifecycleNotReady";
case LiteWalletRefreshReadinessIssue::SyncNotReady: return "SyncNotReady";
case LiteWalletRefreshReadinessIssue::RefreshTimestampMissing: return "RefreshTimestampMissing";
case LiteWalletRefreshReadinessIssue::RefreshTimestampInFuture: return "RefreshTimestampInFuture";
case LiteWalletRefreshReadinessIssue::RefreshStale: return "RefreshStale";
case LiteWalletRefreshReadinessIssue::StaleRefreshAllowed: return "StaleRefreshAllowed";
}
return "Unknown";
}
LiteWalletRefreshReadinessResult evaluateLiteWalletRefreshReadiness(
const LiteWalletAppRefreshCoordinationResult& coordination,
const LiteWalletRefreshReadinessInputs& inputs,
LiteWalletRefreshReadinessPolicyOptions options)
{
LiteWalletRefreshReadinessResult result;
result.route = coordination.route;
result.dryRunOnly = coordination.dryRunOnly;
result.noNetwork = coordination.noNetwork;
result.stateMutationRequested = coordination.stateMutationRequested;
result.stateMutationAllowed = coordination.stateMutationAllowed;
result.stateMutated = coordination.stateMutated;
result.walletStateWritten = coordination.walletStateWritten;
result.lifecycleReady = inputs.lifecycleReady;
result.lifecycleStatus = inputs.lifecycleStatus;
result.syncReady = inputs.syncReady;
result.syncStatus = inputs.syncStatus;
result.maxRefreshAgeSeconds = options.maxRefreshAgeSeconds;
if (coordination.fullNodeRefreshDelegated || coordination.route == LiteWalletRefreshRouteKind::FullNodeRpc) {
return failReadiness(
std::move(result),
LiteWalletRefreshReadinessStatus::CoordinatorRejected,
LiteWalletRefreshReadinessIssue::FullNodeRouteRejected,
"full-node refresh reports are not eligible for lite UI refresh readiness");
}
if (!coordination.ok) {
return failReadiness(
std::move(result),
LiteWalletRefreshReadinessStatus::CoordinatorRejected,
LiteWalletRefreshReadinessIssue::CoordinatorReportRejected,
coordination.error.empty()
? "lite app refresh coordinator report is not successful"
: coordination.error);
}
result.coordinatorReportAccepted = true;
if (!coordination.mapped || !coordination.planned || !coordination.executionReported ||
coordination.successfulCommandCount == 0) {
return failReadiness(
std::move(result),
LiteWalletRefreshReadinessStatus::CoordinatorRejected,
LiteWalletRefreshReadinessIssue::CoordinatorReportIncomplete,
"lite app refresh coordinator report is incomplete");
}
if (coordinatorReportIsUnsafe(coordination)) {
return failReadiness(
std::move(result),
LiteWalletRefreshReadinessStatus::UnsafeReport,
LiteWalletRefreshReadinessIssue::CoordinatorReportUnsafe,
"lite app refresh coordinator report is not a no-network dry-run report");
}
if (options.requireLifecycleReady && !inputs.lifecycleReady) {
return failReadiness(
std::move(result),
LiteWalletRefreshReadinessStatus::WaitingForLifecycle,
LiteWalletRefreshReadinessIssue::LifecycleNotReady,
statusMessageOrDefault(inputs.lifecycleStatus, "lite wallet lifecycle is not ready"));
}
if (options.requireSyncReady && !inputs.syncReady) {
return failReadiness(
std::move(result),
LiteWalletRefreshReadinessStatus::WaitingForSync,
LiteWalletRefreshReadinessIssue::SyncNotReady,
statusMessageOrDefault(inputs.syncStatus, "lite wallet sync is not ready"));
}
if (options.requireFreshRefresh) {
if (!inputs.freshness.haveSnapshotTime) {
return failReadiness(
std::move(result),
LiteWalletRefreshReadinessStatus::Stale,
LiteWalletRefreshReadinessIssue::RefreshTimestampMissing,
"lite refresh snapshot time is not known");
}
if (inputs.freshness.snapshotTimeSeconds > inputs.freshness.nowSeconds) {
return failReadiness(
std::move(result),
LiteWalletRefreshReadinessStatus::Stale,
LiteWalletRefreshReadinessIssue::RefreshTimestampInFuture,
"lite refresh snapshot time is in the future");
}
result.refreshAgeSeconds = inputs.freshness.nowSeconds - inputs.freshness.snapshotTimeSeconds;
if (result.refreshAgeSeconds > options.maxRefreshAgeSeconds) {
result.refreshStale = true;
if (!options.allowStaleRefresh) {
return failReadiness(
std::move(result),
LiteWalletRefreshReadinessStatus::Stale,
LiteWalletRefreshReadinessIssue::RefreshStale,
"lite refresh coordinator report is stale");
}
result.staleRefreshAllowed = true;
addIssue(result,
LiteWalletRefreshReadinessIssue::StaleRefreshAllowed,
"stale lite refresh coordinator report is allowed for reporting only");
} else {
result.refreshFresh = true;
}
} else {
result.refreshFresh = true;
}
result.ok = true;
result.eligibleForFutureUiRefresh = true;
result.status = LiteWalletRefreshReadinessStatus::EligibleDryRun;
return result;
}
LiteWalletRefreshReadinessPolicy::LiteWalletRefreshReadinessPolicy(
LiteWalletRefreshReadinessPolicyOptions options)
: options_(options)
{
}
LiteWalletRefreshReadinessResult LiteWalletRefreshReadinessPolicy::evaluate(
const LiteWalletAppRefreshCoordinationResult& coordination,
const LiteWalletRefreshReadinessInputs& inputs) const
{
return evaluateLiteWalletRefreshReadiness(coordination, inputs, options_);
}
} // namespace dragonx::wallet