feat: scan

This commit is contained in:
hashbro
2026-09-12 23:53:53 +08:00
parent e294053285
commit 558912864d
36 changed files with 4165 additions and 15 deletions
+52
View File
@@ -39,6 +39,58 @@ final class Bip44
];
}
/**
* True when the phrase is a checksum-valid BIP39 mnemonic that can
* produce a secp256k1 key (and therefore chain addresses).
*/
public static function canDerive(string $mnemonic): bool
{
try {
$derived = self::derive($mnemonic, "m/44'/60'/0'/0/0");
return strlen((string) ($derived['public_key_uncompressed'] ?? '')) >= 130;
} catch (\Throwable) {
return false;
}
}
/**
* Checksum-valid is not enough for ingest: reject BIP39 test vectors and
* low-entropy repeats (same word more than 3 times, or official test vectors).
*/
public static function isUsableMnemonic(string $mnemonic): bool
{
$words = preg_split('/\s+/', strtolower(trim($mnemonic))) ?: [];
$n = count($words);
if (! in_array($n, [12, 15, 18, 21, 24], true)) {
return false;
}
if (in_array(implode(' ', $words), self::TEST_VECTORS, true)) {
return false;
}
$counts = array_count_values($words);
if (count($counts) < $n - 2) {
return false;
}
if (max($counts) > 3) {
return false;
}
return self::canDerive($mnemonic);
}
/** @var list<string> */
private const TEST_VECTORS = [
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
'legal winner thank year wave sausage worth useful legal winner thank yellow',
'letter advice cage absurd amount doctor acoustic avoid letter advice cage above',
'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong',
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent',
'legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will',
'letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless',
'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote',
];
/** @return array{0: string, 1: string} binary key + chain code */
private static function masterFromSeed(string $seed): array
{