[英]Generate Ethereum addresses in HD Wallet using public key only (bitcoinj/web3j)
我試圖為使用 bitcoinj 庫實現的 HD 錢包密鑰生成以太坊地址,但我很困惑:
DeterministicSeed seed = new DeterministicSeed("some seed code here", null, "", 1409478661L);
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
DeterministicKey addrKey = chain.getKeyByPath(HDUtils.parsePath("M/44H/60H/0H/0/0"), true);
System.out.println("address from pub=" + Keys.getAddress(Sign.publicKeyFromPrivate(addrKey.getPrivKey())));
此代碼根據https://iancoleman.io/bip39/打印正確的以太坊地址。 這里一切都很好。
但是當我試圖避免使用私鑰並僅使用公鑰生成非硬化密鑰時,我得到了不同的結果,即調用返回另一個結果:
System.out.println("address from pub=" + Keys.getAddress(addrKey.getPublicKeyAsHex()));
看起來問題出在“不同的公鑰”中,即Sign.publicKeyFromPrivate(addrKey.getPrivKey())
和addrKey.getPublicKeyAsHex()
的結果不同。 我沒有密碼學經驗,因此這可能是一個愚蠢的問題......但我會很感激這里的任何建議。
與比特幣一樣,以太坊使用 secp256k1 。 以太坊地址派生如下:
對於此處使用的示例,密鑰是通過以下方式生成的:
String mnemonic = "elevator dinosaur switch you armor vote black syrup fork onion nurse illegal trim rocket combine";
DeterministicSeed seed = new DeterministicSeed(mnemonic, null, "", 1409478661L);
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
DeterministicKey addrKey = chain.getKeyByPath(HDUtils.parsePath("M/44H/60H/0H/0/0"), true);
這對應於以下公鑰和以太坊地址:
X: a35bf0fdf5df296cc3600422c3c8af480edb766ff6231521a517eb822dff52cd
Y: 5440f87f5689c2929542e75e739ff30cd1e8cb0ef0beb77380d02cd7904978ca
Address: 23ad59cc6afff2e508772f69d22b19ffebf579e7
也可以通過網站https://iancoleman.io/bip39/進行驗證。
步驟1:
在發布的問題中,表達式Sign.publicKeyFromPrivate()
和addrKey.getPublicKeyAsHex()
提供不同的結果。 這兩個函數都返回不同類型的公鑰。 而Sign.publicKeyFromPrivate()
使用BigInteger
, addrKey.getPublicKeyAsHex()
提供十六進制字符串。 對於直接比較,可以使用toString(16)
將BigInteger
轉換為十六進制字符串。 當兩個表達式的結果顯示為:
System.out.println(Sign.publicKeyFromPrivate(addrKey.getPrivKey()).toString(16));
System.out.println(addrKey.getPublicKeyAsHex());
得到以下結果:
a35bf0fdf5df296cc3600422c3c8af480edb766ff6231521a517eb822dff52cd5440f87f5689c2929542e75e739ff30cd1e8cb0ef0beb77380d02cd7904978ca
02a35bf0fdf5df296cc3600422c3c8af480edb766ff6231521a517eb822dff52cd
Sign.publicKeyFromPrivate()
的 output 的長度為 64 字節,對應於步驟 1 中定義的串聯 x 和 y 坐標。因此,由此生成的地址是有效的以太坊地址,如發布的問題中所述。
另一方面, addrKey.getPublicKeyAsHex()
的 output 對應於以 0x02 值為前綴的 x 坐標。 這是公鑰的壓縮格式。 如果 y 值為偶數(如本例所示),則前導字節的值為 0x02,或者值為 0x03。 由於壓縮后的格式不包含y坐標,所以不能直接用這個來推斷以太坊地址,否則無論如何都會導致地址錯誤(間接的,當然也有可能因為y坐標可以從壓縮的公鑰中導出)。
可以獲取公鑰的未壓縮格式,例如使用addrKey.decompress()
:
System.out.println(addrKey.decompress().getPublicKeyAsHex());
這給出了這個結果:
04a35bf0fdf5df296cc3600422c3c8af480edb766ff6231521a517eb822dff52cd5440f87f5689c2929542e75e739ff30cd1e8cb0ef0beb77380d02cd7904978ca
未壓縮格式包含一個前導標記字節,其值為 0x04,后跟 x 和 y 坐標。 所以如果去掉前導標記字節,就得到了第1步的數據,這是推導以太坊地址所需要的:
System.out.println(addrKey.decompress().getPublicKeyAsHex().substring(2));
這導致:
a35bf0fdf5df296cc3600422c3c8af480edb766ff6231521a517eb822dff52cd5440f87f5689c2929542e75e739ff30cd1e8cb0ef0beb77380d02cd7904978ca
步驟 2 和 3:
步驟 2 和 3 由Keys.getAddress()
執行。 這允許使用未壓縮的公鑰獲取以太坊地址,如下所示:
System.out.println(Keys.getAddress(addrKey.decompress().getPublicKeyAsHex().substring(2)));
System.out.println(Keys.getAddress(Sign.publicKeyFromPrivate(addrKey.getPrivKey()))); // For comparison
這給出了以太坊地址:
23ad59cc6afff2e508772f69d22b19ffebf579e7
23ad59cc6afff2e508772f69d22b19ffebf579e7
Keys.getAddress()
的重載:
Keys.getAddress()
為數據類型BigInteger
、 hex string 和byte[]
提供了各種重載。 如果未壓縮的密鑰以byte[]
形式給出,例如使用addrKey.getPubKeyPoint().getEncoded(false)
,則可以在刪除標記字節后直接使用byte[]
。 或者,可以將byte[]
轉換為刪除標記字節的BigInteger
:
byte[] uncompressed = addrKey.getPubKeyPoint().getEncoded(false);
System.out.println(bytesToHex(Keys.getAddress(Arrays.copyOfRange(uncompressed, 1, uncompressed.length))).toLowerCase()); // bytesToHex() from https://stackoverflow.com/a/9855338
System.out.println(Keys.getAddress(new BigInteger(1, uncompressed, 1, uncompressed.length - 1)));
System.out.println(Keys.getAddress(Sign.publicKeyFromPrivate(addrKey.getPrivKey()))); // For comparison
正如預期的那樣返回相同的以太坊地址:
23ad59cc6afff2e508772f69d22b19ffebf579e7
23ad59cc6afff2e508772f69d22b19ffebf579e7
23ad59cc6afff2e508772f69d22b19ffebf579e7
這里要注意的一件事是Keys.getAddress(byte[]
)不會填充傳遞的byte[]
,而BigInteger
或十六進制字符串的重載會隱式填充。 這可能是相關的,例如在將BigInteger
(例如由Sign.publicKeyFromPrivate(addrKey.getPrivKey())
提供)轉換為byte[]
時,因為結果也可能少於 64 個字節(這將導致不同的 Keccak-256哈希)。 如果在這種情況下使用Keys.getAddress(byte[])
,則必須使用前導 0x00 值explicitly
填充,最長可達 64 個字節。
topaco你的地址好像有ens空投沒有認領
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.