Why is bitcoinj java library not being able to decode input addresses for some transactions?

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 &lt;pubkey hash&gt; 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 &lt;script hash&gt; 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

Popular posts from this blog

Need help to recover blpckchain.info wallet, my wife forgot her password and the brute force with btcrecover is not catching the password

When downloading the blockchain my application is become unusable, is there an issue with my code or am I using the BitcoinJ library incorrectly?

Mistakenly sent BTC from my personal wallet back to one of the exchange wallet addresses that I had received BTC from before - help recovering!