fix(send): validate recipient address checksums (Base58Check + Bech32)

The send screen labelled any prefix+length match as a "Valid" address, so a
mistyped address that still matched the pattern passed the gate. Add pure,
offline checksum validation — Base58Check (transparent R-addresses) and Bech32
(Sapling zs-addresses) — and require it in the validity check. Both verifiers are
version-byte/HRP agnostic (the HRP is taken from the string, the Base58 checksum
is chain-independent), so a correct implementation never rejects a genuine
address while catching transcription errors. Works for both build variants
(no daemon round-trip), unit-tested against standard BIP173 / Base58Check vectors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 14:43:34 -05:00
parent 3cec333d84
commit 070a516f4e
5 changed files with 204 additions and 7 deletions

View File

@@ -18,6 +18,7 @@
#include "ui/windows/mining_benchmark.h"
#include "ui/windows/mining_pool_panel.h"
#include "ui/windows/mining_tab_helpers.h"
#include "util/address_validation.h"
#include "util/amount_format.h"
#include "util/payment_uri.h"
#include "util/platform.h"
@@ -4242,6 +4243,29 @@ void testAtomicFileWrite()
fs::remove_all(dir);
}
// Address checksum validation. Uses standard Base58Check / BIP173 Bech32 vectors — the
// algorithms are chain-agnostic, so passing these means real DRGX addresses validate too.
void testAddressChecksumValidation()
{
using dragonx::util::isValidBase58Check;
using dragonx::util::isValidBech32;
// Base58Check: a valid P2PKH address verifies; a one-char transcription error fails.
EXPECT_TRUE(isValidBase58Check("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"));
EXPECT_FALSE(isValidBase58Check("1A1zP1eP5QGefi2DMPTfTL5SLmv7Divfna")); // last char flipped
EXPECT_FALSE(isValidBase58Check("not-base58-0OIl")); // invalid alphabet
EXPECT_FALSE(isValidBase58Check(""));
// Bech32 (BIP173 valid vectors) verify; corrupted checksums fail.
EXPECT_TRUE(isValidBech32("A12UEL5L"));
EXPECT_TRUE(isValidBech32("abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"));
EXPECT_TRUE(isValidBech32("split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"));
EXPECT_FALSE(isValidBech32("A12UEL5M")); // checksum corrupted
EXPECT_FALSE(isValidBech32("abc1rzg")); // too short / bad checksum
EXPECT_FALSE(isValidBech32("Abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw")); // mixed case
EXPECT_FALSE(isValidBech32("nosalt")); // no separator
}
// Live probe of a real lite server (env-gated). Validates CONNECT_ONLY latency + IP capture.
void testLiteServerProbeLive()
{
@@ -4426,6 +4450,7 @@ int main()
testLiteServerHostParsing();
testLiteOfficialServerDetection();
testAtomicFileWrite();
testAddressChecksumValidation();
testLiteServerProbeLive();
testXmrigLiveInstall();
testGeneratedResourceBehavior();