2

I am trying to make transaction via bitcoinj (version 0.14.3) and i am expecting to get change back after payment. I am working with testnet, it's not real bitcoins. I have next code:

Transaction tx = new Transaction(this.networkParameters);
Coin coinToSent = Coin.valueOf(Config.APP_COST);
Coin coinToChange = Coin.valueOf(walletBalance.getValue() - coinToSent.getValue());
tx.addOutput(coinToSent, appAddress);
tx.addOutput(coinToChange, changeAddress);

SendRequest request = SendRequest.forTx(tx);
try {
    this.walletAppKit.wallet().completeTx(request);
} catch (InsufficientMoneyException e) {
    e.printStackTrace();
    return false;
}
this.walletAppKit.wallet().commitTx(request.tx);
this.walletAppKit.peerGroup().broadcastTransaction(request.tx); 

So, i am putting two outputs into transaction:

  1. Address to sending money
  2. Address of my wallet for getting change back

To the first address i send some money. And to the second address i send next value: all available money on my wallet minus money received to first address.

But after broadcasting i had an unexpected result. After making a few transactions with this scheme, I've noticed wrong values withdrawing from my wallet. What is surprising, sometimes money is withdrawing but sometimes money coming to wallet.

Here is the link to testnet explorer

Can someone explain what i am doing wrong and how to fix it?

yar_resh
  • 41
  • 5

1 Answers1

-1

The reason that the transaction is not working as expected is that the Bitcoinj Wallet class is doing a lot of things in the background for you - in this case, it is automatically generating a change address where is would store the change for the transaction. The other factor is that bitcoinj calculates a fee that it will give the miners to process the transaction, which is why you're seeing the change address getting less coin that you requested (on the production network it will throw an exception if you don't have enough funds to cover the fee).

I think the best solution here, for cases which are relatively simple, is to work with the Wallet API and let it do the work of generating change address automatically (It's using a deterministic keychain so all the addresses and keys can be regenerated in case you lose the wallet). For example:

public static Transaction send(Wallet wallet, 
                               String destinationAddress, 
                               long satoshis) throws Exception {
  Address dest = Address.fromBase58(params, destinationAddress);
  SendRequest request = SendRequest.to(dest, Coin.valueOf(satoshis));
  SendResult result = wallet.sendCoins(request);
  Transaction endTransaction = result.broadcastComplete.get();
  return endTransaction;
}

After sendCoins is completed and the transaction is broadcasted, the wallet will maintain the rest (you can save it to a file after the transactin is complete if you really want to make sure you have the keys for the change addresses), here's a way you can see a list of 10 change addresses and their public/private keys :

NetworkParameters params = new MainNetParams();
List<DeterministicKey> keys = wallet.freshKeys(KeyChain.KeyPurpose.CHANGE, 10);
keys.forEach(key -> {
  Address address = new Address(params, key.getPubKeyHash());
  System.out.println(address +" : " + key.toStringWithPrivate(params));
});
ohad serfaty
  • 644
  • 6
  • 11