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.