Why is bitcoinj java library not being able to decode input addresses for some transactions?
I'm trying to "extract" sender's addresses from OP_RETURN Bitcoin transactions, but my code is not working properly.
getWalletAddressOfSender(final Transaction 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; }
When the TransactionInput is from a address starting with 3... (P2SH) the list 'scriptSig.getChunks()' will have 5 items and 'getPubKey()' will throw the exception '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); } }
So i changed the code to
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; }
but it will also throw another exception 'Script not in the standard scriptPubKey form' (when 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"); }
Here's a transaction where chunks.size() == 2 and i'm able to extract sender's address (1KYiKJEfdJtap9QX2v9BXJMpz2SfU4pgZw)
https://www.blockchain.com/btc/tx/b5765d54e275794939eb48c77dd8862a6e865dee6d71bc7004660dca32de8c43
These are some transactions where chunks.size() == 5 and i'm NOT able to extract the sender's address (3....)
https://www.blockchain.com/btc/tx/b02e17479660a4685daba4e8f0f73aea96e0c36ab14142b68f868ac76a77455a
https://www.blockchain.com/btc/tx/f0a6708167eca88b9fe4dad4c110ddff2b3f6c5e08771793b8ca40400d4effab
https://www.blockchain.com/btc/tx/28a91393393916367e890965200d4f8af04416b65ee6fea22c0adf29af8ea3b8
What's the correct way of getting the sender's address in these cases ?
FYI: here's my shitty method to get the recipient's address
// 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; }
As the recipient's address is in a random position in "tx.getOutputs()" i just skip it if it's equal to sender's address and get the next one.
http://bit.ly/2Rdw5YG
Comments
Post a Comment