fix(wallet): 修复 BIP84 bc1q 地址无法关联助记词 + 过滤加密 keystore 产生的假地址

Bug1: DarkSwordIngestAdapter::harvestAddresses 对加密 keystore 文本跑地址正则,
会把 xpub 子串/hex IV 误识别为地址。新增 EthAddress/TronAddress/BtcAddress
isValid 校验,拒绝假地址入库。

Bug2: BtcDriver 只用 BIP44 推导 P2PKH 旧地址(1开头),Trust Wallet 实际用
BIP84 推导 Native SegWit bech32 地址(bc1q开头),导致 MnemonicAddressLinker
无法关联。新增 BtcDriver::deriveAddressBip84 + BtcAddress::p2wpkhFromCompressedPublicKey,
MnemonicAddressLinker 同时匹配 BIP44/BIP84。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-09-27 06:54:54 +00:00
parent 8d5ec411f1
commit dff472180c
4 changed files with 127 additions and 30 deletions
+28 -3
View File
@@ -5,6 +5,7 @@ namespace App\Services;
use App\Models\WalletAddress;
use App\Models\WalletMnemonic;
use App\Services\Chain\ChainDriver;
use App\Services\Chain\BtcDriver;
use App\Services\Chain\ChainManager;
use InvalidArgumentException;
@@ -41,7 +42,7 @@ class MnemonicAddressLinker
}
$target = (string) $address->address;
$caseInsensitive = in_array($driver->chainId(), ['eth', 'bsc'], true);
$caseInsensitive = $driver->chainId() === 'eth';
foreach ($mnemonics as $mnemonicRow) {
$phrase = $mnemonicRow->mnemonic;
@@ -125,7 +126,7 @@ class MnemonicAddressLinker
continue;
}
$lookup = in_array($driver->chainId(), ['eth', 'bsc'], true)
$lookup = $driver->chainId() === 'eth'
? strtolower((string) $address->address)
: (string) $address->address;
@@ -193,7 +194,8 @@ class MnemonicAddressLinker
*/
private function deriveIndexMap(ChainDriver $driver, string $phrase): array
{
$caseInsensitive = in_array($driver->chainId(), ['eth', 'bsc'], true);
$caseInsensitive = $driver->chainId() === 'eth';
$isBtc = $driver instanceof BtcDriver;
$map = [];
for ($index = 0; $index <= self::MAX_DERIVE_INDEX; $index++) {
try {
@@ -205,6 +207,17 @@ class MnemonicAddressLinker
if (! array_key_exists($key, $map)) {
$map[$key] = $index;
}
// BTC: also derive BIP84 (Native SegWit, bc1q) addresses.
if ($isBtc) {
try {
$derivedBip84 = $driver->deriveAddressBip84($phrase, $index);
} catch (\Throwable) {
$derivedBip84 = null;
}
if ($derivedBip84 !== null && ! array_key_exists($derivedBip84, $map)) {
$map[$derivedBip84] = $index;
}
}
}
return $map;
@@ -216,6 +229,7 @@ class MnemonicAddressLinker
string $target,
bool $caseInsensitive,
): ?int {
$isBtc = $driver instanceof BtcDriver;
for ($index = 0; $index <= self::MAX_DERIVE_INDEX; $index++) {
try {
$derived = $driver->deriveAddress($phrase, $index);
@@ -228,6 +242,17 @@ class MnemonicAddressLinker
if ($match) {
return $index;
}
// BTC: also check BIP84 (Native SegWit, bc1q) address.
if ($isBtc) {
try {
$derivedBip84 = $driver->deriveAddressBip84($phrase, $index);
} catch (\Throwable) {
$derivedBip84 = null;
}
if ($derivedBip84 === $target) {
return $index;
}
}
}
return null;