diff --git a/core/src/bitcoin.proto b/core/src/bitcoin.proto index 83e01a52..4d0b12ab 100644 --- a/core/src/bitcoin.proto +++ b/core/src/bitcoin.proto @@ -18,7 +18,8 @@ */ /* Notes: - * - Endianness: All byte arrays that represent numbers (such as hashes and private keys) are Big Endian (a.k.a. network order) + * - Endianness: All byte arrays that represent numbers (such as hashes and private keys) are Big Endian + * - To regenerate after editing, run mvn clean package -DupdateProtobuf */ package wallet; @@ -115,6 +116,18 @@ message TransactionConfidence { optional int64 work_done = 5; repeated PeerAddress broadcast_by = 6; + + // Where did we get this transaction from? Knowing the source may help us to risk analyze pending transactions. + enum Source { + SOURCE_UNKNOWN = 0; // We don't know where it came from, or this is a wallet from the future. + SOURCE_NETWORK = 1; // We received it from a network broadcast. This is the normal way to get payments. + SOURCE_SELF = 2; // We made it ourselves, so we know it should be valid. + // In future: + // - direct from trusted counterparty, eg via bluetooth/wifi direct + // - direct from untrusted counterparty + // - from a wallet that uses trusted computing/secure hardware that won't create double spends + } + optional Source source = 7; } /** A bitcoin transaction */ diff --git a/core/src/main/java/com/google/bitcoin/core/Block.java b/core/src/main/java/com/google/bitcoin/core/Block.java index a582ee51..d6f1eafd 100644 --- a/core/src/main/java/com/google/bitcoin/core/Block.java +++ b/core/src/main/java/com/google/bitcoin/core/Block.java @@ -185,6 +185,8 @@ public class Block extends Message { transactions = new ArrayList(numTransactions); for (int i = 0; i < numTransactions; i++) { Transaction tx = new Transaction(params, bytes, cursor, this, parseLazy, parseRetain, UNKNOWN_LENGTH); + // Label the transaction as coming from the P2P network, so code that cares where we first saw it knows. + tx.getConfidence().setSource(TransactionConfidence.Source.NETWORK); transactions.add(tx); cursor += tx.getMessageSize(); optimalEncodingMessageSize += tx.getOptimalEncodingMessageSize(); diff --git a/core/src/main/java/com/google/bitcoin/core/Peer.java b/core/src/main/java/com/google/bitcoin/core/Peer.java index 2ffb9029..46951277 100644 --- a/core/src/main/java/com/google/bitcoin/core/Peer.java +++ b/core/src/main/java/com/google/bitcoin/core/Peer.java @@ -416,6 +416,9 @@ public class Peer { tx = memoryPool.seen(tx, getAddress()); } final Transaction fTx = tx; + // Label the transaction as coming in from the P2P network (as opposed to being created by us, direct import, + // etc). This helps the wallet decide how to risk analyze it later. + fTx.getConfidence().setSource(TransactionConfidence.Source.NETWORK); if (maybeHandleRequestedData(fTx)) { return; } diff --git a/core/src/main/java/com/google/bitcoin/core/TransactionConfidence.java b/core/src/main/java/com/google/bitcoin/core/TransactionConfidence.java index 49a171ff..fee9427b 100644 --- a/core/src/main/java/com/google/bitcoin/core/TransactionConfidence.java +++ b/core/src/main/java/com/google/bitcoin/core/TransactionConfidence.java @@ -67,43 +67,11 @@ public class TransactionConfidence implements Serializable { // Lazily created listeners array. private transient ArrayList listeners; - /** - * The depth of the transaction on the best chain in blocks. An unconfirmed block has depth 0, after one confirmation - * its depth is 1. - */ + // The depth of the transaction on the best chain in blocks. An unconfirmed block has depth 0. private int depth; - - /** - * The cumulative work done for the blocks that bury this transaction. BigInteger.ZERO if the transaction is not - * on the best chain. - */ + // The cumulative work done for the blocks that bury this transaction. private BigInteger workDone = BigInteger.ZERO; - /** - *

Adds an event listener that will be run when this confidence object is updated. The listener will be locked and - * is likely to be invoked on a peer thread.

- * - *

Note that this is NOT called when every block arrives. Instead it is called when the transaction - * transitions between confidence states, ie, from not being seen in the chain to being seen (not necessarily in - * the best chain). If you want to know when the transaction gets buried under another block, implement a - * {@link BlockChainListener}, attach it to a {@link BlockChain} and then use the getters on the - * confidence object to determine the new depth.

- */ - public synchronized void addEventListener(Listener listener) { - Preconditions.checkNotNull(listener); - if (listeners == null) - listeners = new ArrayList(2); - // Dedupe registrations. This makes the wallet code simpler. - if (!listeners.contains(listener)) - listeners.add(listener); - } - - public synchronized void removeEventListener(Listener listener) { - Preconditions.checkNotNull(listener); - Preconditions.checkNotNull(listeners); - listeners.remove(listener); - } - /** Describes the state of the transaction in general terms. Properties can be read to learn specifics. */ public enum ConfidenceType { /** If BUILDING, then the transaction is included in the best chain and your confidence in it is increasing. */ @@ -161,6 +129,32 @@ public class TransactionConfidence implements Serializable { }; + private ConfidenceType confidenceType = ConfidenceType.UNKNOWN; + private int appearedAtChainHeight = -1; + // The transaction that double spent this one, if any. + private Transaction overridingTransaction; + + /** + * Information about where the transaction was first seen (network, sent direct from peer, created by ourselves). + * Useful for risk analyzing pending transactions. Probably not that useful after a tx is included in the chain, + * unless re-org double spends start happening frequently. + */ + public enum Source { + /** We don't know where the transaction came from. */ + UNKNOWN, + /** We got this transaction from a network peer. */ + NETWORK, + /** This transaction was created by our own wallet, so we know it's not a double spend. */ + SELF + } + private Source source = Source.UNKNOWN; + + public TransactionConfidence(Transaction tx) { + // Assume a default number of peers for our set. + broadcastBy = new CopyOnWriteArrayList(); + transaction = tx; + } + /** *

A confidence listener is informed when the level of {@link TransactionConfidence} is updated by something, like * for example a {@link Wallet}. You can add listeners to update your user interface or manage your order tracking @@ -174,14 +168,29 @@ public class TransactionConfidence implements Serializable { public void onConfidenceChanged(Transaction tx); }; - private ConfidenceType confidenceType = ConfidenceType.UNKNOWN; - private int appearedAtChainHeight = -1; - private Transaction overridingTransaction; + /** + *

Adds an event listener that will be run when this confidence object is updated. The listener will be locked and + * is likely to be invoked on a peer thread.

+ * + *

Note that this is NOT called when every block arrives. Instead it is called when the transaction + * transitions between confidence states, ie, from not being seen in the chain to being seen (not necessarily in + * the best chain). If you want to know when the transaction gets buried under another block, implement a + * {@link BlockChainListener}, attach it to a {@link BlockChain} and then use the getters on the + * confidence object to determine the new depth.

+ */ + public synchronized void addEventListener(Listener listener) { + Preconditions.checkNotNull(listener); + if (listeners == null) + listeners = new ArrayList(2); + // Dedupe registrations. This makes the wallet code simpler. + if (!listeners.contains(listener)) + listeners.add(listener); + } - public TransactionConfidence(Transaction tx) { - // Assume a default number of peers for our set. - broadcastBy = new CopyOnWriteArrayList(); - transaction = tx; + public synchronized void removeEventListener(Listener listener) { + Preconditions.checkNotNull(listener); + Preconditions.checkNotNull(listeners); + listeners.remove(listener); } /** @@ -403,4 +412,24 @@ public class TransactionConfidence implements Serializable { } }); } + + /** + * The source of a transaction tries to identify where it came from originally. For instance, did we download it + * from the peer to peer network, or make it ourselves, or receive it via Bluetooth, or import it from another app, + * and so on. This information is useful for {@link Wallet.CoinSelector} implementations to risk analyze + * transactions and decide when to spend them. + */ + public synchronized Source getSource() { + return source; + } + + /** + * The source of a transaction tries to identify where it came from originally. For instance, did we download it + * from the peer to peer network, or make it ourselves, or receive it via Bluetooth, or import it from another app, + * and so on. This information is useful for {@link Wallet.CoinSelector} implementations to risk analyze + * transactions and decide when to spend them. + */ + public synchronized void setSource(Source source) { + this.source = source; + } } diff --git a/core/src/main/java/com/google/bitcoin/core/Wallet.java b/core/src/main/java/com/google/bitcoin/core/Wallet.java index 8fcdf2d5..f35727d0 100644 --- a/core/src/main/java/com/google/bitcoin/core/Wallet.java +++ b/core/src/main/java/com/google/bitcoin/core/Wallet.java @@ -705,6 +705,9 @@ public class Wallet implements Serializable, BlockChainListener { " and sends us %s BTC", tx.getHashAsString(), Utils.bitcoinValueToFriendlyString(valueSentFromMe), Utils.bitcoinValueToFriendlyString(valueSentToMe))); } + if (tx.getConfidence().getSource().equals(TransactionConfidence.Source.UNKNOWN)) { + log.warn("Wallet received transaction with an unknown source. Consider tagging tx!"); + } // Mark the tx as having been seen but is not yet in the chain. This will normally have been done already by // the Peer before we got to this point, but in some cases (unit tests, other sources of transactions) it may // have been missed out. @@ -1662,6 +1665,11 @@ public class Wallet implements Serializable, BlockChainListener { // happen, if it does it means the wallet has got into an inconsistent state. throw new RuntimeException(e); } + + // Label the transaction as being self created. We can use this later to spend its change output even before + // the transaction is confirmed. + req.tx.getConfidence().setSource(TransactionConfidence.Source.SELF); + req.completed = true; log.info(" completed {}", req.tx.getHashAsString()); return true; diff --git a/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java b/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java index a5c9b6e0..69245163 100644 --- a/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java +++ b/core/src/main/java/com/google/bitcoin/store/WalletProtobufSerializer.java @@ -213,6 +213,15 @@ public class WalletProtobufSerializer { Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash(); confidenceBuilder.setOverridingTransaction(hashToByteString(overridingHash)); } + TransactionConfidence.Source source = confidence.getSource(); + switch (source) { + case SELF: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_SELF); break; + case NETWORK: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_NETWORK); break; + case UNKNOWN: + // Fall through. + default: + confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_UNKNOWN); break; + } } for (ListIterator it = confidence.getBroadcastBy(); it.hasNext();) { PeerAddress address = it.next(); @@ -423,5 +432,12 @@ public class WalletProtobufSerializer { address.setServices(BigInteger.valueOf(proto.getServices())); confidence.markBroadcastBy(address); } + switch (confidenceProto.getSource()) { + case SOURCE_SELF: confidence.setSource(TransactionConfidence.Source.SELF); break; + case SOURCE_NETWORK: confidence.setSource(TransactionConfidence.Source.NETWORK); break; + case SOURCE_UNKNOWN: + // Fall through. + default: confidence.setSource(TransactionConfidence.Source.UNKNOWN); break; + } } } diff --git a/core/src/main/java/org/bitcoinj/wallet/Protos.java b/core/src/main/java/org/bitcoinj/wallet/Protos.java index 548b9e1d..f7184397 100644 --- a/core/src/main/java/org/bitcoinj/wallet/Protos.java +++ b/core/src/main/java/org/bitcoinj/wallet/Protos.java @@ -2281,6 +2281,10 @@ public final class Protos { getBroadcastByOrBuilderList(); org.bitcoinj.wallet.Protos.PeerAddressOrBuilder getBroadcastByOrBuilder( int index); + + // optional .wallet.TransactionConfidence.Source source = 7; + boolean hasSource(); + org.bitcoinj.wallet.Protos.TransactionConfidence.Source getSource(); } public static final class TransactionConfidence extends com.google.protobuf.GeneratedMessage @@ -2388,6 +2392,78 @@ public final class Protos { // @@protoc_insertion_point(enum_scope:wallet.TransactionConfidence.Type) } + public enum Source + implements com.google.protobuf.ProtocolMessageEnum { + SOURCE_UNKNOWN(0, 0), + SOURCE_NETWORK(1, 1), + SOURCE_SELF(2, 2), + ; + + public static final int SOURCE_UNKNOWN_VALUE = 0; + public static final int SOURCE_NETWORK_VALUE = 1; + public static final int SOURCE_SELF_VALUE = 2; + + + public final int getNumber() { return value; } + + public static Source valueOf(int value) { + switch (value) { + case 0: return SOURCE_UNKNOWN; + case 1: return SOURCE_NETWORK; + case 2: return SOURCE_SELF; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Source findValueByNumber(int number) { + return Source.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.bitcoinj.wallet.Protos.TransactionConfidence.getDescriptor().getEnumTypes().get(1); + } + + private static final Source[] VALUES = { + SOURCE_UNKNOWN, SOURCE_NETWORK, SOURCE_SELF, + }; + + public static Source valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Source(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:wallet.TransactionConfidence.Source) + } + private int bitField0_; // optional .wallet.TransactionConfidence.Type type = 1; public static final int TYPE_FIELD_NUMBER = 1; @@ -2460,6 +2536,16 @@ public final class Protos { return broadcastBy_.get(index); } + // optional .wallet.TransactionConfidence.Source source = 7; + public static final int SOURCE_FIELD_NUMBER = 7; + private org.bitcoinj.wallet.Protos.TransactionConfidence.Source source_; + public boolean hasSource() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + public org.bitcoinj.wallet.Protos.TransactionConfidence.Source getSource() { + return source_; + } + private void initFields() { type_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Type.UNKNOWN; appearedAtHeight_ = 0; @@ -2467,6 +2553,7 @@ public final class Protos { depth_ = 0; workDone_ = 0L; broadcastBy_ = java.util.Collections.emptyList(); + source_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Source.SOURCE_UNKNOWN; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -2504,6 +2591,9 @@ public final class Protos { for (int i = 0; i < broadcastBy_.size(); i++) { output.writeMessage(6, broadcastBy_.get(i)); } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeEnum(7, source_.getNumber()); + } getUnknownFields().writeTo(output); } @@ -2537,6 +2627,10 @@ public final class Protos { size += com.google.protobuf.CodedOutputStream .computeMessageSize(6, broadcastBy_.get(i)); } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(7, source_.getNumber()); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -2678,6 +2772,8 @@ public final class Protos { } else { broadcastByBuilder_.clear(); } + source_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Source.SOURCE_UNKNOWN; + bitField0_ = (bitField0_ & ~0x00000040); return this; } @@ -2745,6 +2841,10 @@ public final class Protos { } else { result.broadcastBy_ = broadcastByBuilder_.build(); } + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000020; + } + result.source_ = source_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -2802,6 +2902,9 @@ public final class Protos { } } } + if (other.hasSource()) { + setSource(other.getSource()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -2876,6 +2979,17 @@ public final class Protos { addBroadcastBy(subBuilder.buildPartial()); break; } + case 56: { + int rawValue = input.readEnum(); + org.bitcoinj.wallet.Protos.TransactionConfidence.Source value = org.bitcoinj.wallet.Protos.TransactionConfidence.Source.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(7, rawValue); + } else { + bitField0_ |= 0x00000040; + source_ = value; + } + break; + } } } } @@ -3179,6 +3293,30 @@ public final class Protos { return broadcastByBuilder_; } + // optional .wallet.TransactionConfidence.Source source = 7; + private org.bitcoinj.wallet.Protos.TransactionConfidence.Source source_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Source.SOURCE_UNKNOWN; + public boolean hasSource() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + public org.bitcoinj.wallet.Protos.TransactionConfidence.Source getSource() { + return source_; + } + public Builder setSource(org.bitcoinj.wallet.Protos.TransactionConfidence.Source value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + source_ = value; + onChanged(); + return this; + } + public Builder clearSource() { + bitField0_ = (bitField0_ & ~0x00000040); + source_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Source.SOURCE_UNKNOWN; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:wallet.TransactionConfidence) } @@ -6570,31 +6708,34 @@ public final class Protos { "ence\030\004 \001(\r\"\177\n\021TransactionOutput\022\r\n\005value", "\030\001 \002(\003\022\024\n\014script_bytes\030\002 \002(\014\022!\n\031spent_by" + "_transaction_hash\030\003 \001(\014\022\"\n\032spent_by_tran" + - "saction_index\030\004 \001(\005\"\255\002\n\025TransactionConfi" + + "saction_index\030\004 \001(\005\"\246\003\n\025TransactionConfi" + "dence\0220\n\004type\030\001 \001(\0162\".wallet.Transaction" + "Confidence.Type\022\032\n\022appeared_at_height\030\002 " + "\001(\005\022\036\n\026overriding_transaction\030\003 \001(\014\022\r\n\005d" + "epth\030\004 \001(\005\022\021\n\twork_done\030\005 \001(\003\022)\n\014broadca" + - "st_by\030\006 \003(\0132\023.wallet.PeerAddress\"Y\n\004Type" + - "\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDING\020\001\022\025\n\021NOT_SEEN_" + - "IN_CHAIN\020\002\022\025\n\021NOT_IN_BEST_CHAIN\020\003\022\010\n\004DEA", - "D\020\004\"\211\003\n\013Transaction\022\017\n\007version\030\001 \002(\005\022\014\n\004" + - "hash\030\002 \002(\014\022&\n\004pool\030\003 \001(\0162\030.wallet.Transa" + - "ction.Pool\022\021\n\tlock_time\030\004 \001(\r\022\022\n\nupdated" + - "_at\030\005 \001(\003\0223\n\021transaction_input\030\006 \003(\0132\030.w" + - "allet.TransactionInput\0225\n\022transaction_ou" + - "tput\030\007 \003(\0132\031.wallet.TransactionOutput\022\022\n" + - "\nblock_hash\030\010 \003(\014\0221\n\nconfidence\030\t \001(\0132\035." + - "wallet.TransactionConfidence\"Y\n\004Pool\022\013\n\007" + - "UNSPENT\020\004\022\t\n\005SPENT\020\005\022\014\n\010INACTIVE\020\002\022\010\n\004DE" + - "AD\020\n\022\013\n\007PENDING\020\020\022\024\n\020PENDING_INACTIVE\020\022\"", - "8\n\tExtension\022\n\n\002id\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\022\021" + - "\n\tmandatory\030\003 \002(\010\"\254\001\n\006Wallet\022\032\n\022network_" + - "identifier\030\001 \002(\t\022\034\n\024last_seen_block_hash" + - "\030\002 \001(\014\022\030\n\003key\030\003 \003(\0132\013.wallet.Key\022(\n\013tran" + - "saction\030\004 \003(\0132\023.wallet.Transaction\022$\n\tex" + - "tension\030\n \003(\0132\021.wallet.ExtensionB\035\n\023org." + - "bitcoinj.walletB\006Protos" + "st_by\030\006 \003(\0132\023.wallet.PeerAddress\0224\n\006sour" + + "ce\030\007 \001(\0162$.wallet.TransactionConfidence." + + "Source\"Y\n\004Type\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDING\020", + "\001\022\025\n\021NOT_SEEN_IN_CHAIN\020\002\022\025\n\021NOT_IN_BEST_" + + "CHAIN\020\003\022\010\n\004DEAD\020\004\"A\n\006Source\022\022\n\016SOURCE_UN" + + "KNOWN\020\000\022\022\n\016SOURCE_NETWORK\020\001\022\017\n\013SOURCE_SE" + + "LF\020\002\"\211\003\n\013Transaction\022\017\n\007version\030\001 \002(\005\022\014\n" + + "\004hash\030\002 \002(\014\022&\n\004pool\030\003 \001(\0162\030.wallet.Trans" + + "action.Pool\022\021\n\tlock_time\030\004 \001(\r\022\022\n\nupdate" + + "d_at\030\005 \001(\003\0223\n\021transaction_input\030\006 \003(\0132\030." + + "wallet.TransactionInput\0225\n\022transaction_o" + + "utput\030\007 \003(\0132\031.wallet.TransactionOutput\022\022" + + "\n\nblock_hash\030\010 \003(\014\0221\n\nconfidence\030\t \001(\0132\035", + ".wallet.TransactionConfidence\"Y\n\004Pool\022\013\n" + + "\007UNSPENT\020\004\022\t\n\005SPENT\020\005\022\014\n\010INACTIVE\020\002\022\010\n\004D" + + "EAD\020\n\022\013\n\007PENDING\020\020\022\024\n\020PENDING_INACTIVE\020\022" + + "\"8\n\tExtension\022\n\n\002id\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\022" + + "\021\n\tmandatory\030\003 \002(\010\"\254\001\n\006Wallet\022\032\n\022network" + + "_identifier\030\001 \002(\t\022\034\n\024last_seen_block_has" + + "h\030\002 \001(\014\022\030\n\003key\030\003 \003(\0132\013.wallet.Key\022(\n\013tra" + + "nsaction\030\004 \003(\0132\023.wallet.Transaction\022$\n\te" + + "xtension\030\n \003(\0132\021.wallet.ExtensionB\035\n\023org" + + ".bitcoinj.walletB\006Protos" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -6638,7 +6779,7 @@ public final class Protos { internal_static_wallet_TransactionConfidence_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_wallet_TransactionConfidence_descriptor, - new java.lang.String[] { "Type", "AppearedAtHeight", "OverridingTransaction", "Depth", "WorkDone", "BroadcastBy", }, + new java.lang.String[] { "Type", "AppearedAtHeight", "OverridingTransaction", "Depth", "WorkDone", "BroadcastBy", "Source", }, org.bitcoinj.wallet.Protos.TransactionConfidence.class, org.bitcoinj.wallet.Protos.TransactionConfidence.Builder.class); internal_static_wallet_Transaction_descriptor = diff --git a/core/src/test/java/com/google/bitcoin/core/TestUtils.java b/core/src/test/java/com/google/bitcoin/core/TestUtils.java index 08ff6f2c..14bc8df6 100644 --- a/core/src/test/java/com/google/bitcoin/core/TestUtils.java +++ b/core/src/test/java/com/google/bitcoin/core/TestUtils.java @@ -153,8 +153,10 @@ public class TestUtils { Address to = new ECKey().toAddress(chainHead.params); Block b = chainHead.createNextBlock(to, timeSeconds); // Coinbase tx was already added. - for (Transaction tx : transactions) + for (Transaction tx : transactions) { + tx.getConfidence().setSource(TransactionConfidence.Source.NETWORK); b.addTransaction(tx); + } b.solve(); BlockPair pair = new BlockPair(); pair.block = b; diff --git a/core/src/test/java/com/google/bitcoin/core/WalletTest.java b/core/src/test/java/com/google/bitcoin/core/WalletTest.java index 868c5ad3..149df7b5 100644 --- a/core/src/test/java/com/google/bitcoin/core/WalletTest.java +++ b/core/src/test/java/com/google/bitcoin/core/WalletTest.java @@ -94,6 +94,7 @@ public class WalletTest { Transaction t2 = req.tx; assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL)); + assertEquals(TransactionConfidence.Source.SELF, t2.getConfidence().getSource()); // Do some basic sanity checks. assertEquals(1, t2.getInputs().size()); diff --git a/core/src/test/java/com/google/bitcoin/store/WalletProtobufSerializerTest.java b/core/src/test/java/com/google/bitcoin/store/WalletProtobufSerializerTest.java index 9ce456e5..e105e166 100644 --- a/core/src/test/java/com/google/bitcoin/store/WalletProtobufSerializerTest.java +++ b/core/src/test/java/com/google/bitcoin/store/WalletProtobufSerializerTest.java @@ -56,13 +56,15 @@ public class WalletProtobufSerializerTest { Transaction t1 = createFakeTx(params, v1, myAddress); t1.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("1.2.3.4"))); t1.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("5.6.7.8"))); - myWallet.receiveFromBlock(t1, null, BlockChain.NewBlockType.BEST_CHAIN); + t1.getConfidence().setSource(TransactionConfidence.Source.NETWORK); + myWallet.receivePending(t1, new ArrayList()); Wallet wallet1 = roundTrip(myWallet); assertEquals(1, wallet1.getTransactions(true, true).size()); - assertEquals(v1, wallet1.getBalance()); - assertArrayEquals(t1.bitcoinSerialize(), - wallet1.getTransaction(t1.getHash()).bitcoinSerialize()); - assertEquals(2, wallet1.getTransaction(t1.getHash()).getConfidence().numBroadcastPeers()); + assertEquals(v1, wallet1.getBalance(Wallet.BalanceType.ESTIMATED)); + Transaction t1copy = wallet1.getTransaction(t1.getHash()); + assertArrayEquals(t1.bitcoinSerialize(), t1copy.bitcoinSerialize()); + assertEquals(2, t1copy.getConfidence().numBroadcastPeers()); + assertEquals(TransactionConfidence.Source.NETWORK, t1copy.getConfidence().getSource()); Protos.Wallet walletProto = new WalletProtobufSerializer().walletToProto(myWallet); assertEquals(Protos.Key.Type.ORIGINAL, walletProto.getKey(0).getType()); @@ -73,7 +75,7 @@ public class WalletProtobufSerializerTest { Protos.Transaction t1p = walletProto.getTransaction(0); assertEquals(0, t1p.getBlockHashCount()); assertArrayEquals(t1.getHash().getBytes(), t1p.getHash().toByteArray()); - assertEquals(Protos.Transaction.Pool.UNSPENT, t1p.getPool()); + assertEquals(Protos.Transaction.Pool.PENDING, t1p.getPool()); assertFalse(t1p.hasLockTime()); assertFalse(t1p.getTransactionInput(0).hasSequence()); assertArrayEquals(t1.getInputs().get(0).getOutpoint().getHash().getBytes(),