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>
This commit is contained in:
227
tools/hushchat_fixture_check.cpp
Normal file
227
tools/hushchat_fixture_check.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#include "chat/chat_protocol.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
using dragonx::chat::HushChatCompatibilityFixtureImportCandidate;
|
||||
using dragonx::chat::HushChatCompatibilityFixtureKind;
|
||||
|
||||
std::string baseName(const std::string& path)
|
||||
{
|
||||
return fs::path(path).filename().string();
|
||||
}
|
||||
|
||||
std::vector<std::string> jsonFilesInDirectory(const fs::path& directory)
|
||||
{
|
||||
std::vector<std::string> paths;
|
||||
if (!fs::exists(directory) || !fs::is_directory(directory)) return paths;
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(directory)) {
|
||||
if (!entry.is_regular_file()) continue;
|
||||
if (entry.path().extension() != ".json") continue;
|
||||
paths.push_back(entry.path().string());
|
||||
}
|
||||
std::sort(paths.begin(), paths.end());
|
||||
return paths;
|
||||
}
|
||||
|
||||
HushChatCompatibilityFixtureKind expectedKindFromPath(const std::string& path)
|
||||
{
|
||||
const std::string name = baseName(path);
|
||||
if (name.find("outgoing") != std::string::npos) return HushChatCompatibilityFixtureKind::OutgoingMemo;
|
||||
if (name.find("seed") != std::string::npos) return HushChatCompatibilityFixtureKind::SeedPublicKeyProjection;
|
||||
if (name.find("corrupted") != std::string::npos) return HushChatCompatibilityFixtureKind::CorruptedAuthFailure;
|
||||
if (name.find("cont") != std::string::npos) return HushChatCompatibilityFixtureKind::ContactExclusion;
|
||||
return HushChatCompatibilityFixtureKind::IncomingMemo;
|
||||
}
|
||||
|
||||
void printUsage(const char* program)
|
||||
{
|
||||
std::cerr << "Usage: " << program
|
||||
<< " [--allow-pending] [--replacement-dry-run] [--validate-capture-manifest]"
|
||||
<< " <fixture-file-or-directory>...\n";
|
||||
}
|
||||
|
||||
const char* boolText(bool value)
|
||||
{
|
||||
return value ? "1" : "0";
|
||||
}
|
||||
|
||||
void printReplacementDryRun(const dragonx::chat::HushChatCompatibilityFixtureReplacementDryRunResult& dryRun)
|
||||
{
|
||||
std::cout << "HushChat fixture replacement dry run: "
|
||||
<< (dryRun.ok ? "Ready" : "Refused") << "\n";
|
||||
std::cout << "error=" << dryRun.error_name
|
||||
<< " dry_run_only=" << boolText(dryRun.dry_run_only)
|
||||
<< " redacted=" << boolText(dryRun.redacted_report)
|
||||
<< " would_replace=" << boolText(dryRun.would_replace)
|
||||
<< " replacement_refused=" << boolText(dryRun.replacement_refused) << "\n";
|
||||
std::cout << "required=" << dryRun.required_count
|
||||
<< " supplied=" << dryRun.supplied_count
|
||||
<< " missing=" << dryRun.missing_count
|
||||
<< " verified=" << dryRun.verified_count
|
||||
<< " seed_projected=" << dryRun.seed_projection_verified_count
|
||||
<< " future_auth_required=" << dryRun.future_auth_failure_required_count
|
||||
<< " auth_structural_ready=" << dryRun.auth_failure_structural_ready_count
|
||||
<< " cont_excluded=" << dryRun.excluded_count
|
||||
<< " pending=" << dryRun.pending_count
|
||||
<< " rejected=" << dryRun.rejected_count << "\n";
|
||||
|
||||
for (const auto& item : dryRun.report_items) {
|
||||
std::cout << dragonx::chat::hushChatCompatibilityFixtureKindName(item.expected_kind)
|
||||
<< " decision=" << (item.replacement_eligible ? "ready" : "refused")
|
||||
<< " error=" << item.error_name
|
||||
<< " supplied=" << boolText(item.supplied)
|
||||
<< " pending=" << boolText(item.pending)
|
||||
<< " seed_projected=" << boolText(item.seed_projection_verified)
|
||||
<< " future_auth_required=" << boolText(item.future_auth_failure_required)
|
||||
<< " auth_structural_ready=" << boolText(item.structurally_ready_for_future_auth_check)
|
||||
<< " cont_excluded=" << boolText(item.cont_excluded)
|
||||
<< " decrypted=" << boolText(item.decrypted)
|
||||
<< " authenticated=" << boolText(item.authenticated);
|
||||
if (item.supplied) std::cout << " file=" << baseName(item.path);
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::string captureManifestPathFromInput(const std::string& input)
|
||||
{
|
||||
fs::path path(input);
|
||||
if (fs::exists(path) && fs::is_directory(path)) return (path / "capture-manifest.json").string();
|
||||
return input;
|
||||
}
|
||||
|
||||
void printCaptureManifestValidation(const dragonx::chat::HushChatCaptureManifestValidationResult& validation)
|
||||
{
|
||||
std::cout << "HushChat capture manifest: " << (validation.ok ? "Ready" : "Refused") << "\n";
|
||||
std::cout << "error=" << validation.error_name
|
||||
<< " redacted=" << boolText(validation.redacted_report)
|
||||
<< " provenance_only=" << boolText(validation.validates_provenance_only)
|
||||
<< " no_sensitive_material=" << boolText(validation.no_sensitive_material_declared)
|
||||
<< " dry_run_instruction=" << boolText(validation.has_dry_run_command);
|
||||
if (!validation.manifest_path.empty()) std::cout << " manifest=" << baseName(validation.manifest_path);
|
||||
if (!validation.fixture_directory.empty()) std::cout << " fixture_dir=" << baseName(validation.fixture_directory);
|
||||
std::cout << "\n";
|
||||
std::cout << "required=" << validation.required_count
|
||||
<< " declared=" << validation.declared_count
|
||||
<< " missing=" << validation.missing_count
|
||||
<< " duplicate=" << validation.duplicate_count
|
||||
<< " handling_flags=" << validation.handling_flag_count
|
||||
<< " prohibited=" << validation.prohibited_field_count << "\n";
|
||||
|
||||
for (const auto& category : validation.categories) {
|
||||
std::cout << dragonx::chat::hushChatCompatibilityFixtureKindName(category.kind)
|
||||
<< " declared=" << boolText(category.declared);
|
||||
if (!category.staged_filename.empty()) std::cout << " file=" << baseName(category.staged_filename);
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
bool allowPending = false;
|
||||
bool replacementDryRun = false;
|
||||
bool validateCaptureManifest = false;
|
||||
std::vector<std::string> inputPaths;
|
||||
|
||||
for (int index = 1; index < argc; ++index) {
|
||||
const std::string arg = argv[index];
|
||||
if (arg == "--allow-pending") {
|
||||
allowPending = true;
|
||||
} else if (arg == "--replacement-dry-run") {
|
||||
replacementDryRun = true;
|
||||
} else if (arg == "--validate-capture-manifest") {
|
||||
validateCaptureManifest = true;
|
||||
} else if (arg == "--help" || arg == "-h") {
|
||||
printUsage(argv[0]);
|
||||
return 0;
|
||||
} else {
|
||||
inputPaths.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (inputPaths.empty()) {
|
||||
printUsage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (validateCaptureManifest && (allowPending || replacementDryRun)) {
|
||||
std::cerr << "Capture manifest validation is separate from fixture and replacement checks.\n";
|
||||
return 2;
|
||||
}
|
||||
if (replacementDryRun && allowPending) {
|
||||
std::cerr << "Replacement dry run is strict; remove --allow-pending.\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (validateCaptureManifest) {
|
||||
bool allValid = true;
|
||||
for (const auto& input : inputPaths) {
|
||||
const auto validation = dragonx::chat::loadHushChatCaptureManifestFile(
|
||||
captureManifestPathFromInput(input),
|
||||
true);
|
||||
printCaptureManifestValidation(validation);
|
||||
if (!validation.ok) allValid = false;
|
||||
}
|
||||
return allValid ? 0 : 1;
|
||||
}
|
||||
|
||||
std::vector<std::string> paths;
|
||||
for (const auto& input : inputPaths) {
|
||||
fs::path path(input);
|
||||
if (fs::exists(path) && fs::is_directory(path)) {
|
||||
auto directoryFiles = jsonFilesInDirectory(path);
|
||||
paths.insert(paths.end(), directoryFiles.begin(), directoryFiles.end());
|
||||
} else {
|
||||
paths.push_back(input);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<HushChatCompatibilityFixtureImportCandidate> candidates;
|
||||
candidates.reserve(paths.size());
|
||||
for (const auto& path : paths) {
|
||||
candidates.push_back(HushChatCompatibilityFixtureImportCandidate{expectedKindFromPath(path), path});
|
||||
}
|
||||
|
||||
if (replacementDryRun) {
|
||||
const auto dryRun = dragonx::chat::inspectHushChatCompatibilityFixtureReplacementDryRun(candidates, true);
|
||||
printReplacementDryRun(dryRun);
|
||||
return dryRun.ok ? 0 : 1;
|
||||
}
|
||||
|
||||
const auto checklist = dragonx::chat::inspectHushChatCompatibilityFixtureImportChecklist(candidates, true);
|
||||
std::cout << "HushChat fixture import checklist: " << checklist.error_name << "\n";
|
||||
std::cout << "required=" << checklist.required_count
|
||||
<< " supplied=" << checklist.supplied_count
|
||||
<< " missing=" << checklist.missing_count
|
||||
<< " verified=" << checklist.verified_count
|
||||
<< " seed_projected=" << checklist.seed_projection_verified_count
|
||||
<< " future_auth_required=" << checklist.future_auth_failure_required_count
|
||||
<< " auth_structural_ready=" << checklist.auth_failure_structural_ready_count
|
||||
<< " cont_excluded=" << checklist.excluded_count
|
||||
<< " pending=" << checklist.pending_count
|
||||
<< " rejected=" << checklist.rejected_count << "\n";
|
||||
|
||||
for (const auto& item : checklist.items) {
|
||||
std::cout << dragonx::chat::hushChatCompatibilityFixtureKindName(item.expected_kind)
|
||||
<< " " << item.error_name;
|
||||
if (item.future_auth_failure_required) std::cout << " FutureAuthFailureRequired";
|
||||
if (item.structurally_ready_for_future_auth_check) std::cout << " StructurallyReadyForFutureAuthCheck";
|
||||
if (item.supplied) std::cout << " " << baseName(item.path);
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
if (checklist.replacement_ready) return 0;
|
||||
if (allowPending && checklist.supplied_count == checklist.required_count &&
|
||||
checklist.missing_count == 0 && checklist.pending_count > 0 && checklist.rejected_count == 0) return 0;
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user