Files
ObsidianDragon/src/util/address_validation.h
DanS c3e81a5fa6 fix(send): accept P2SH/multisig recipients in the send + URI address gates
Same defect class as the import-key fix: a hardcoded prefix/length pre-filter
layered over the checksum validators rejected valid addresses before the daemon
saw them. The send-screen recipient gate required a[0]=='R', and the payment-URI
parser accepted only 'R'/'t' with rigid length bands — so every valid P2SH /
multisig address (DragonX SCRIPT_ADDRESS=85 → 'b…') was silently refused, leaving
the Send button disabled with no usable recipient.

Centralize recipient recognition in util/address_validation:
- isTransparentAddress: Base58Check with a 21-byte version+hash160 payload —
  covers P2PKH ('R…', v60) AND P2SH ('b…', v85) on every network, rejects WIF
  keys / typos by real checksum.
- isShieldedAddress: Bech32 + a Sapling payment-address HRP (zs / ztestsapling /
  zregtestsapling), distinguishing a payment address from a viewing key.
- isValidRecipientAddress: either of the above.

send_tab's two validity helpers (the single choke point for all 5 call sites) and
the payment-URI format check now route through these. The URI parser now
checksum-validates the recipient (fail-fast on transcription errors) rather than
being prefix/length-only.

Tests use real checksummed vectors (P2PKH/P2SH/shielded, WIF- and typo-rejection);
testPaymentUri updated off its old fake fixed-char addresses. Suite green (1/1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-09 00:12:17 -05:00

53 lines
2.5 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
//
// address_validation.h — pure, offline checksum validation for wallet addresses.
// These verify the *encoding checksum* (Base58Check / Bech32), which is independent
// of the chain's version bytes / HRP, so a correct implementation never rejects a
// genuinely-valid address while still catching transcription errors. Used to make
// the send screen's "Valid address" label/gate reflect reality instead of a bare
// prefix+length heuristic. No network, no daemon — safe for both build variants.
#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace dragonx {
namespace util {
// True if `s` decodes as Base58Check with a valid 4-byte double-SHA256 checksum
// (transparent R-addresses). Version-byte agnostic by design.
bool isValidBase58Check(const std::string& s);
// Decodes `s` as Base58Check; on success returns true and fills `payloadOut` with the
// decoded bytes EXCLUDING the trailing 4-byte checksum (i.e. version byte + data). Lets
// callers inspect the version byte / payload length (e.g. to tell a WIF from an address).
bool decodeBase58Check(const std::string& s, std::vector<std::uint8_t>& payloadOut);
// True if `s` is a valid Bech32 string (Sapling zs-addresses). The HRP is taken
// from the string itself and folded into the checksum, so no HRP is hardcoded.
bool isValidBech32(const std::string& s);
// Returns the (lower-cased) human-readable prefix of a valid Bech32 string, or "" if
// `s` is not valid Bech32. The HRP identifies the key/address type (e.g. "zivks").
std::string bech32Hrp(const std::string& s);
// True if `s` is a transparent (Base58Check) address — P2PKH *or* P2SH/multisig. Accepts any
// address whose payload is a 21-byte version+hash160, so it covers both the 'R…' (v60) and 'b…'
// (v85 script) forms on every DragonX network and rejects WIF keys / typos by checksum. Version-byte
// agnostic by design — a bare prefix check ('R' only) silently drops valid P2SH recipients.
bool isTransparentAddress(const std::string& s);
// True if `s` is a shielded Sapling payment address (HRP "zs" / "ztestsapling" / "zregtestsapling"),
// with a valid Bech32 checksum. Distinguishes a payment address from a viewing key (e.g. "zivks…").
bool isShieldedAddress(const std::string& s);
// True if `s` is any address a payment can be sent to (transparent or shielded).
bool isValidRecipientAddress(const std::string& s);
} // namespace util
} // namespace dragonx