Be lenient in parsing tx confidence protobuf, store pubkeys, cleanup

This commit is contained in:
Miron Cuperman 2012-01-26 10:43:15 -08:00
parent 1c28bd3972
commit 69ee4c7729
9 changed files with 350 additions and 208 deletions

View file

@ -26,14 +26,24 @@ package wallet;
option java_package = "org.bitcoinj.wallet";
option java_outer_classname = "Protos";
/**
* A key use to control Bitcoin spending
*
* Either the private key, the public key or both may be present. It is recommended that
* if the private key is provided that the public key is provided too because deriving it is slow.
*
* If only the public key is provided, the key can only be used to watch the blockchain and verify
* transactions, and not for spending.
*/
message Key {
enum Type {
ORIGINAL = 1; // Original bitcoin secp256k1 curve
}
required bytes private_key = 1; // integer representation of the EC private key
required Type type = 2;
optional string label = 3; // for presentation purposes
optional int64 creation_timestamp = 4; // datetime stored as millis since epoch.
required Type type = 1;
optional bytes private_key = 2; // integer representation of the EC private key
optional bytes public_key = 3; // integer representation of the EC public key
optional string label = 4; // for presentation purposes
optional int64 creation_timestamp = 5; // datetime stored as millis since epoch.
}
message TransactionInput {
@ -53,20 +63,30 @@ message TransactionOutput {
// if spent, the index of the transaction output of the transaction doing the spend
}
/**
* A description of the confidence we have that a transaction cannot be reversed in the future.
*
* Parsing should be lenient, since this could change for different applications yet we should
* maintain backward compatibility.
*/
message TransactionConfidence {
enum Type {
UNKNOWN = 0;
BUILDING = 1;
NOT_SEEN_IN_CHAIN = 2;
NOT_IN_BEST_CHAIN = 3;
OVERRIDDEN_BY_DOUBLE_SPEND = 4;
BUILDING = 1; // In best chain. If and only if appeared_at_height is present.
NOT_SEEN_IN_CHAIN = 2; // Pending inclusion in best chain.
NOT_IN_BEST_CHAIN = 3; // In non-best chain, pending inclusion in best chain.
OVERRIDDEN_BY_DOUBLE_SPEND = 4; // If and only if overriding_transaction is present.
}
required Type type = 1;
// This is optional in case we add confidence types to prevent parse errors - backwards compatible.
optional Type type = 1;
optional int32 appeared_at_height = 2;
optional bytes overriding_transaction = 3;
optional bytes overriding_transaction = 3; // Hash of tx. Should be in this wallet.
}
/** A bitcoin transaction */
message Transaction {
/**
* This is a bitfield oriented enum, with the following bits:
@ -107,6 +127,7 @@ message Transaction {
// Sha256Hash of block in block chain in which this transaction appears
}
/** An extension to the wallet */
message Extension {
required string id = 1; // like org.whatever.foo.bar
required bytes data = 2;
@ -116,6 +137,7 @@ message Extension {
required bool mandatory = 3;
}
/** A bitcoin wallet */
message Wallet {
required string network_identifier = 1; // the network used by this wallet
// org.bitcoin.production = production network (Satoshi genesis block)

View file

@ -49,6 +49,6 @@ public class DumpedPrivateKey extends VersionedChecksummedBytes {
* Returns an ECKey created from this encoded private key.
*/
public ECKey getKey() {
return new ECKey(new BigInteger(1, bytes), null);
return new ECKey(new BigInteger(1, bytes));
}
}

View file

@ -85,7 +85,7 @@ public class ECKey implements Serializable {
* reference implementation in its wallet. Note that this is slow because it requires an EC point multiply.
*/
public static ECKey fromASN1(byte[] asn1privkey) {
return new ECKey(extractPrivateKeyFromASN1(asn1privkey), null);
return new ECKey(extractPrivateKeyFromASN1(asn1privkey));
}
/**
@ -122,7 +122,7 @@ public class ECKey implements Serializable {
* the public key already correctly matches the public key. If only the public key is supplied, this ECKey cannot
* be used for signing.
*/
public ECKey(BigInteger privKey, BigInteger pubKey) {
private ECKey(BigInteger privKey, byte[] pubKey) {
this.priv = privKey;
this.pub = null;
if (pubKey == null && privKey != null) {
@ -132,22 +132,27 @@ public class ECKey implements Serializable {
// We expect the pubkey to be in regular encoded form, just as a BigInteger. Therefore the first byte is
// a special marker byte.
// TODO: This is probably not a useful API and may be confusing.
this.pub = Utils.bigIntegerToBytes(pubKey, 65);
this.pub = pubKey;
}
}
/** Creates an ECKey given the private key only. The public key is calculated from it (this is slow) */
public ECKey(BigInteger privKey) {
this(privKey, (byte[])null);
}
/** A constructor variant with BigInteger pubkey. See {@link ECKey#ECKey(BigInteger, byte[])}. */
public ECKey(BigInteger privKey, BigInteger pubKey) {
this(privKey, Utils.bigIntegerToBytes(pubKey, 65));
}
/**
* Creates an ECKey given only the private key bytes. This is the same as using the BigInteger constructor, but
* is more convenient if you are importing a key from elsewhere. The public key will be automatically derived
* from the private key. Same as calling {@link ECKey#fromPrivKeyBytes(byte[])}.
*/
public ECKey(byte[] privKeyBytes, byte[] pubKeyBytes) {
priv = privKeyBytes == null ? null : new BigInteger(1, privKeyBytes);
pub = pubKeyBytes;
if (pub == null && priv != null) {
// Derive public from private.
pub = publicKeyFromPrivate(priv);
}
public ECKey(byte[] privKeyBytes, byte[] pubKey) {
this(privKeyBytes == null ? null : new BigInteger(1, privKeyBytes), pubKey);
}
/**
@ -284,10 +289,6 @@ public class ECKey implements Serializable {
return Utils.bigIntegerToBytes(priv, 32);
}
public static ECKey fromPrivKeyBytes(byte[] bytes) {
return new ECKey(new BigInteger(1, bytes), null);
}
/**
* Exports the private key in the form used by the Satoshi client "dumpprivkey" and "importprivkey" commands. Use
* the {@link com.google.bitcoin.core.DumpedPrivateKey#toString()} method to get the string.

View file

@ -707,7 +707,7 @@ public class Transaction extends ChildMessage implements Serializable {
return confidence;
}
/** Check if the transaction has a known confidence */
public boolean hasConfidence() {
return confidence != null && confidence.getConfidenceType() != TransactionConfidence.ConfidenceType.UNKNOWN;
}

View file

@ -70,6 +70,9 @@ public class Utils {
* @return 32 byte long array.
*/
public static byte[] bigIntegerToBytes(BigInteger b, int numBytes) {
if (b == null) {
return null;
}
byte[] bytes = new byte[numBytes];
byte[] biBytes = b.toByteArray();
int start = (biBytes.length == numBytes + 1) ? 1 : 0;

View file

@ -44,7 +44,7 @@ public class PrivateKeys {
key = dumpedPrivateKey.getKey();
} else {
BigInteger privKey = Base58.decodeToBigInteger(args[0]);
key = new ECKey(privKey, null);
key = new ECKey(privKey);
}
System.out.println("Address from private key is: " + key.toAddress(params).toString());
// And the address ...

View file

@ -31,6 +31,8 @@ import com.google.protobuf.ByteString;
import com.google.protobuf.TextFormat;
import org.bitcoinj.wallet.Protos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
@ -46,6 +48,8 @@ import java.util.Map;
* @author Miron Cuperman
*/
public class WalletProtobufSerializer {
private static final Logger log = LoggerFactory.getLogger(WalletProtobufSerializer.class);
// Used for de-serialization
private Map<ByteString, Transaction> txMap;
@ -82,7 +86,9 @@ public class WalletProtobufSerializer {
// .setCreationTimestamp() TODO
// .setLabel() TODO
.setType(Protos.Key.Type.ORIGINAL)
.setPrivateKey(ByteString.copyFrom(key.getPrivKeyBytes())));
.setPrivateKey(ByteString.copyFrom(key.getPrivKeyBytes()))
.setPublicKey(ByteString.copyFrom(key.getPubKey()))
);
}
return walletBuilder.build();
}
@ -126,9 +132,11 @@ public class WalletProtobufSerializer {
.setValue(output.getValue().longValue());
final TransactionInput spentBy = output.getSpentBy();
if (spentBy != null) {
Sha256Hash spendingHash = spentBy.getParentTransaction().getHash();
outputBuilder
.setSpentByTransactionHash(ByteString.copyFrom(spentBy.getParentTransaction().getHash().getBytes()))
.setSpentByTransactionIndex(spentBy.getParentTransaction().getInputs().indexOf(spentBy));
.setSpentByTransactionHash(hashToByteString(spendingHash))
.setSpentByTransactionIndex(
spentBy.getParentTransaction().getInputs().indexOf(spentBy));
}
txBuilder.addTransactionOutput(outputBuilder);
}
@ -136,7 +144,7 @@ public class WalletProtobufSerializer {
// Handle which blocks tx was seen in
if (tx.getAppearsInHashes() != null) {
for (Sha256Hash hash : tx.getAppearsInHashes()) {
txBuilder.addBlockHash(ByteString.copyFrom(hash.getBytes()));
txBuilder.addBlockHash(hashToByteString(hash));
}
}
@ -145,20 +153,33 @@ public class WalletProtobufSerializer {
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);
writeConfidence(txBuilder, confidence, confidenceBuilder);
}
return txBuilder.build();
}
private static void writeConfidence(
Protos.Transaction.Builder txBuilder,
TransactionConfidence confidence,
Protos.TransactionConfidence.Builder confidenceBuilder) {
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) {
Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash();
confidenceBuilder.setOverridingTransaction(hashToByteString(overridingHash));
}
txBuilder.setConfidence(confidenceBuilder);
}
private static ByteString hashToByteString(Sha256Hash hash) {
return ByteString.copyFrom(hash.getBytes());
}
public static Wallet readWallet(InputStream input, NetworkParameters params)
throws IOException {
WalletProtobufSerializer serializer = new WalletProtobufSerializer();
@ -175,7 +196,12 @@ public class WalletProtobufSerializer {
if (keyProto.getType() != Protos.Key.Type.ORIGINAL) {
throw new IllegalArgumentException("Unknown key type in wallet");
}
wallet.addKey(ECKey.fromPrivKeyBytes(keyProto.getPrivateKey().toByteArray()));
if (!keyProto.hasPrivateKey()) {
throw new IllegalArgumentException("Don't know how to handle pubkey-only keys");
}
byte[] pubKey = keyProto.hasPublicKey() ? keyProto.getPublicKey().toByteArray() : null;
wallet.addKey(new ECKey(keyProto.getPrivateKey().toByteArray(), pubKey));
}
// Read all transactions and create outputs
@ -205,7 +231,9 @@ public class WalletProtobufSerializer {
private void readTransaction(Protos.Transaction txProto, NetworkParameters params) {
Transaction tx = new Transaction(params, txProto.getVersion(), new Sha256Hash(txProto.getHash().toByteArray()));
Transaction tx =
new Transaction(params, txProto.getVersion(),
new Sha256Hash(txProto.getHash().toByteArray()));
if (txProto.hasUpdatedAt())
tx.setUpdateTime(new Date(txProto.getUpdatedAt()));
@ -270,17 +298,43 @@ public class WalletProtobufSerializer {
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()));
}
readConfidence(tx, confidenceProto, confidence);
}
return new WalletTransaction(pool, tx);
}
private void readConfidence(
Transaction tx, Protos.TransactionConfidence confidenceProto,
TransactionConfidence confidence) {
// We are lenient here because tx confidence is not an essential part of the wallet.
// If the tx has an unknown type of confidence, ignore.
if (!confidenceProto.hasType()) {
log.warn("Unknown confidence type for tx {}", tx.getHashAsString());
return;
}
ConfidenceType confidenceType =
TransactionConfidence.ConfidenceType.valueOf(confidenceProto.getType().getNumber());
confidence.setConfidenceType(confidenceType);
if (confidenceProto.hasAppearedAtHeight()) {
if (confidence.getConfidenceType() != ConfidenceType.BUILDING) {
log.warn("Have appearedAtHeight but not BUILDING for tx {}", tx.getHashAsString());
return;
}
confidence.setAppearedAtChainHeight(confidenceProto.getAppearedAtHeight());
}
if (confidenceProto.hasOverridingTransaction()) {
if (confidence.getConfidenceType() != ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND) {
log.warn("Have overridingTransaction but not OVERRIDDEN for tx {}", tx.getHashAsString());
return;
}
Transaction overridingTransaction =
txMap.get(confidenceProto.getOverridingTransaction());
if (overridingTransaction == null) {
log.warn("Have overridingTransaction that is not in wallet for tx {}", tx.getHashAsString());
return;
}
confidence.setOverridingTransaction(overridingTransaction);
}
}
}

View file

@ -11,19 +11,23 @@ public final class Protos {
public interface KeyOrBuilder
extends com.google.protobuf.MessageOrBuilder {
// required bytes private_key = 1;
boolean hasPrivateKey();
com.google.protobuf.ByteString getPrivateKey();
// required .wallet.Key.Type type = 2;
// required .wallet.Key.Type type = 1;
boolean hasType();
org.bitcoinj.wallet.Protos.Key.Type getType();
// optional string label = 3;
// optional bytes private_key = 2;
boolean hasPrivateKey();
com.google.protobuf.ByteString getPrivateKey();
// optional bytes public_key = 3;
boolean hasPublicKey();
com.google.protobuf.ByteString getPublicKey();
// optional string label = 4;
boolean hasLabel();
String getLabel();
// optional int64 creation_timestamp = 4;
// optional int64 creation_timestamp = 5;
boolean hasCreationTimestamp();
long getCreationTimestamp();
}
@ -122,31 +126,41 @@ public final class Protos {
}
private int bitField0_;
// required bytes private_key = 1;
public static final int PRIVATE_KEY_FIELD_NUMBER = 1;
private com.google.protobuf.ByteString privateKey_;
public boolean hasPrivateKey() {
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public com.google.protobuf.ByteString getPrivateKey() {
return privateKey_;
}
// required .wallet.Key.Type type = 2;
public static final int TYPE_FIELD_NUMBER = 2;
// required .wallet.Key.Type type = 1;
public static final int TYPE_FIELD_NUMBER = 1;
private org.bitcoinj.wallet.Protos.Key.Type type_;
public boolean hasType() {
return ((bitField0_ & 0x00000002) == 0x00000002);
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public org.bitcoinj.wallet.Protos.Key.Type getType() {
return type_;
}
// optional string label = 3;
public static final int LABEL_FIELD_NUMBER = 3;
// optional bytes private_key = 2;
public static final int PRIVATE_KEY_FIELD_NUMBER = 2;
private com.google.protobuf.ByteString privateKey_;
public boolean hasPrivateKey() {
return ((bitField0_ & 0x00000002) == 0x00000002);
}
public com.google.protobuf.ByteString getPrivateKey() {
return privateKey_;
}
// optional bytes public_key = 3;
public static final int PUBLIC_KEY_FIELD_NUMBER = 3;
private com.google.protobuf.ByteString publicKey_;
public boolean hasPublicKey() {
return ((bitField0_ & 0x00000004) == 0x00000004);
}
public com.google.protobuf.ByteString getPublicKey() {
return publicKey_;
}
// optional string label = 4;
public static final int LABEL_FIELD_NUMBER = 4;
private java.lang.Object label_;
public boolean hasLabel() {
return ((bitField0_ & 0x00000004) == 0x00000004);
return ((bitField0_ & 0x00000008) == 0x00000008);
}
public String getLabel() {
java.lang.Object ref = label_;
@ -174,19 +188,20 @@ public final class Protos {
}
}
// optional int64 creation_timestamp = 4;
public static final int CREATION_TIMESTAMP_FIELD_NUMBER = 4;
// optional int64 creation_timestamp = 5;
public static final int CREATION_TIMESTAMP_FIELD_NUMBER = 5;
private long creationTimestamp_;
public boolean hasCreationTimestamp() {
return ((bitField0_ & 0x00000008) == 0x00000008);
return ((bitField0_ & 0x00000010) == 0x00000010);
}
public long getCreationTimestamp() {
return creationTimestamp_;
}
private void initFields() {
privateKey_ = com.google.protobuf.ByteString.EMPTY;
type_ = org.bitcoinj.wallet.Protos.Key.Type.ORIGINAL;
privateKey_ = com.google.protobuf.ByteString.EMPTY;
publicKey_ = com.google.protobuf.ByteString.EMPTY;
label_ = "";
creationTimestamp_ = 0L;
}
@ -195,10 +210,6 @@ public final class Protos {
byte isInitialized = memoizedIsInitialized;
if (isInitialized != -1) return isInitialized == 1;
if (!hasPrivateKey()) {
memoizedIsInitialized = 0;
return false;
}
if (!hasType()) {
memoizedIsInitialized = 0;
return false;
@ -211,16 +222,19 @@ public final class Protos {
throws java.io.IOException {
getSerializedSize();
if (((bitField0_ & 0x00000001) == 0x00000001)) {
output.writeBytes(1, privateKey_);
output.writeEnum(1, type_.getNumber());
}
if (((bitField0_ & 0x00000002) == 0x00000002)) {
output.writeEnum(2, type_.getNumber());
output.writeBytes(2, privateKey_);
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
output.writeBytes(3, getLabelBytes());
output.writeBytes(3, publicKey_);
}
if (((bitField0_ & 0x00000008) == 0x00000008)) {
output.writeInt64(4, creationTimestamp_);
output.writeBytes(4, getLabelBytes());
}
if (((bitField0_ & 0x00000010) == 0x00000010)) {
output.writeInt64(5, creationTimestamp_);
}
getUnknownFields().writeTo(output);
}
@ -233,19 +247,23 @@ public final class Protos {
size = 0;
if (((bitField0_ & 0x00000001) == 0x00000001)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(1, privateKey_);
.computeEnumSize(1, type_.getNumber());
}
if (((bitField0_ & 0x00000002) == 0x00000002)) {
size += com.google.protobuf.CodedOutputStream
.computeEnumSize(2, type_.getNumber());
.computeBytesSize(2, privateKey_);
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(3, getLabelBytes());
.computeBytesSize(3, publicKey_);
}
if (((bitField0_ & 0x00000008) == 0x00000008)) {
size += com.google.protobuf.CodedOutputStream
.computeInt64Size(4, creationTimestamp_);
.computeBytesSize(4, getLabelBytes());
}
if (((bitField0_ & 0x00000010) == 0x00000010)) {
size += com.google.protobuf.CodedOutputStream
.computeInt64Size(5, creationTimestamp_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
@ -371,14 +389,16 @@ public final class Protos {
public Builder clear() {
super.clear();
privateKey_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000001);
type_ = org.bitcoinj.wallet.Protos.Key.Type.ORIGINAL;
bitField0_ = (bitField0_ & ~0x00000001);
privateKey_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000002);
label_ = "";
publicKey_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000004);
creationTimestamp_ = 0L;
label_ = "";
bitField0_ = (bitField0_ & ~0x00000008);
creationTimestamp_ = 0L;
bitField0_ = (bitField0_ & ~0x00000010);
return this;
}
@ -420,18 +440,22 @@ public final class Protos {
if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
to_bitField0_ |= 0x00000001;
}
result.privateKey_ = privateKey_;
result.type_ = type_;
if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
to_bitField0_ |= 0x00000002;
}
result.type_ = type_;
result.privateKey_ = privateKey_;
if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
to_bitField0_ |= 0x00000004;
}
result.label_ = label_;
result.publicKey_ = publicKey_;
if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
to_bitField0_ |= 0x00000008;
}
result.label_ = label_;
if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
to_bitField0_ |= 0x00000010;
}
result.creationTimestamp_ = creationTimestamp_;
result.bitField0_ = to_bitField0_;
onBuilt();
@ -449,11 +473,14 @@ public final class Protos {
public Builder mergeFrom(org.bitcoinj.wallet.Protos.Key other) {
if (other == org.bitcoinj.wallet.Protos.Key.getDefaultInstance()) return this;
if (other.hasType()) {
setType(other.getType());
}
if (other.hasPrivateKey()) {
setPrivateKey(other.getPrivateKey());
}
if (other.hasType()) {
setType(other.getType());
if (other.hasPublicKey()) {
setPublicKey(other.getPublicKey());
}
if (other.hasLabel()) {
setLabel(other.getLabel());
@ -466,10 +493,6 @@ public final class Protos {
}
public final boolean isInitialized() {
if (!hasPrivateKey()) {
return false;
}
if (!hasType()) {
return false;
@ -500,29 +523,34 @@ public final class Protos {
}
break;
}
case 10: {
bitField0_ |= 0x00000001;
privateKey_ = input.readBytes();
break;
}
case 16: {
case 8: {
int rawValue = input.readEnum();
org.bitcoinj.wallet.Protos.Key.Type value = org.bitcoinj.wallet.Protos.Key.Type.valueOf(rawValue);
if (value == null) {
unknownFields.mergeVarintField(2, rawValue);
unknownFields.mergeVarintField(1, rawValue);
} else {
bitField0_ |= 0x00000002;
bitField0_ |= 0x00000001;
type_ = value;
}
break;
}
case 18: {
bitField0_ |= 0x00000002;
privateKey_ = input.readBytes();
break;
}
case 26: {
bitField0_ |= 0x00000004;
publicKey_ = input.readBytes();
break;
}
case 34: {
bitField0_ |= 0x00000008;
label_ = input.readBytes();
break;
}
case 32: {
bitField0_ |= 0x00000008;
case 40: {
bitField0_ |= 0x00000010;
creationTimestamp_ = input.readInt64();
break;
}
@ -532,34 +560,10 @@ public final class Protos {
private int bitField0_;
// required bytes private_key = 1;
private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY;
public boolean hasPrivateKey() {
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public com.google.protobuf.ByteString getPrivateKey() {
return privateKey_;
}
public Builder setPrivateKey(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000001;
privateKey_ = value;
onChanged();
return this;
}
public Builder clearPrivateKey() {
bitField0_ = (bitField0_ & ~0x00000001);
privateKey_ = getDefaultInstance().getPrivateKey();
onChanged();
return this;
}
// required .wallet.Key.Type type = 2;
// required .wallet.Key.Type type = 1;
private org.bitcoinj.wallet.Protos.Key.Type type_ = org.bitcoinj.wallet.Protos.Key.Type.ORIGINAL;
public boolean hasType() {
return ((bitField0_ & 0x00000002) == 0x00000002);
return ((bitField0_ & 0x00000001) == 0x00000001);
}
public org.bitcoinj.wallet.Protos.Key.Type getType() {
return type_;
@ -568,22 +572,70 @@ public final class Protos {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000002;
bitField0_ |= 0x00000001;
type_ = value;
onChanged();
return this;
}
public Builder clearType() {
bitField0_ = (bitField0_ & ~0x00000002);
bitField0_ = (bitField0_ & ~0x00000001);
type_ = org.bitcoinj.wallet.Protos.Key.Type.ORIGINAL;
onChanged();
return this;
}
// optional string label = 3;
// optional bytes private_key = 2;
private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY;
public boolean hasPrivateKey() {
return ((bitField0_ & 0x00000002) == 0x00000002);
}
public com.google.protobuf.ByteString getPrivateKey() {
return privateKey_;
}
public Builder setPrivateKey(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000002;
privateKey_ = value;
onChanged();
return this;
}
public Builder clearPrivateKey() {
bitField0_ = (bitField0_ & ~0x00000002);
privateKey_ = getDefaultInstance().getPrivateKey();
onChanged();
return this;
}
// optional bytes public_key = 3;
private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY;
public boolean hasPublicKey() {
return ((bitField0_ & 0x00000004) == 0x00000004);
}
public com.google.protobuf.ByteString getPublicKey() {
return publicKey_;
}
public Builder setPublicKey(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000004;
publicKey_ = value;
onChanged();
return this;
}
public Builder clearPublicKey() {
bitField0_ = (bitField0_ & ~0x00000004);
publicKey_ = getDefaultInstance().getPublicKey();
onChanged();
return this;
}
// optional string label = 4;
private java.lang.Object label_ = "";
public boolean hasLabel() {
return ((bitField0_ & 0x00000004) == 0x00000004);
return ((bitField0_ & 0x00000008) == 0x00000008);
}
public String getLabel() {
java.lang.Object ref = label_;
@ -599,39 +651,39 @@ public final class Protos {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000004;
bitField0_ |= 0x00000008;
label_ = value;
onChanged();
return this;
}
public Builder clearLabel() {
bitField0_ = (bitField0_ & ~0x00000004);
bitField0_ = (bitField0_ & ~0x00000008);
label_ = getDefaultInstance().getLabel();
onChanged();
return this;
}
void setLabel(com.google.protobuf.ByteString value) {
bitField0_ |= 0x00000004;
bitField0_ |= 0x00000008;
label_ = value;
onChanged();
}
// optional int64 creation_timestamp = 4;
// optional int64 creation_timestamp = 5;
private long creationTimestamp_ ;
public boolean hasCreationTimestamp() {
return ((bitField0_ & 0x00000008) == 0x00000008);
return ((bitField0_ & 0x00000010) == 0x00000010);
}
public long getCreationTimestamp() {
return creationTimestamp_;
}
public Builder setCreationTimestamp(long value) {
bitField0_ |= 0x00000008;
bitField0_ |= 0x00000010;
creationTimestamp_ = value;
onChanged();
return this;
}
public Builder clearCreationTimestamp() {
bitField0_ = (bitField0_ & ~0x00000008);
bitField0_ = (bitField0_ & ~0x00000010);
creationTimestamp_ = 0L;
onChanged();
return this;
@ -1721,7 +1773,7 @@ public final class Protos {
public interface TransactionConfidenceOrBuilder
extends com.google.protobuf.MessageOrBuilder {
// required .wallet.TransactionConfidence.Type type = 1;
// optional .wallet.TransactionConfidence.Type type = 1;
boolean hasType();
org.bitcoinj.wallet.Protos.TransactionConfidence.Type getType();
@ -1840,7 +1892,7 @@ public final class Protos {
}
private int bitField0_;
// required .wallet.TransactionConfidence.Type type = 1;
// optional .wallet.TransactionConfidence.Type type = 1;
public static final int TYPE_FIELD_NUMBER = 1;
private org.bitcoinj.wallet.Protos.TransactionConfidence.Type type_;
public boolean hasType() {
@ -1880,10 +1932,6 @@ public final class Protos {
byte isInitialized = memoizedIsInitialized;
if (isInitialized != -1) return isInitialized == 1;
if (!hasType()) {
memoizedIsInitialized = 0;
return false;
}
memoizedIsInitialized = 1;
return true;
}
@ -2131,10 +2179,6 @@ public final class Protos {
}
public final boolean isInitialized() {
if (!hasType()) {
return false;
}
return true;
}
@ -2188,7 +2232,7 @@ public final class Protos {
private int bitField0_;
// required .wallet.TransactionConfidence.Type type = 1;
// optional .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);
@ -2590,12 +2634,6 @@ public final class Protos {
return false;
}
}
if (hasConfidence()) {
if (!getConfidence().isInitialized()) {
memoizedIsInitialized = 0;
return false;
}
}
memoizedIsInitialized = 1;
return true;
}
@ -3051,12 +3089,6 @@ public final class Protos {
return false;
}
}
if (hasConfidence()) {
if (!getConfidence().isInitialized()) {
return false;
}
}
return true;
}
@ -5639,39 +5671,40 @@ public final class Protos {
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\rbitcoin.proto\022\006wallet\"{\n\003Key\022\023\n\013privat" +
"e_key\030\001 \002(\014\022\036\n\004type\030\002 \002(\0162\020.wallet.Key.T" +
"ype\022\r\n\005label\030\003 \001(\t\022\032\n\022creation_timestamp" +
"\030\004 \001(\003\"\024\n\004Type\022\014\n\010ORIGINAL\020\001\"\203\001\n\020Transac" +
"tionInput\022\"\n\032transaction_out_point_hash\030" +
"\001 \002(\014\022#\n\033transaction_out_point_index\030\002 \002" +
"(\005\022\024\n\014script_bytes\030\003 \002(\014\022\020\n\010sequence\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_transac" +
"tion_hash\030\003 \001(\014\022\"\n\032spent_by_transaction_",
"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"
"\n\rbitcoin.proto\022\006wallet\"\217\001\n\003Key\022\036\n\004type\030" +
"\001 \002(\0162\020.wallet.Key.Type\022\023\n\013private_key\030\002" +
" \001(\014\022\022\n\npublic_key\030\003 \001(\014\022\r\n\005label\030\004 \001(\t\022" +
"\032\n\022creation_timestamp\030\005 \001(\003\"\024\n\004Type\022\014\n\010O" +
"RIGINAL\020\001\"\203\001\n\020TransactionInput\022\"\n\032transa" +
"ction_out_point_hash\030\001 \002(\014\022#\n\033transactio" +
"n_out_point_index\030\002 \002(\005\022\024\n\014script_bytes\030" +
"\003 \002(\014\022\020\n\010sequence\030\004 \001(\r\"\177\n\021TransactionOu" +
"tput\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\032",
"spent_by_transaction_index\030\004 \001(\005\"\366\001\n\025Tra" +
"nsactionConfidence\0220\n\004type\030\001 \001(\0162\".walle" +
"t.TransactionConfidence.Type\022\032\n\022appeared" +
"_at_height\030\002 \001(\005\022\036\n\026overriding_transacti" +
"on\030\003 \001(\014\"o\n\004Type\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDIN" +
"G\020\001\022\025\n\021NOT_SEEN_IN_CHAIN\020\002\022\025\n\021NOT_IN_BES" +
"T_CHAIN\020\003\022\036\n\032OVERRIDDEN_BY_DOUBLE_SPEND\020" +
"\004\"\211\003\n\013Transaction\022\017\n\007version\030\001 \002(\005\022\014\n\004ha" +
"sh\030\002 \002(\014\022&\n\004pool\030\003 \002(\0162\030.wallet.Transact" +
"ion.Pool\022\021\n\tlock_time\030\004 \001(\r\022\022\n\nupdated_a",
"t\030\005 \001(\003\0223\n\021transaction_input\030\006 \003(\0132\030.wal" +
"let.TransactionInput\0225\n\022transaction_outp" +
"ut\030\007 \003(\0132\031.wallet.TransactionOutput\022\022\n\nb" +
"lock_hash\030\010 \003(\014\0221\n\nconfidence\030\t \001(\0132\035.wa" +
"llet.TransactionConfidence\"Y\n\004Pool\022\013\n\007UN" +
"SPENT\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" +
"\tExtension\022\n\n\002id\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\022\021\n\t" +
"mandatory\030\003 \002(\010\"\254\001\n\006Wallet\022\032\n\022network_id" +
"entifier\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\013transa" +
"ction\030\004 \003(\0132\023.wallet.Transaction\022$\n\texte" +
"nsion\030\n \003(\0132\021.wallet.ExtensionB\035\n\023org.bi" +
"tcoinj.walletB\006Protos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@ -5683,7 +5716,7 @@ public final class Protos {
internal_static_wallet_Key_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_wallet_Key_descriptor,
new java.lang.String[] { "PrivateKey", "Type", "Label", "CreationTimestamp", },
new java.lang.String[] { "Type", "PrivateKey", "PublicKey", "Label", "CreationTimestamp", },
org.bitcoinj.wallet.Protos.Key.class,
org.bitcoinj.wallet.Protos.Key.Builder.class);
internal_static_wallet_TransactionInput_descriptor =

View file

@ -31,7 +31,7 @@ public class ECKeyTest {
// Test that we can construct an ECKey from a private key (deriving the public from the private), then signing
// a message with it.
BigInteger privkey = new BigInteger(1, Hex.decode("180cb41c7c600be951b5d3d0a7334acc7506173875834f7a6c4c786a28fcbb19"));
ECKey key = new ECKey(privkey, null);
ECKey key = new ECKey(privkey);
byte[] message = new byte[32]; // All zeroes.
byte[] output = key.sign(message);
assertTrue(key.verify(message, output));
@ -70,6 +70,35 @@ public class ECKeyTest {
assertTrue(decodedKey.verify(message, roundtripKey.sign(message)));
}
@Test
public void testKeyPairRoundtrip() {
byte[] privkeyASN1 = Hex.decode(
"3082011302010104205c0b98e524ad188ddef35dc6abba13c34a351a05409e5d285403718b93336a4aa081a53081a2020101302c06072a8648ce3d0101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a144034200042af7a2aafe8dafd7dc7f9cfb58ce09bda7dce28653ab229b98d1d3d759660c672dd0db18c8c2d76aa470448e876fc2089ab1354c01a6e72cefc50915f4a963ee");
ECKey decodedKey = ECKey.fromASN1(privkeyASN1);
// Now re-encode and decode the ASN.1 to see if it is equivalent (it does not produce the exact same byte
// sequence, some integers are padded now).
ECKey roundtripKey =
new ECKey(decodedKey.getPrivKeyBytes(), decodedKey.getPubKey());
for (ECKey key : new ECKey[] {decodedKey, roundtripKey}) {
byte[] message = reverseBytes(Hex.decode(
"11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a"));
byte[] output = key.sign(message);
assertTrue(key.verify(message, output));
output = Hex.decode(
"304502206faa2ebc614bf4a0b31f0ce4ed9012eb193302ec2bcaccc7ae8bb40577f47549022100c73a1a1acc209f3f860bf9b9f5e13e9433db6f8b7bd527a088a0e0cd0a4c83e9");
assertTrue(key.verify(message, output));
}
// Try to sign with one key and verify with the other.
byte[] message = reverseBytes(Hex.decode(
"11da3761e86431e4a54c176789e41f1651b324d240d599a7067bee23d328ec2a"));
assertTrue(roundtripKey.verify(message, decodedKey.sign(message)));
assertTrue(decodedKey.verify(message, roundtripKey.sign(message)));
}
@Test
public void base58Encoding() throws Exception {
String addr = "mqAJmaxMcG5pPHHc3H3NtyXzY7kGbJLuMF";