From 1c28bd3972e93e7eeffbe4b777b13de925b45f47 Mon Sep 17 00:00:00 2001 From: Miron Cuperman Date: Wed, 25 Jan 2012 10:40:20 -0800 Subject: [PATCH] Persist TransactionConfidence to protobuf --- src/bitcoin.proto | 16 + src/com/google/bitcoin/core/Transaction.java | 5 + .../bitcoin/core/TransactionConfidence.java | 35 +- src/com/google/bitcoin/core/Wallet.java | 1 - .../store/WalletProtobufSerializer.java | 32 + src/org/bitcoinj/wallet/Protos.java | 765 +++++++++++++++++- .../store/WalletProtobufSerializerTest.java | 9 + 7 files changed, 835 insertions(+), 28 deletions(-) diff --git a/src/bitcoin.proto b/src/bitcoin.proto index 2eaf2876..4a971bea 100644 --- a/src/bitcoin.proto +++ b/src/bitcoin.proto @@ -53,6 +53,20 @@ message TransactionOutput { // if spent, the index of the transaction output of the transaction doing the spend } +message TransactionConfidence { + enum Type { + UNKNOWN = 0; + BUILDING = 1; + NOT_SEEN_IN_CHAIN = 2; + NOT_IN_BEST_CHAIN = 3; + OVERRIDDEN_BY_DOUBLE_SPEND = 4; + } + + required Type type = 1; + optional int32 appeared_at_height = 2; + optional bytes overriding_transaction = 3; +} + message Transaction { /** * This is a bitfield oriented enum, with the following bits: @@ -88,6 +102,8 @@ message Transaction { repeated bytes block_hash = 8; + + optional TransactionConfidence confidence = 9; // Sha256Hash of block in block chain in which this transaction appears } diff --git a/src/com/google/bitcoin/core/Transaction.java b/src/com/google/bitcoin/core/Transaction.java index 050a551d..5d32d8b9 100644 --- a/src/com/google/bitcoin/core/Transaction.java +++ b/src/com/google/bitcoin/core/Transaction.java @@ -707,6 +707,11 @@ public class Transaction extends ChildMessage implements Serializable { return confidence; } + + public boolean hasConfidence() { + return confidence != null && confidence.getConfidenceType() != TransactionConfidence.ConfidenceType.UNKNOWN; + } + @Override public boolean equals(Object other) { if (!(other instanceof Transaction)) return false; diff --git a/src/com/google/bitcoin/core/TransactionConfidence.java b/src/com/google/bitcoin/core/TransactionConfidence.java index 4a37672c..d8ccb9e7 100644 --- a/src/com/google/bitcoin/core/TransactionConfidence.java +++ b/src/com/google/bitcoin/core/TransactionConfidence.java @@ -95,7 +95,7 @@ public class TransactionConfidence implements Serializable { /** 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. */ - BUILDING, + BUILDING(1), /** * If NOT_SEEN_IN_CHAIN, then the transaction is pending and should be included shortly. You can estimate how @@ -103,26 +103,47 @@ public class TransactionConfidence implements Serializable { * it, using {@link com.google.bitcoin.core.TransactionConfidence#numBroadcastPeers()}. Or if you saw it from * a trusted peer, you can assume it's valid and will get mined sooner or later as well. */ - NOT_SEEN_IN_CHAIN, + NOT_SEEN_IN_CHAIN(2), /** * If NOT_IN_BEST_CHAIN, then the transaction has been included in a block, but that block is on a fork. A * transaction can change from BUILDING to NOT_IN_BEST_CHAIN and vice versa if a reorganization takes place, * due to a split in the consensus. */ - NOT_IN_BEST_CHAIN, + NOT_IN_BEST_CHAIN(3), /** * If OVERRIDDEN_BY_DOUBLE_SPEND, then it means the transaction won't confirm unless there is another re-org, * because some other transaction is spending one of its inputs. Such transactions should be alerted to the user * so they can take action, eg, suspending shipment of goods if they are a merchant. */ - OVERRIDDEN_BY_DOUBLE_SPEND, + OVERRIDDEN_BY_DOUBLE_SPEND(4), /** * If a transaction hasn't been broadcast yet, or there's no record of it, its confidence is UNKNOWN. */ - UNKNOWN + UNKNOWN(0); + + private int value; + ConfidenceType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static ConfidenceType valueOf(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return BUILDING; + case 2: return NOT_SEEN_IN_CHAIN; + case 3: return NOT_IN_BEST_CHAIN; + case 4: return OVERRIDDEN_BY_DOUBLE_SPEND; + default: return null; + } + } + }; /** @@ -139,7 +160,7 @@ public class TransactionConfidence implements Serializable { private int appearedAtChainHeight = -1; private Transaction overridingTransaction; - TransactionConfidence(Transaction tx) { + public TransactionConfidence(Transaction tx) { // Assume a default number of peers for our set. broadcastBy = new HashSet(10); transaction = tx; @@ -311,7 +332,7 @@ public class TransactionConfidence implements Serializable { /** * Called when the transaction becomes newly dead, that is, we learn that one of its inputs has already been spent - * in such a way that the double-spending transaction takes precence over this one. It will not become valid now + * in such a way that the double-spending transaction takes precedence over this one. It will not become valid now * unless there is a re-org. Automatically sets the confidence type to OVERRIDDEN_BY_DOUBLE_SPEND. */ public synchronized void setOverridingTransaction(Transaction overridingTransaction) { diff --git a/src/com/google/bitcoin/core/Wallet.java b/src/com/google/bitcoin/core/Wallet.java index e1e2ad9b..e1224e0a 100644 --- a/src/com/google/bitcoin/core/Wallet.java +++ b/src/com/google/bitcoin/core/Wallet.java @@ -25,7 +25,6 @@ import java.io.*; import java.math.BigInteger; import java.util.*; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import static com.google.bitcoin.core.Utils.bitcoinValueToFriendlyString; diff --git a/src/com/google/bitcoin/store/WalletProtobufSerializer.java b/src/com/google/bitcoin/store/WalletProtobufSerializer.java index 45cf29e5..0abc61a8 100644 --- a/src/com/google/bitcoin/store/WalletProtobufSerializer.java +++ b/src/com/google/bitcoin/store/WalletProtobufSerializer.java @@ -20,11 +20,13 @@ import com.google.bitcoin.core.ECKey; import com.google.bitcoin.core.NetworkParameters; import com.google.bitcoin.core.Sha256Hash; import com.google.bitcoin.core.Transaction; +import com.google.bitcoin.core.TransactionConfidence; import com.google.bitcoin.core.TransactionInput; import com.google.bitcoin.core.TransactionOutPoint; import com.google.bitcoin.core.TransactionOutput; import com.google.bitcoin.core.Wallet; import com.google.bitcoin.core.WalletTransaction; +import com.google.bitcoin.core.TransactionConfidence.ConfidenceType; import com.google.protobuf.ByteString; import com.google.protobuf.TextFormat; @@ -138,6 +140,22 @@ public class WalletProtobufSerializer { } } + if (tx.hasConfidence()) { + TransactionConfidence confidence = tx.getConfidence(); + Protos.TransactionConfidence.Builder confidenceBuilder = + Protos.TransactionConfidence.newBuilder(); + + confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue())); + if (confidence.getConfidenceType() == ConfidenceType.BUILDING) { + confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight()); + } + if (confidence.getConfidenceType() == ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND) { + confidenceBuilder.setOverridingTransaction(ByteString.copyFrom(confidence.getOverridingTransaction().getHash().getBytes())); + } + + txBuilder.setConfidence(confidenceBuilder); + } + return txBuilder.build(); } @@ -248,6 +266,20 @@ public class WalletProtobufSerializer { output.markAsSpent(spendingTx.getInputs().get(spendingIndex)); } } + + if(txProto.hasConfidence()) { + Protos.TransactionConfidence confidenceProto = txProto.getConfidence(); + TransactionConfidence confidence = tx.getConfidence(); + confidence.setConfidenceType(TransactionConfidence.ConfidenceType.valueOf(confidenceProto.getType().getNumber())); + if (confidenceProto.hasAppearedAtHeight()) { + assert confidence.getConfidenceType() == ConfidenceType.BUILDING; + confidence.setAppearedAtChainHeight(confidenceProto.getAppearedAtHeight()); + } + if (confidenceProto.hasOverridingTransaction()) { + assert confidence.getConfidenceType() == ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND; + confidence.setOverridingTransaction(txMap.get(confidenceProto.getOverridingTransaction())); + } + } return new WalletTransaction(pool, tx); } diff --git a/src/org/bitcoinj/wallet/Protos.java b/src/org/bitcoinj/wallet/Protos.java index e7deff8a..53c9f7b7 100644 --- a/src/org/bitcoinj/wallet/Protos.java +++ b/src/org/bitcoinj/wallet/Protos.java @@ -1718,6 +1718,556 @@ public final class Protos { // @@protoc_insertion_point(class_scope:wallet.TransactionOutput) } + public interface TransactionConfidenceOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // required .wallet.TransactionConfidence.Type type = 1; + boolean hasType(); + org.bitcoinj.wallet.Protos.TransactionConfidence.Type getType(); + + // optional int32 appeared_at_height = 2; + boolean hasAppearedAtHeight(); + int getAppearedAtHeight(); + + // optional bytes overriding_transaction = 3; + boolean hasOverridingTransaction(); + com.google.protobuf.ByteString getOverridingTransaction(); + } + public static final class TransactionConfidence extends + com.google.protobuf.GeneratedMessage + implements TransactionConfidenceOrBuilder { + // Use TransactionConfidence.newBuilder() to construct. + private TransactionConfidence(Builder builder) { + super(builder); + } + private TransactionConfidence(boolean noInit) {} + + private static final TransactionConfidence defaultInstance; + public static TransactionConfidence getDefaultInstance() { + return defaultInstance; + } + + public TransactionConfidence getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.bitcoinj.wallet.Protos.internal_static_wallet_TransactionConfidence_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.bitcoinj.wallet.Protos.internal_static_wallet_TransactionConfidence_fieldAccessorTable; + } + + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + UNKNOWN(0, 0), + BUILDING(1, 1), + NOT_SEEN_IN_CHAIN(2, 2), + NOT_IN_BEST_CHAIN(3, 3), + OVERRIDDEN_BY_DOUBLE_SPEND(4, 4), + ; + + public static final int UNKNOWN_VALUE = 0; + public static final int BUILDING_VALUE = 1; + public static final int NOT_SEEN_IN_CHAIN_VALUE = 2; + public static final int NOT_IN_BEST_CHAIN_VALUE = 3; + public static final int OVERRIDDEN_BY_DOUBLE_SPEND_VALUE = 4; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return BUILDING; + case 2: return NOT_SEEN_IN_CHAIN; + case 3: return NOT_IN_BEST_CHAIN; + case 4: return OVERRIDDEN_BY_DOUBLE_SPEND; + 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 Type findValueByNumber(int number) { + return Type.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(0); + } + + private static final Type[] VALUES = { + UNKNOWN, BUILDING, NOT_SEEN_IN_CHAIN, NOT_IN_BEST_CHAIN, OVERRIDDEN_BY_DOUBLE_SPEND, + }; + + public static Type 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 Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:wallet.TransactionConfidence.Type) + } + + private int bitField0_; + // required .wallet.TransactionConfidence.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private org.bitcoinj.wallet.Protos.TransactionConfidence.Type type_; + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public org.bitcoinj.wallet.Protos.TransactionConfidence.Type getType() { + return type_; + } + + // optional int32 appeared_at_height = 2; + public static final int APPEARED_AT_HEIGHT_FIELD_NUMBER = 2; + private int appearedAtHeight_; + public boolean hasAppearedAtHeight() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public int getAppearedAtHeight() { + return appearedAtHeight_; + } + + // optional bytes overriding_transaction = 3; + public static final int OVERRIDING_TRANSACTION_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString overridingTransaction_; + public boolean hasOverridingTransaction() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + public com.google.protobuf.ByteString getOverridingTransaction() { + return overridingTransaction_; + } + + private void initFields() { + type_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Type.UNKNOWN; + appearedAtHeight_ = 0; + overridingTransaction_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + if (!hasType()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeEnum(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeInt32(2, appearedAtHeight_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, overridingTransaction_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, appearedAtHeight_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, overridingTransaction_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.bitcoinj.wallet.Protos.TransactionConfidence parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.bitcoinj.wallet.Protos.TransactionConfidence prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.bitcoinj.wallet.Protos.TransactionConfidenceOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.bitcoinj.wallet.Protos.internal_static_wallet_TransactionConfidence_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.bitcoinj.wallet.Protos.internal_static_wallet_TransactionConfidence_fieldAccessorTable; + } + + // Construct using org.bitcoinj.wallet.Protos.TransactionConfidence.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Type.UNKNOWN; + bitField0_ = (bitField0_ & ~0x00000001); + appearedAtHeight_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + overridingTransaction_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.bitcoinj.wallet.Protos.TransactionConfidence.getDescriptor(); + } + + public org.bitcoinj.wallet.Protos.TransactionConfidence getDefaultInstanceForType() { + return org.bitcoinj.wallet.Protos.TransactionConfidence.getDefaultInstance(); + } + + public org.bitcoinj.wallet.Protos.TransactionConfidence build() { + org.bitcoinj.wallet.Protos.TransactionConfidence result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + private org.bitcoinj.wallet.Protos.TransactionConfidence buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + org.bitcoinj.wallet.Protos.TransactionConfidence result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return result; + } + + public org.bitcoinj.wallet.Protos.TransactionConfidence buildPartial() { + org.bitcoinj.wallet.Protos.TransactionConfidence result = new org.bitcoinj.wallet.Protos.TransactionConfidence(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.appearedAtHeight_ = appearedAtHeight_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.overridingTransaction_ = overridingTransaction_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.bitcoinj.wallet.Protos.TransactionConfidence) { + return mergeFrom((org.bitcoinj.wallet.Protos.TransactionConfidence)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.bitcoinj.wallet.Protos.TransactionConfidence other) { + if (other == org.bitcoinj.wallet.Protos.TransactionConfidence.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasAppearedAtHeight()) { + setAppearedAtHeight(other.getAppearedAtHeight()); + } + if (other.hasOverridingTransaction()) { + setOverridingTransaction(other.getOverridingTransaction()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + if (!hasType()) { + + return false; + } + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + org.bitcoinj.wallet.Protos.TransactionConfidence.Type value = org.bitcoinj.wallet.Protos.TransactionConfidence.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 16: { + bitField0_ |= 0x00000002; + appearedAtHeight_ = input.readInt32(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + overridingTransaction_ = input.readBytes(); + break; + } + } + } + } + + private int bitField0_; + + // required .wallet.TransactionConfidence.Type type = 1; + private org.bitcoinj.wallet.Protos.TransactionConfidence.Type type_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Type.UNKNOWN; + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public org.bitcoinj.wallet.Protos.TransactionConfidence.Type getType() { + return type_; + } + public Builder setType(org.bitcoinj.wallet.Protos.TransactionConfidence.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.bitcoinj.wallet.Protos.TransactionConfidence.Type.UNKNOWN; + onChanged(); + return this; + } + + // optional int32 appeared_at_height = 2; + private int appearedAtHeight_ ; + public boolean hasAppearedAtHeight() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public int getAppearedAtHeight() { + return appearedAtHeight_; + } + public Builder setAppearedAtHeight(int value) { + bitField0_ |= 0x00000002; + appearedAtHeight_ = value; + onChanged(); + return this; + } + public Builder clearAppearedAtHeight() { + bitField0_ = (bitField0_ & ~0x00000002); + appearedAtHeight_ = 0; + onChanged(); + return this; + } + + // optional bytes overriding_transaction = 3; + private com.google.protobuf.ByteString overridingTransaction_ = com.google.protobuf.ByteString.EMPTY; + public boolean hasOverridingTransaction() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + public com.google.protobuf.ByteString getOverridingTransaction() { + return overridingTransaction_; + } + public Builder setOverridingTransaction(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + overridingTransaction_ = value; + onChanged(); + return this; + } + public Builder clearOverridingTransaction() { + bitField0_ = (bitField0_ & ~0x00000004); + overridingTransaction_ = getDefaultInstance().getOverridingTransaction(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:wallet.TransactionConfidence) + } + + static { + defaultInstance = new TransactionConfidence(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:wallet.TransactionConfidence) + } + public interface TransactionOrBuilder extends com.google.protobuf.MessageOrBuilder { @@ -1765,6 +2315,11 @@ public final class Protos { java.util.List getBlockHashList(); int getBlockHashCount(); com.google.protobuf.ByteString getBlockHash(int index); + + // optional .wallet.TransactionConfidence confidence = 9; + boolean hasConfidence(); + org.bitcoinj.wallet.Protos.TransactionConfidence getConfidence(); + org.bitcoinj.wallet.Protos.TransactionConfidenceOrBuilder getConfidenceOrBuilder(); } public static final class Transaction extends com.google.protobuf.GeneratedMessage @@ -1982,6 +2537,19 @@ public final class Protos { return blockHash_.get(index); } + // optional .wallet.TransactionConfidence confidence = 9; + public static final int CONFIDENCE_FIELD_NUMBER = 9; + private org.bitcoinj.wallet.Protos.TransactionConfidence confidence_; + public boolean hasConfidence() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + public org.bitcoinj.wallet.Protos.TransactionConfidence getConfidence() { + return confidence_; + } + public org.bitcoinj.wallet.Protos.TransactionConfidenceOrBuilder getConfidenceOrBuilder() { + return confidence_; + } + private void initFields() { version_ = 0; hash_ = com.google.protobuf.ByteString.EMPTY; @@ -1991,6 +2559,7 @@ public final class Protos { transactionInput_ = java.util.Collections.emptyList(); transactionOutput_ = java.util.Collections.emptyList(); blockHash_ = java.util.Collections.emptyList();; + confidence_ = org.bitcoinj.wallet.Protos.TransactionConfidence.getDefaultInstance(); } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -2021,6 +2590,12 @@ public final class Protos { return false; } } + if (hasConfidence()) { + if (!getConfidence().isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } memoizedIsInitialized = 1; return true; } @@ -2052,6 +2627,9 @@ public final class Protos { for (int i = 0; i < blockHash_.size(); i++) { output.writeBytes(8, blockHash_.get(i)); } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeMessage(9, confidence_); + } getUnknownFields().writeTo(output); } @@ -2098,6 +2676,10 @@ public final class Protos { size += dataSize; size += 1 * getBlockHashList().size(); } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(9, confidence_); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -2216,6 +2798,7 @@ public final class Protos { if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { getTransactionInputFieldBuilder(); getTransactionOutputFieldBuilder(); + getConfidenceFieldBuilder(); } } private static Builder create() { @@ -2248,6 +2831,12 @@ public final class Protos { } blockHash_ = java.util.Collections.emptyList();; bitField0_ = (bitField0_ & ~0x00000080); + if (confidenceBuilder_ == null) { + confidence_ = org.bitcoinj.wallet.Protos.TransactionConfidence.getDefaultInstance(); + } else { + confidenceBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000100); return this; } @@ -2329,6 +2918,14 @@ public final class Protos { bitField0_ = (bitField0_ & ~0x00000080); } result.blockHash_ = blockHash_; + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000020; + } + if (confidenceBuilder_ == null) { + result.confidence_ = confidence_; + } else { + result.confidence_ = confidenceBuilder_.build(); + } result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -2422,6 +3019,9 @@ public final class Protos { } onChanged(); } + if (other.hasConfidence()) { + mergeConfidence(other.getConfidence()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -2451,6 +3051,12 @@ public final class Protos { return false; } } + if (hasConfidence()) { + if (!getConfidence().isInitialized()) { + + return false; + } + } return true; } @@ -2525,6 +3131,15 @@ public final class Protos { blockHash_.add(input.readBytes()); break; } + case 74: { + org.bitcoinj.wallet.Protos.TransactionConfidence.Builder subBuilder = org.bitcoinj.wallet.Protos.TransactionConfidence.newBuilder(); + if (hasConfidence()) { + subBuilder.mergeFrom(getConfidence()); + } + input.readMessage(subBuilder, extensionRegistry); + setConfidence(subBuilder.buildPartial()); + break; + } } } } @@ -3065,6 +3680,96 @@ public final class Protos { return this; } + // optional .wallet.TransactionConfidence confidence = 9; + private org.bitcoinj.wallet.Protos.TransactionConfidence confidence_ = org.bitcoinj.wallet.Protos.TransactionConfidence.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.bitcoinj.wallet.Protos.TransactionConfidence, org.bitcoinj.wallet.Protos.TransactionConfidence.Builder, org.bitcoinj.wallet.Protos.TransactionConfidenceOrBuilder> confidenceBuilder_; + public boolean hasConfidence() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + public org.bitcoinj.wallet.Protos.TransactionConfidence getConfidence() { + if (confidenceBuilder_ == null) { + return confidence_; + } else { + return confidenceBuilder_.getMessage(); + } + } + public Builder setConfidence(org.bitcoinj.wallet.Protos.TransactionConfidence value) { + if (confidenceBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + confidence_ = value; + onChanged(); + } else { + confidenceBuilder_.setMessage(value); + } + bitField0_ |= 0x00000100; + return this; + } + public Builder setConfidence( + org.bitcoinj.wallet.Protos.TransactionConfidence.Builder builderForValue) { + if (confidenceBuilder_ == null) { + confidence_ = builderForValue.build(); + onChanged(); + } else { + confidenceBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000100; + return this; + } + public Builder mergeConfidence(org.bitcoinj.wallet.Protos.TransactionConfidence value) { + if (confidenceBuilder_ == null) { + if (((bitField0_ & 0x00000100) == 0x00000100) && + confidence_ != org.bitcoinj.wallet.Protos.TransactionConfidence.getDefaultInstance()) { + confidence_ = + org.bitcoinj.wallet.Protos.TransactionConfidence.newBuilder(confidence_).mergeFrom(value).buildPartial(); + } else { + confidence_ = value; + } + onChanged(); + } else { + confidenceBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000100; + return this; + } + public Builder clearConfidence() { + if (confidenceBuilder_ == null) { + confidence_ = org.bitcoinj.wallet.Protos.TransactionConfidence.getDefaultInstance(); + onChanged(); + } else { + confidenceBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000100); + return this; + } + public org.bitcoinj.wallet.Protos.TransactionConfidence.Builder getConfidenceBuilder() { + bitField0_ |= 0x00000100; + onChanged(); + return getConfidenceFieldBuilder().getBuilder(); + } + public org.bitcoinj.wallet.Protos.TransactionConfidenceOrBuilder getConfidenceOrBuilder() { + if (confidenceBuilder_ != null) { + return confidenceBuilder_.getMessageOrBuilder(); + } else { + return confidence_; + } + } + private com.google.protobuf.SingleFieldBuilder< + org.bitcoinj.wallet.Protos.TransactionConfidence, org.bitcoinj.wallet.Protos.TransactionConfidence.Builder, org.bitcoinj.wallet.Protos.TransactionConfidenceOrBuilder> + getConfidenceFieldBuilder() { + if (confidenceBuilder_ == null) { + confidenceBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.bitcoinj.wallet.Protos.TransactionConfidence, org.bitcoinj.wallet.Protos.TransactionConfidence.Builder, org.bitcoinj.wallet.Protos.TransactionConfidenceOrBuilder>( + confidence_, + getParentForChildren(), + isClean()); + confidence_ = null; + } + return confidenceBuilder_; + } + // @@protoc_insertion_point(builder_scope:wallet.Transaction) } @@ -4905,6 +5610,11 @@ public final class Protos { private static com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_wallet_TransactionOutput_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_wallet_TransactionConfidence_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_wallet_TransactionConfidence_fieldAccessorTable; private static com.google.protobuf.Descriptors.Descriptor internal_static_wallet_Transaction_descriptor; private static @@ -4939,22 +5649,29 @@ public final class Protos { "(\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_transac" + "tion_hash\030\003 \001(\014\022\"\n\032spent_by_transaction_", - "index\030\004 \001(\005\"\326\002\n\013Transaction\022\017\n\007version\030\001" + - " \002(\005\022\014\n\004hash\030\002 \002(\014\022&\n\004pool\030\003 \002(\0162\030.walle" + - "t.Transaction.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.wallet.TransactionInput\0225\n\022transa" + - "ction_output\030\007 \003(\0132\031.wallet.TransactionO" + - "utput\022\022\n\nblock_hash\030\010 \003(\014\"Y\n\004Pool\022\013\n\007UNS" + - "PENT\020\004\022\t\n\005SPENT\020\005\022\014\n\010INACTIVE\020\002\022\010\n\004DEAD\020" + - "\n\022\013\n\007PENDING\020\020\022\024\n\020PENDING_INACTIVE\020\022\"8\n\t" + - "Extension\022\n\n\002id\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\022\021\n\tm", - "andatory\030\003 \002(\010\"\254\001\n\006Wallet\022\032\n\022network_ide" + - "ntifier\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\013transac" + - "tion\030\004 \003(\0132\023.wallet.Transaction\022$\n\texten" + - "sion\030\n \003(\0132\021.wallet.ExtensionB\035\n\023org.bit" + - "coinj.walletB\006Protos" + "index\030\004 \001(\005\"\366\001\n\025TransactionConfidence\0220\n" + + "\004type\030\001 \002(\0162\".wallet.TransactionConfiden" + + "ce.Type\022\032\n\022appeared_at_height\030\002 \001(\005\022\036\n\026o" + + "verriding_transaction\030\003 \001(\014\"o\n\004Type\022\013\n\007U" + + "NKNOWN\020\000\022\014\n\010BUILDING\020\001\022\025\n\021NOT_SEEN_IN_CH" + + "AIN\020\002\022\025\n\021NOT_IN_BEST_CHAIN\020\003\022\036\n\032OVERRIDD" + + "EN_BY_DOUBLE_SPEND\020\004\"\211\003\n\013Transaction\022\017\n\007" + + "version\030\001 \002(\005\022\014\n\004hash\030\002 \002(\014\022&\n\004pool\030\003 \002(" + + "\0162\030.wallet.Transaction.Pool\022\021\n\tlock_time" + + "\030\004 \001(\r\022\022\n\nupdated_at\030\005 \001(\003\0223\n\021transactio", + "n_input\030\006 \003(\0132\030.wallet.TransactionInput\022" + + "5\n\022transaction_output\030\007 \003(\0132\031.wallet.Tra" + + "nsactionOutput\022\022\n\nblock_hash\030\010 \003(\014\0221\n\nco" + + "nfidence\030\t \001(\0132\035.wallet.TransactionConfi" + + "dence\"Y\n\004Pool\022\013\n\007UNSPENT\020\004\022\t\n\005SPENT\020\005\022\014\n" + + "\010INACTIVE\020\002\022\010\n\004DEAD\020\n\022\013\n\007PENDING\020\020\022\024\n\020PE" + + "NDING_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\006W" + + "allet\022\032\n\022network_identifier\030\001 \002(\t\022\034\n\024las" + + "t_seen_block_hash\030\002 \001(\014\022\030\n\003key\030\003 \003(\0132\013.w", + "allet.Key\022(\n\013transaction\030\004 \003(\0132\023.wallet." + + "Transaction\022$\n\textension\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() { @@ -4985,16 +5702,24 @@ public final class Protos { new java.lang.String[] { "Value", "ScriptBytes", "SpentByTransactionHash", "SpentByTransactionIndex", }, org.bitcoinj.wallet.Protos.TransactionOutput.class, org.bitcoinj.wallet.Protos.TransactionOutput.Builder.class); - internal_static_wallet_Transaction_descriptor = + internal_static_wallet_TransactionConfidence_descriptor = getDescriptor().getMessageTypes().get(3); + internal_static_wallet_TransactionConfidence_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_wallet_TransactionConfidence_descriptor, + new java.lang.String[] { "Type", "AppearedAtHeight", "OverridingTransaction", }, + org.bitcoinj.wallet.Protos.TransactionConfidence.class, + org.bitcoinj.wallet.Protos.TransactionConfidence.Builder.class); + internal_static_wallet_Transaction_descriptor = + getDescriptor().getMessageTypes().get(4); internal_static_wallet_Transaction_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_wallet_Transaction_descriptor, - new java.lang.String[] { "Version", "Hash", "Pool", "LockTime", "UpdatedAt", "TransactionInput", "TransactionOutput", "BlockHash", }, + new java.lang.String[] { "Version", "Hash", "Pool", "LockTime", "UpdatedAt", "TransactionInput", "TransactionOutput", "BlockHash", "Confidence", }, org.bitcoinj.wallet.Protos.Transaction.class, org.bitcoinj.wallet.Protos.Transaction.Builder.class); internal_static_wallet_Extension_descriptor = - getDescriptor().getMessageTypes().get(4); + getDescriptor().getMessageTypes().get(5); internal_static_wallet_Extension_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_wallet_Extension_descriptor, @@ -5002,7 +5727,7 @@ public final class Protos { org.bitcoinj.wallet.Protos.Extension.class, org.bitcoinj.wallet.Protos.Extension.Builder.class); internal_static_wallet_Wallet_descriptor = - getDescriptor().getMessageTypes().get(5); + getDescriptor().getMessageTypes().get(6); internal_static_wallet_Wallet_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_wallet_Wallet_descriptor, diff --git a/tests/com/google/bitcoin/store/WalletProtobufSerializerTest.java b/tests/com/google/bitcoin/store/WalletProtobufSerializerTest.java index ecc78e59..50fff002 100644 --- a/tests/com/google/bitcoin/store/WalletProtobufSerializerTest.java +++ b/tests/com/google/bitcoin/store/WalletProtobufSerializerTest.java @@ -12,6 +12,7 @@ import com.google.bitcoin.core.NetworkParameters; import com.google.bitcoin.core.Transaction; import com.google.bitcoin.core.Utils; import com.google.bitcoin.core.Wallet; +import com.google.bitcoin.core.TransactionConfidence.ConfidenceType; import org.bitcoinj.wallet.Protos; import org.junit.Before; @@ -73,12 +74,20 @@ public class WalletProtobufSerializerTest { ECKey k2 = new ECKey(); BigInteger v2 = toNanoCoins(0, 50); Transaction t2 = wallet.sendCoinsOffline(k2.toAddress(params), v2); + t2.getConfidence().setConfidenceType(ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND); + t2.getConfidence().setOverridingTransaction(t1); + t1.getConfidence().setConfidenceType(ConfidenceType.BUILDING); + t1.getConfidence().setAppearedAtChainHeight(123); wallet1 = roundTrip(wallet); Transaction t1r = wallet1.getTransaction(t1.getHash()); Transaction t2r = wallet1.getTransaction(t2.getHash()); assertArrayEquals(t2.bitcoinSerialize(), t2r.bitcoinSerialize()); assertArrayEquals(t1.bitcoinSerialize(), t1r.bitcoinSerialize()); assertEquals(t1r.getOutputs().get(0).getSpentBy(), t2r.getInputs().get(0)); + assertEquals(ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND, t2r.getConfidence().getConfidenceType()); + assertEquals(t1r, t2r.getConfidence().getOverridingTransaction()); + assertEquals(ConfidenceType.BUILDING, t1r.getConfidence().getConfidenceType()); + assertEquals(123, t1r.getConfidence().getAppearedAtChainHeight()); assertEquals(1, wallet1.getPendingTransactions().size()); assertEquals(2, wallet1.getTransactions(true, true).size());