bitcoinj java庫不解碼某些交易的輸入地址
我正在嘗試從 OP_RETURN 比特幣交易中“提取”發件人的地址,但我的程式碼無法正常工作。
getWalletAddressOfSender(最終交易 tx)
public static Address getWalletAddressOfSender(final Transaction tx) { Address fromAddress = null; for (final TransactionInput ti : tx.getInputs()) { try { Script scriptSig = ti.getScriptSig(); List<ScriptChunk> chunks = scriptSig.getChunks(); byte[] pubKey = scriptSig.getPubKey(); fromAddress = new Address(MainNetParams.get(), Utils.sha256hash160(pubKey));// scriptSig.getFromAddress(MainNetParams.get()); return fromAddress; } catch (final ScriptException x) { System.out.println(x.getMessage()); } } return null; }
當 TransactionInput 來自以 3… (P2SH) 開頭的地址時,列表 ‘scriptSig.getChunks()’ 將有 5 個項目,並且 ‘getPubKey()’ 將拋出異常’Script not of right size, expecting 2 but got 5’。
scriptSig.getPubKey()
/** * Returns the public key in this script. If a script contains two constants and nothing else, it is assumed to * be a scriptSig (input) for a pay-to-address output and the second constant is returned (the first is the * signature). If a script contains a constant and an OP_CHECKSIG opcode, the constant is returned as it is * assumed to be a direct pay-to-key scriptPubKey (output) and the first constant is the public key. * * @throws ScriptException if the script is none of the named forms. */ public byte[] getPubKey() throws ScriptException { if (chunks.size() != 2) { throw new ScriptException("Script not of right size, expecting 2 but got " + chunks.size()); } final ScriptChunk chunk0 = chunks.get(0); final byte[] chunk0data = chunk0.data; final ScriptChunk chunk1 = chunks.get(1); final byte[] chunk1data = chunk1.data; if (chunk0data != null && chunk0data.length > 2 && chunk1data != null && chunk1data.length > 2) { // If we have two large constants assume the input to a pay-to-address output. return chunk1data; } else if (chunk1.equalsOpCode(OP_CHECKSIG) && chunk0data != null && chunk0data.length > 2) { // A large constant followed by an OP_CHECKSIG is the key. return chunk0data; } else { throw new ScriptException("Script did not match expected form: " + this); } }
所以我將程式碼更改為
public static Address getWalletAddressOfSender(final Transaction tx) { Address fromAddress = null; for (final TransactionInput ti : tx.getInputs()) { try { Script scriptSig = ti.getScriptSig(); List<ScriptChunk> chunks = scriptSig.getChunks(); if(chunks.size() > 2) { System.out.print("This is a 5 chunks transaction... "); byte[] pubKeyHash = scriptSig.getPubKeyHash(); fromAddress = Address.fromP2SHHash(MainNetParams.get(), pubKeyHash); } else { byte[] pubKey = scriptSig.getPubKey(); fromAddress = new Address(MainNetParams.get(), Utils.sha256hash160(pubKey));// scriptSig.getFromAddress(MainNetParams.get()); } return fromAddress; } catch (final ScriptException x) { System.out.println(x.getMessage()); } } return null; }
但它也會拋出另一個異常’Script not in the standard scriptPubKey form’(當 chunks.size() > 2 時)
scriptSig.getPubKeyHash()
/** * <p>If a program matches the standard template DUP HASH160 <pubkey hash> EQUALVERIFY CHECKSIG * then this function retrieves the third element. * In this case, this is useful for fetching the destination address of a transaction.</p> * * <p>If a program matches the standard template HASH160 <script hash> EQUAL * then this function retrieves the second element. * In this case, this is useful for fetching the hash of the redeem script of a transaction.</p> * * <p>Otherwise it throws a ScriptException.</p> * */ public byte[] getPubKeyHash() throws ScriptException { if (isSentToAddress()) return chunks.get(2).data; else if (isPayToScriptHash()) return chunks.get(1).data; else throw new ScriptException("Script not in the standard scriptPubKey form"); }
這是一個事務,其中 chunks.size() == 2 並且我能夠提取發件人的地址 (1KYiKJEfdJtap9QX2v9BXJMpz2SfU4pgZw)
<https://www.blockchain.com/btc/tx/b5765d54e275794939eb48c77dd8862a6e865dee6d71bc7004660dca32de8c43>
這些是一些交易,其中 chunks.size() == 5 並且我無法提取發件人的地址 (3….)
<https://www.blockchain.com/btc/tx/b02e17479660a4685daba4e8f0f73aea96e0c36ab14142b68f868ac76a77455a>
<https://www.blockchain.com/btc/tx/f0a6708167eca88b9fe4dad4c110ddff2b3f6c5e08771793b8ca40400d4effab>
<https://www.blockchain.com/btc/tx/28a91393393916367e890965200d4f8af04416b65ee6fea22c0adf29af8ea3b8>
在這些情況下獲取發件人地址的正確方法是什麼?
僅供參考:這是我獲取收件人地址的糟糕方法
// THIS IF A FUCKED UP WORKAROUND, FIX ASAP @Nullable public static Address getWalletAddressOfReceiver(final Transaction tx, final Address senderAddress) { for (final TransactionOutput output : tx.getOutputs()) { try { final Script script = output.getScriptPubKey(); Address receiverAddress = script.getToAddress(MainNetParams.get(), true); if(receiverAddress.equals(senderAddress)) continue; return receiverAddress; } catch (final ScriptException x) { } } return null; }
由於收件人的地址在“tx.getOutputs()”中的隨機位置,如果它等於發件人的地址,我就跳過它並獲取下一個。
當輸入兌換 P2PKH 輸出(支付到以 1 開頭的地址)時,輸入腳本(scriptSig)的格式為:
[signature] [public key]
. 因為公鑰包含在標準輸入腳本(兌換 P2PKH 輸出)和輸出腳本 (P2PK) 中,所以該Script.getPubKey()
方法被編寫為在輸入腳本或輸出腳本中返回公鑰,因此它可以在這里為您工作。但是,公鑰雜湊(地址)僅包含在標準輸出腳本中。因此,該
Script.getPubKeyHash()
方法假定您正在討論輸出腳本,因此在這裡對您不起作用。一個輸入腳本贖回一個 P2SH(pay to address 以 3 開頭)輸出不一定是 5 個塊,但地址是贖回腳本的雜湊,贖回腳本總是最後一個塊。(具體來說,輸入腳本是:
[script sig for the redeem script (may be multiple chunks)] [redeem script]
。)所以您需要做的就是獲取最後一個塊(這也是您為 P2PKH 所做的)並對其進行雜湊處理。您可能會遇到麻煩的一個地方是,如果您正在兌換 P2SH 輸出,並且輸入腳本是 2 個塊。(這可能會出現,例如,如果贖回腳本是
[public key] OP_CHECKSIG
- 那麼 sigScript 的形式是[signature] [20-byte hash]
,與贖回 P2PKH 輸出的形式無法區分。)檢查這一點的方法是查看前一筆交易並檢查其輸出腳本(這也可以讓您更輕鬆地找到您正在尋找的地址)。但是假設當您兌換 P2SH 輸出時輸入腳本超過 2 個塊,這樣的事情應該可以工作:
public static Address getWalletAddressOfSender(final Transaction tx) { Address fromAddress = null; for (final TransactionInput ti : tx.getInputs()) { try { Script scriptSig = ti.getScriptSig(); List<ScriptChunk> chunks = scriptSig.getChunks(); byte[] pubKey = chunks.get(chunks.size() - 1).data; byte[] pubKeyHash = Utils.sha256hash160(pubKey); if (chunks.size() > 2) { // assume P2SH fromAddress = Address.fromP2SHHash(MainNetParams.get(), pubKeyHash); } else if (chunks.size() == 2) { // assume P2PKH fromAddress = new Address(MainNetParams.get(), pubKeyHash); } return fromAddress; } catch (final ScriptException x) { System.out.println(x.getMessage()); } } return null; }
(注意我在這裡使用了變數名
pubKey
和變數名pubKeyHash
,但在 P2SH 的情況下,這些將改為腳本和腳本雜湊。如果引用的輸出是多重簽名、P2PK 或非標準的東西,此方法也可能會導致問題。 )我還注意到您的方法僅返回第一個發件人地址。如果交易有來自多個來源的輸入,則可能值得返回一個列表或一組這些發件人地址。只是一個想法。
希望這可以幫助!