diff --git a/src/com/google/bitcoin/core/Transaction.java b/src/com/google/bitcoin/core/Transaction.java index d3b570cc..2394439d 100644 --- a/src/com/google/bitcoin/core/Transaction.java +++ b/src/com/google/bitcoin/core/Transaction.java @@ -98,9 +98,11 @@ public class Transaction extends Message implements Serializable { } /** - * Returns the sum of the outputs that are sending coins to a key in our wallet. + * Calculates the sum of the outputs that are sending coins to a key in the wallet. + * @return sum in nanocoins */ public BigInteger getValueSentToMe(Wallet wallet) { + // This is tested in WalletTest. BigInteger v = BigInteger.ZERO; for (TransactionOutput o : outputs) { if (o.isMine(wallet)) { @@ -110,6 +112,28 @@ public class Transaction extends Message implements Serializable { return v; } + /** + * Calculates the sum of the inputs that are spending coins with keys in the wallet. This requires the + * transactions sending coins to those keys to be in the wallet. This method will not attempt to download the + * blocks containing the input transactions if the key is in the wallet but the transactions are not. + * + * @return sum in nanocoins. + */ + public BigInteger getValueSentFromMe(Wallet wallet) throws ScriptException { + // This is tested in WalletTest. + BigInteger v = BigInteger.ZERO; + for (TransactionInput input : inputs) { + boolean connected = input.outpoint.connect(wallet.unspent) || + input.outpoint.connect(wallet.fullySpent); + if (connected) { + // This input is taking value from an transaction in our wallet. To discover the value, + // we must find the connected transaction. + v = v.add(input.outpoint.getConnectedOutput().getValue()); + } + } + return v; + } + /** * These constants are a part of a scriptSig signature on the inputs. They define the details of how a * transaction can be redeemed, specifically, they control how the hash of the transaction is calculated. @@ -223,7 +247,7 @@ public class Transaction extends Message implements Serializable { /** * Once a transaction has some inputs and outputs added, the signatures in the inputs can be calculated. The * signature is over the transaction itself, to prove the redeemer actually created that transaction, - * so we have to do this step last. + * so we have to do this step last.

* * This method is similar to SignatureHash in script.cpp * diff --git a/src/com/google/bitcoin/core/TransactionOutPoint.java b/src/com/google/bitcoin/core/TransactionOutPoint.java index e2beacf1..6ae16d39 100644 --- a/src/com/google/bitcoin/core/TransactionOutPoint.java +++ b/src/com/google/bitcoin/core/TransactionOutPoint.java @@ -19,15 +19,16 @@ package com.google.bitcoin.core; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; +import java.util.Arrays; +import java.util.List; /** - * This message is effectively a reference or pointer to a transaction output. + * This message is a reference or pointer to an output of a different transaction. */ public class TransactionOutPoint extends Message implements Serializable { - // ID of the transaction to which we refer. - + /** Hash of the transaction to which we refer. */ byte[] hash; - // Which output of that transaction we are talking about. + /** Which output of that transaction we are talking about. */ long index; // This is not part of bitcoin serialization. @@ -63,6 +64,21 @@ public class TransactionOutPoint extends Message implements Serializable { Utils.uint32ToByteStreamLE(index, stream); } + /** + * Scans the list for the transaction this outpoint refers to, and sets up the internal reference used by + * getConnectedOutput(). + * @return true if connection took place, false if the referenced transaction was not in the list. + */ + boolean connect(List transactions) { + for (Transaction tx : transactions) { + if (Arrays.equals(tx.getHash(), hash)) { + fromTx = tx; + return true; + } + } + return false; + } + /** * If this transaction was created using the explicit constructor rather than deserialized, * retrieves the connected output transaction. Asserts if there is no connected transaction. diff --git a/src/com/google/bitcoin/core/Wallet.java b/src/com/google/bitcoin/core/Wallet.java index 3e3e7c21..e7611a01 100644 --- a/src/com/google/bitcoin/core/Wallet.java +++ b/src/com/google/bitcoin/core/Wallet.java @@ -35,17 +35,21 @@ import java.util.List; public class Wallet implements Serializable { private static final long serialVersionUID = -4501424466753895784L; - // A list of transactions with outputs we can spend. Note that some of these transactions may be partially spent, - // that is, they have outputs some of which are redeemed and others which aren't already. The spentness of each - // output is tracked in the TransactionOutput object. The value of all unspent outputs is the balance of the - // wallet. + /** + * A list of transactions with outputs we can spend. Note that some of these transactions may be partially spent, + * that is, they have outputs some of which are redeemed and others which aren't already. The spentness of each + * output is tracked in the TransactionOutput object. The value of all unspent outputs is the balance of the + * wallet. + */ public final ArrayList unspent; - // When all the outputs of a transaction are spent, it gets put here. These transactions aren't useful for - // anything except record keeping and presentation to the user. - private final LinkedList fullySpent; + /** + * When all the outputs of a transaction are spent, it gets put here. These transactions aren't useful for + * anything except record keeping and presentation to the user. + */ + final LinkedList fullySpent; - // A list of public/private EC keys owned by this user. + /** A list of public/private EC keys owned by this user. */ public final ArrayList keychain; private final NetworkParameters params; @@ -320,7 +324,8 @@ public class Wallet implements Serializable { /** * Locates a keypair from the keychain given the hash of the public key. This is needed when finding out which - * key we need to use to redeem a transaction output. Returns null if no key was found. + * key we need to use to redeem a transaction output. + * @return ECKey object or null if no such key was found. */ public synchronized ECKey findKeyFromPubHash(byte[] pubkeyHash) { for (ECKey key : keychain) { diff --git a/tests/com/google/bitcoin/core/WalletTest.java b/tests/com/google/bitcoin/core/WalletTest.java index 6a3dcd13..f78ef4ed 100644 --- a/tests/com/google/bitcoin/core/WalletTest.java +++ b/tests/com/google/bitcoin/core/WalletTest.java @@ -138,4 +138,17 @@ public class WalletTest { wallet.confirmSend(send2); assertEquals(bitcoinValueToFriendlyString(wallet.getBalance()), "0.80"); } + + @Test + public void testBalances() throws Exception { + BigInteger nanos = Utils.toNanoCoins(1, 0); + Transaction tx1 = createFakeTx(nanos, myAddress); + wallet.receive(tx1); + assertEquals(nanos, tx1.getValueSentToMe(wallet)); + // Send 0.10 to somebody else. + Transaction send1 = wallet.createSend(new ECKey().toAddress(params), toNanoCoins(0, 10), myAddress); + // Reserialize. + Transaction send2 = new Transaction(params, send1.bitcoinSerialize()); + assertEquals(nanos, send2.getValueSentFromMe(wallet)); + } }