3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-12 02:05:53 +00:00

Cache deterministic seed

This commit is contained in:
Devrandom 2014-09-20 16:02:30 -07:00 committed by Mike Hearn
parent cfd795ccbd
commit 03bacf4fa9
10 changed files with 543 additions and 90 deletions

View File

@ -135,7 +135,10 @@ public class MnemonicCode {
String pass = Joiner.on(' ').join(words);
String salt = "mnemonic" + passphrase;
return PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64);
long start = System.currentTimeMillis();
byte[] seed = PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64);
log.info("PBKDF2 took {}ms", System.currentTimeMillis() - start);
return seed;
}
/**

View File

@ -586,6 +586,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
if (seed != null) {
Protos.Key.Builder mnemonicEntry = BasicKeyChain.serializeEncryptableItem(seed);
mnemonicEntry.setType(Protos.Key.Type.DETERMINISTIC_MNEMONIC);
serializeSeedEncryptableItem(seed, mnemonicEntry);
entries.add(mnemonicEntry.build());
}
Map<ECKey, Protos.Key.Builder> keys = basicKeyChain.serializeToEditableProtobufs();
@ -628,6 +629,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
List<DeterministicKeyChain> chains = newLinkedList();
DeterministicSeed seed = null;
DeterministicKeyChain chain = null;
int lookaheadSize = -1;
for (Protos.Key key : keys) {
final Protos.Key.Type t = key.getType();
@ -642,11 +644,25 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
long timestamp = key.getCreationTimestamp() / 1000;
String passphrase = DEFAULT_PASSPHRASE_FOR_MNEMONIC; // FIXME allow non-empty passphrase
if (key.hasSecretBytes()) {
seed = new DeterministicSeed(key.getSecretBytes().toStringUtf8(), passphrase, timestamp);
if (key.hasEncryptedDeterministicSeed())
throw new UnreadableWalletException("Malformed key proto: " + key.toString());
byte[] seedBytes = null;
if (key.hasDeterministicSeed()) {
seedBytes = key.getDeterministicSeed().toByteArray();
}
seed = new DeterministicSeed(key.getSecretBytes().toStringUtf8(), seedBytes, passphrase, timestamp);
} else if (key.hasEncryptedData()) {
if (key.hasDeterministicSeed())
throw new UnreadableWalletException("Malformed key proto: " + key.toString());
EncryptedData data = new EncryptedData(key.getEncryptedData().getInitialisationVector().toByteArray(),
key.getEncryptedData().getEncryptedPrivateKey().toByteArray());
seed = new DeterministicSeed(data, timestamp);
EncryptedData encryptedSeedBytes = null;
if (key.hasEncryptedDeterministicSeed()) {
Protos.EncryptedData encryptedSeed = key.getEncryptedDeterministicSeed();
encryptedSeedBytes = new EncryptedData(encryptedSeed.getInitialisationVector().toByteArray(),
encryptedSeed.getEncryptedPrivateKey().toByteArray());
}
seed = new DeterministicSeed(data, encryptedSeedBytes, timestamp);
} else {
throw new UnreadableWalletException("Malformed key proto: " + key.toString());
}
@ -1059,4 +1075,22 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
}
return keys.build();
}
/*package*/ static void serializeSeedEncryptableItem(DeterministicSeed seed, Protos.Key.Builder proto) {
// The seed can be missing if we have not derived it yet from the mnemonic.
// This will not normally happen once all the wallets are on the latest code that caches
// the seed.
if (seed.isEncrypted() && seed.getEncryptedSeedData() != null) {
EncryptedData data = seed.getEncryptedSeedData();
proto.getEncryptedDeterministicSeedBuilder()
.setEncryptedPrivateKey(ByteString.copyFrom(data.encryptedBytes))
.setInitialisationVector(ByteString.copyFrom(data.initialisationVector));
// We don't allow mixing of encryption types at the moment.
checkState(seed.getEncryptionType() == Protos.Wallet.EncryptionType.ENCRYPTED_SCRYPT_AES);
} else {
final byte[] secret = seed.getSeedBytes();
if (secret != null)
proto.setDeterministicSeed(ByteString.copyFrom(secret));
}
}
}

View File

@ -48,10 +48,11 @@ public class DeterministicSeed implements EncryptableItem {
@Nullable private final byte[] seed;
@Nullable private List<String> mnemonicCode;
@Nullable private EncryptedData encryptedMnemonicCode;
@Nullable private EncryptedData encryptedSeed;
private final long creationTimeSeconds;
public DeterministicSeed(String mnemonicCode, String passphrase, long creationTimeSeconds) throws UnreadableWalletException {
this(decodeMnemonicCode(mnemonicCode), passphrase, creationTimeSeconds);
public DeterministicSeed(String mnemonicCode, byte[] seed, String passphrase, long creationTimeSeconds) throws UnreadableWalletException {
this(decodeMnemonicCode(mnemonicCode), seed, passphrase, creationTimeSeconds);
}
public DeterministicSeed(byte[] seed, List<String> mnemonic, long creationTimeSeconds) {
@ -61,10 +62,11 @@ public class DeterministicSeed implements EncryptableItem {
this.creationTimeSeconds = creationTimeSeconds;
}
public DeterministicSeed(EncryptedData encryptedMnemonic, long creationTimeSeconds) {
public DeterministicSeed(EncryptedData encryptedMnemonic, @Nullable EncryptedData encryptedSeed, long creationTimeSeconds) {
this.seed = null;
this.mnemonicCode = null;
this.encryptedMnemonicCode = checkNotNull(encryptedMnemonic);
this.encryptedSeed = encryptedSeed;
this.creationTimeSeconds = creationTimeSeconds;
}
@ -72,11 +74,12 @@ public class DeterministicSeed implements EncryptableItem {
* Constructs a seed from a BIP 39 mnemonic code. See {@link com.google.bitcoin.crypto.MnemonicCode} for more
* details on this scheme.
* @param mnemonicCode A list of words.
* @param seed The derived seed, or pass null to derive it from mnemonicCode (slow)
* @param passphrase A user supplied passphrase, or an empty string if there is no passphrase
* @param creationTimeSeconds When the seed was originally created, UNIX time.
*/
public DeterministicSeed(List<String> mnemonicCode, String passphrase, long creationTimeSeconds) {
this(MnemonicCode.toSeed(mnemonicCode, passphrase), mnemonicCode, creationTimeSeconds);
public DeterministicSeed(List<String> mnemonicCode, @Nullable byte[] seed, String passphrase, long creationTimeSeconds) {
this((seed != null ? seed : MnemonicCode.toSeed(mnemonicCode, passphrase)), mnemonicCode, creationTimeSeconds);
}
/**
@ -167,6 +170,11 @@ public class DeterministicSeed implements EncryptableItem {
return Protos.Wallet.EncryptionType.ENCRYPTED_SCRYPT_AES;
}
@Nullable
public EncryptedData getEncryptedSeedData() {
return encryptedSeed;
}
@Override
public long getCreationTimeSeconds() {
return creationTimeSeconds;
@ -175,8 +183,9 @@ public class DeterministicSeed implements EncryptableItem {
public DeterministicSeed encrypt(KeyCrypter keyCrypter, KeyParameter aesKey) {
checkState(encryptedMnemonicCode == null, "Trying to encrypt seed twice");
checkState(mnemonicCode != null, "Mnemonic missing so cannot encrypt");
EncryptedData mnemonic = keyCrypter.encrypt(getMnemonicAsBytes(), aesKey);
return new DeterministicSeed(mnemonic, creationTimeSeconds);
EncryptedData encryptedMnemonic = keyCrypter.encrypt(getMnemonicAsBytes(), aesKey);
EncryptedData encryptedSeed = keyCrypter.encrypt(seed, aesKey);
return new DeterministicSeed(encryptedMnemonic, encryptedSeed, creationTimeSeconds);
}
private byte[] getMnemonicAsBytes() {
@ -187,13 +196,17 @@ public class DeterministicSeed implements EncryptableItem {
checkState(isEncrypted());
checkNotNull(encryptedMnemonicCode);
List<String> mnemonic = null;
byte[] seed = null;
try {
mnemonic = decodeMnemonicCode(crypter.decrypt(encryptedMnemonicCode, aesKey));
if (encryptedSeed != null) {
seed = crypter.decrypt(encryptedSeed, aesKey);
}
} catch (UnreadableWalletException e) {
// TODO what is the best way to handle this exception?
throw new RuntimeException(e);
}
return new DeterministicSeed(mnemonic, passphrase, creationTimeSeconds);
return new DeterministicSeed(mnemonic, seed, passphrase, creationTimeSeconds);
}
@Override

View File

@ -2345,6 +2345,54 @@ public final class Protos {
* <code>optional .wallet.DeterministicKey deterministic_key = 7;</code>
*/
org.bitcoinj.wallet.Protos.DeterministicKeyOrBuilder getDeterministicKeyOrBuilder();
// optional bytes deterministic_seed = 8;
/**
* <code>optional bytes deterministic_seed = 8;</code>
*
* <pre>
**
* The seed for a deterministic key hierarchy. Derived from the mnemonic,
* but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
boolean hasDeterministicSeed();
/**
* <code>optional bytes deterministic_seed = 8;</code>
*
* <pre>
**
* The seed for a deterministic key hierarchy. Derived from the mnemonic,
* but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
com.google.protobuf.ByteString getDeterministicSeed();
// optional .wallet.EncryptedData encrypted_deterministic_seed = 9;
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
boolean hasEncryptedDeterministicSeed();
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
org.bitcoinj.wallet.Protos.EncryptedData getEncryptedDeterministicSeed();
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
org.bitcoinj.wallet.Protos.EncryptedDataOrBuilder getEncryptedDeterministicSeedOrBuilder();
}
/**
* Protobuf type {@code wallet.Key}
@ -2465,6 +2513,24 @@ public final class Protos {
bitField0_ |= 0x00000040;
break;
}
case 66: {
bitField0_ |= 0x00000080;
deterministicSeed_ = input.readBytes();
break;
}
case 74: {
org.bitcoinj.wallet.Protos.EncryptedData.Builder subBuilder = null;
if (((bitField0_ & 0x00000100) == 0x00000100)) {
subBuilder = encryptedDeterministicSeed_.toBuilder();
}
encryptedDeterministicSeed_ = input.readMessage(org.bitcoinj.wallet.Protos.EncryptedData.PARSER, extensionRegistry);
if (subBuilder != null) {
subBuilder.mergeFrom(encryptedDeterministicSeed_);
encryptedDeterministicSeed_ = subBuilder.buildPartial();
}
bitField0_ |= 0x00000100;
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@ -2856,6 +2922,68 @@ public final class Protos {
return deterministicKey_;
}
// optional bytes deterministic_seed = 8;
public static final int DETERMINISTIC_SEED_FIELD_NUMBER = 8;
private com.google.protobuf.ByteString deterministicSeed_;
/**
* <code>optional bytes deterministic_seed = 8;</code>
*
* <pre>
**
* The seed for a deterministic key hierarchy. Derived from the mnemonic,
* but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public boolean hasDeterministicSeed() {
return ((bitField0_ & 0x00000080) == 0x00000080);
}
/**
* <code>optional bytes deterministic_seed = 8;</code>
*
* <pre>
**
* The seed for a deterministic key hierarchy. Derived from the mnemonic,
* but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public com.google.protobuf.ByteString getDeterministicSeed() {
return deterministicSeed_;
}
// optional .wallet.EncryptedData encrypted_deterministic_seed = 9;
public static final int ENCRYPTED_DETERMINISTIC_SEED_FIELD_NUMBER = 9;
private org.bitcoinj.wallet.Protos.EncryptedData encryptedDeterministicSeed_;
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public boolean hasEncryptedDeterministicSeed() {
return ((bitField0_ & 0x00000100) == 0x00000100);
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public org.bitcoinj.wallet.Protos.EncryptedData getEncryptedDeterministicSeed() {
return encryptedDeterministicSeed_;
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public org.bitcoinj.wallet.Protos.EncryptedDataOrBuilder getEncryptedDeterministicSeedOrBuilder() {
return encryptedDeterministicSeed_;
}
private void initFields() {
type_ = org.bitcoinj.wallet.Protos.Key.Type.ORIGINAL;
secretBytes_ = com.google.protobuf.ByteString.EMPTY;
@ -2864,6 +2992,8 @@ public final class Protos {
label_ = "";
creationTimestamp_ = 0L;
deterministicKey_ = org.bitcoinj.wallet.Protos.DeterministicKey.getDefaultInstance();
deterministicSeed_ = com.google.protobuf.ByteString.EMPTY;
encryptedDeterministicSeed_ = org.bitcoinj.wallet.Protos.EncryptedData.getDefaultInstance();
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@ -2886,6 +3016,12 @@ public final class Protos {
return false;
}
}
if (hasEncryptedDeterministicSeed()) {
if (!getEncryptedDeterministicSeed().isInitialized()) {
memoizedIsInitialized = 0;
return false;
}
}
memoizedIsInitialized = 1;
return true;
}
@ -2914,6 +3050,12 @@ public final class Protos {
if (((bitField0_ & 0x00000040) == 0x00000040)) {
output.writeMessage(7, deterministicKey_);
}
if (((bitField0_ & 0x00000080) == 0x00000080)) {
output.writeBytes(8, deterministicSeed_);
}
if (((bitField0_ & 0x00000100) == 0x00000100)) {
output.writeMessage(9, encryptedDeterministicSeed_);
}
getUnknownFields().writeTo(output);
}
@ -2951,6 +3093,14 @@ public final class Protos {
size += com.google.protobuf.CodedOutputStream
.computeMessageSize(7, deterministicKey_);
}
if (((bitField0_ & 0x00000080) == 0x00000080)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(8, deterministicSeed_);
}
if (((bitField0_ & 0x00000100) == 0x00000100)) {
size += com.google.protobuf.CodedOutputStream
.computeMessageSize(9, encryptedDeterministicSeed_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@ -3072,6 +3222,7 @@ public final class Protos {
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
getEncryptedDataFieldBuilder();
getDeterministicKeyFieldBuilder();
getEncryptedDeterministicSeedFieldBuilder();
}
}
private static Builder create() {
@ -3102,6 +3253,14 @@ public final class Protos {
deterministicKeyBuilder_.clear();
}
bitField0_ = (bitField0_ & ~0x00000040);
deterministicSeed_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000080);
if (encryptedDeterministicSeedBuilder_ == null) {
encryptedDeterministicSeed_ = org.bitcoinj.wallet.Protos.EncryptedData.getDefaultInstance();
} else {
encryptedDeterministicSeedBuilder_.clear();
}
bitField0_ = (bitField0_ & ~0x00000100);
return this;
}
@ -3166,6 +3325,18 @@ public final class Protos {
} else {
result.deterministicKey_ = deterministicKeyBuilder_.build();
}
if (((from_bitField0_ & 0x00000080) == 0x00000080)) {
to_bitField0_ |= 0x00000080;
}
result.deterministicSeed_ = deterministicSeed_;
if (((from_bitField0_ & 0x00000100) == 0x00000100)) {
to_bitField0_ |= 0x00000100;
}
if (encryptedDeterministicSeedBuilder_ == null) {
result.encryptedDeterministicSeed_ = encryptedDeterministicSeed_;
} else {
result.encryptedDeterministicSeed_ = encryptedDeterministicSeedBuilder_.build();
}
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@ -3205,6 +3376,12 @@ public final class Protos {
if (other.hasDeterministicKey()) {
mergeDeterministicKey(other.getDeterministicKey());
}
if (other.hasDeterministicSeed()) {
setDeterministicSeed(other.getDeterministicSeed());
}
if (other.hasEncryptedDeterministicSeed()) {
mergeEncryptedDeterministicSeed(other.getEncryptedDeterministicSeed());
}
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@ -3226,6 +3403,12 @@ public final class Protos {
return false;
}
}
if (hasEncryptedDeterministicSeed()) {
if (!getEncryptedDeterministicSeed().isInitialized()) {
return false;
}
}
return true;
}
@ -3817,6 +4000,219 @@ public final class Protos {
return deterministicKeyBuilder_;
}
// optional bytes deterministic_seed = 8;
private com.google.protobuf.ByteString deterministicSeed_ = com.google.protobuf.ByteString.EMPTY;
/**
* <code>optional bytes deterministic_seed = 8;</code>
*
* <pre>
**
* The seed for a deterministic key hierarchy. Derived from the mnemonic,
* but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public boolean hasDeterministicSeed() {
return ((bitField0_ & 0x00000080) == 0x00000080);
}
/**
* <code>optional bytes deterministic_seed = 8;</code>
*
* <pre>
**
* The seed for a deterministic key hierarchy. Derived from the mnemonic,
* but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public com.google.protobuf.ByteString getDeterministicSeed() {
return deterministicSeed_;
}
/**
* <code>optional bytes deterministic_seed = 8;</code>
*
* <pre>
**
* The seed for a deterministic key hierarchy. Derived from the mnemonic,
* but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public Builder setDeterministicSeed(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000080;
deterministicSeed_ = value;
onChanged();
return this;
}
/**
* <code>optional bytes deterministic_seed = 8;</code>
*
* <pre>
**
* The seed for a deterministic key hierarchy. Derived from the mnemonic,
* but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public Builder clearDeterministicSeed() {
bitField0_ = (bitField0_ & ~0x00000080);
deterministicSeed_ = getDefaultInstance().getDeterministicSeed();
onChanged();
return this;
}
// optional .wallet.EncryptedData encrypted_deterministic_seed = 9;
private org.bitcoinj.wallet.Protos.EncryptedData encryptedDeterministicSeed_ = org.bitcoinj.wallet.Protos.EncryptedData.getDefaultInstance();
private com.google.protobuf.SingleFieldBuilder<
org.bitcoinj.wallet.Protos.EncryptedData, org.bitcoinj.wallet.Protos.EncryptedData.Builder, org.bitcoinj.wallet.Protos.EncryptedDataOrBuilder> encryptedDeterministicSeedBuilder_;
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public boolean hasEncryptedDeterministicSeed() {
return ((bitField0_ & 0x00000100) == 0x00000100);
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public org.bitcoinj.wallet.Protos.EncryptedData getEncryptedDeterministicSeed() {
if (encryptedDeterministicSeedBuilder_ == null) {
return encryptedDeterministicSeed_;
} else {
return encryptedDeterministicSeedBuilder_.getMessage();
}
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public Builder setEncryptedDeterministicSeed(org.bitcoinj.wallet.Protos.EncryptedData value) {
if (encryptedDeterministicSeedBuilder_ == null) {
if (value == null) {
throw new NullPointerException();
}
encryptedDeterministicSeed_ = value;
onChanged();
} else {
encryptedDeterministicSeedBuilder_.setMessage(value);
}
bitField0_ |= 0x00000100;
return this;
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public Builder setEncryptedDeterministicSeed(
org.bitcoinj.wallet.Protos.EncryptedData.Builder builderForValue) {
if (encryptedDeterministicSeedBuilder_ == null) {
encryptedDeterministicSeed_ = builderForValue.build();
onChanged();
} else {
encryptedDeterministicSeedBuilder_.setMessage(builderForValue.build());
}
bitField0_ |= 0x00000100;
return this;
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public Builder mergeEncryptedDeterministicSeed(org.bitcoinj.wallet.Protos.EncryptedData value) {
if (encryptedDeterministicSeedBuilder_ == null) {
if (((bitField0_ & 0x00000100) == 0x00000100) &&
encryptedDeterministicSeed_ != org.bitcoinj.wallet.Protos.EncryptedData.getDefaultInstance()) {
encryptedDeterministicSeed_ =
org.bitcoinj.wallet.Protos.EncryptedData.newBuilder(encryptedDeterministicSeed_).mergeFrom(value).buildPartial();
} else {
encryptedDeterministicSeed_ = value;
}
onChanged();
} else {
encryptedDeterministicSeedBuilder_.mergeFrom(value);
}
bitField0_ |= 0x00000100;
return this;
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public Builder clearEncryptedDeterministicSeed() {
if (encryptedDeterministicSeedBuilder_ == null) {
encryptedDeterministicSeed_ = org.bitcoinj.wallet.Protos.EncryptedData.getDefaultInstance();
onChanged();
} else {
encryptedDeterministicSeedBuilder_.clear();
}
bitField0_ = (bitField0_ & ~0x00000100);
return this;
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public org.bitcoinj.wallet.Protos.EncryptedData.Builder getEncryptedDeterministicSeedBuilder() {
bitField0_ |= 0x00000100;
onChanged();
return getEncryptedDeterministicSeedFieldBuilder().getBuilder();
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
public org.bitcoinj.wallet.Protos.EncryptedDataOrBuilder getEncryptedDeterministicSeedOrBuilder() {
if (encryptedDeterministicSeedBuilder_ != null) {
return encryptedDeterministicSeedBuilder_.getMessageOrBuilder();
} else {
return encryptedDeterministicSeed_;
}
}
/**
* <code>optional .wallet.EncryptedData encrypted_deterministic_seed = 9;</code>
*
* <pre>
** Encrypted version of the seed
* </pre>
*/
private com.google.protobuf.SingleFieldBuilder<
org.bitcoinj.wallet.Protos.EncryptedData, org.bitcoinj.wallet.Protos.EncryptedData.Builder, org.bitcoinj.wallet.Protos.EncryptedDataOrBuilder>
getEncryptedDeterministicSeedFieldBuilder() {
if (encryptedDeterministicSeedBuilder_ == null) {
encryptedDeterministicSeedBuilder_ = new com.google.protobuf.SingleFieldBuilder<
org.bitcoinj.wallet.Protos.EncryptedData, org.bitcoinj.wallet.Protos.EncryptedData.Builder, org.bitcoinj.wallet.Protos.EncryptedDataOrBuilder>(
encryptedDeterministicSeed_,
getParentForChildren(),
isClean());
encryptedDeterministicSeed_ = null;
}
return encryptedDeterministicSeedBuilder_;
}
// @@protoc_insertion_point(builder_scope:wallet.Key)
}
@ -18610,75 +19006,78 @@ public final class Protos {
"ey\030\002 \002(\014\"y\n\020DeterministicKey\022\022\n\nchain_co" +
"de\030\001 \002(\014\022\014\n\004path\030\002 \003(\r\022\026\n\016issued_subkeys" +
"\030\003 \001(\r\022\026\n\016lookahead_size\030\004 \001(\r\022\023\n\013isFoll" +
"owing\030\005 \001(\010\"\301\002\n\003Key\022\036\n\004type\030\001 \002(\0162\020.wall" +
"owing\030\005 \001(\010\"\232\003\n\003Key\022\036\n\004type\030\001 \002(\0162\020.wall" +
"et.Key.Type\022\024\n\014secret_bytes\030\002 \001(\014\022-\n\016enc" +
"rypted_data\030\006 \001(\0132\025.wallet.EncryptedData",
"\022\022\n\npublic_key\030\003 \001(\014\022\r\n\005label\030\004 \001(\t\022\032\n\022c" +
"reation_timestamp\030\005 \001(\003\0223\n\021deterministic" +
"_key\030\007 \001(\0132\030.wallet.DeterministicKey\"a\n\004" +
"Type\022\014\n\010ORIGINAL\020\001\022\030\n\024ENCRYPTED_SCRYPT_A" +
"ES\020\002\022\032\n\026DETERMINISTIC_MNEMONIC\020\003\022\025\n\021DETE" +
"RMINISTIC_KEY\020\004\"5\n\006Script\022\017\n\007program\030\001 \002" +
"(\014\022\032\n\022creation_timestamp\030\002 \002(\003\"\222\001\n\020Trans" +
"actionInput\022\"\n\032transaction_out_point_has" +
"h\030\001 \002(\014\022#\n\033transaction_out_point_index\030\002" +
" \002(\r\022\024\n\014script_bytes\030\003 \002(\014\022\020\n\010sequence\030\004",
" \001(\r\022\r\n\005value\030\005 \001(\003\"\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\032spen" +
"t_by_transaction_index\030\004 \001(\005\"\211\003\n\025Transac" +
"tionConfidence\0220\n\004type\030\001 \001(\0162\".wallet.Tr" +
"ansactionConfidence.Type\022\032\n\022appeared_at_" +
"height\030\002 \001(\005\022\036\n\026overriding_transaction\030\003" +
" \001(\014\022\r\n\005depth\030\004 \001(\005\022)\n\014broadcast_by\030\006 \003(" +
"\0132\023.wallet.PeerAddress\0224\n\006source\030\007 \001(\0162$" +
".wallet.TransactionConfidence.Source\"O\n\004",
"Type\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDING\020\001\022\013\n\007PENDI" +
"NG\020\002\022\025\n\021NOT_IN_BEST_CHAIN\020\003\022\010\n\004DEAD\020\004\"A\n" +
"\006Source\022\022\n\016SOURCE_UNKNOWN\020\000\022\022\n\016SOURCE_NE" +
"TWORK\020\001\022\017\n\013SOURCE_SELF\020\002\"\264\005\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.Transaction.Pool\022\021\n\tlock_" +
"time\030\004 \001(\r\022\022\n\nupdated_at\030\005 \001(\003\0223\n\021transa" +
"ction_input\030\006 \003(\0132\030.wallet.TransactionIn" +
"put\0225\n\022transaction_output\030\007 \003(\0132\031.wallet" +
".TransactionOutput\022\022\n\nblock_hash\030\010 \003(\014\022 ",
"\n\030block_relativity_offsets\030\013 \003(\005\0221\n\nconf" +
"idence\030\t \001(\0132\035.wallet.TransactionConfide" +
"nce\0225\n\007purpose\030\n \001(\0162\033.wallet.Transactio" +
"n.Purpose:\007UNKNOWN\022+\n\rexchange_rate\030\014 \001(" +
"\0132\024.wallet.ExchangeRate\022\014\n\004memo\030\r \001(\t\"Y\n" +
"\004Pool\022\013\n\007UNSPENT\020\004\022\t\n\005SPENT\020\005\022\014\n\010INACTIV" +
"E\020\002\022\010\n\004DEAD\020\n\022\013\n\007PENDING\020\020\022\024\n\020PENDING_IN" +
"ACTIVE\020\022\"\224\001\n\007Purpose\022\013\n\007UNKNOWN\020\000\022\020\n\014USE" +
"R_PAYMENT\020\001\022\020\n\014KEY_ROTATION\020\002\022\034\n\030ASSURAN" +
"CE_CONTRACT_CLAIM\020\003\022\035\n\031ASSURANCE_CONTRAC",
"T_PLEDGE\020\004\022\033\n\027ASSURANCE_CONTRACT_STUB\020\005\"" +
"N\n\020ScryptParameters\022\014\n\004salt\030\001 \002(\014\022\020\n\001n\030\002" +
" \001(\003:\00516384\022\014\n\001r\030\003 \001(\005:\0018\022\014\n\001p\030\004 \001(\005:\0011\"" +
"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\" \n\003Tag\022\013\n\003tag\030\001 \002(\t\022\014\n" +
"\004data\030\002 \002(\014\"5\n\021TransactionSigner\022\022\n\nclas" +
"s_name\030\001 \002(\t\022\014\n\004data\030\002 \001(\014\"\211\005\n\006Wallet\022\032\n" +
"\022network_identifier\030\001 \002(\t\022\034\n\024last_seen_b" +
"lock_hash\030\002 \001(\014\022\036\n\026last_seen_block_heigh" +
"t\030\014 \001(\r\022!\n\031last_seen_block_time_secs\030\016 \001",
"(\003\022\030\n\003key\030\003 \003(\0132\013.wallet.Key\022(\n\013transact" +
"ion\030\004 \003(\0132\023.wallet.Transaction\022&\n\016watche" +
"d_script\030\017 \003(\0132\016.wallet.Script\022C\n\017encryp" +
"tion_type\030\005 \001(\0162\035.wallet.Wallet.Encrypti" +
"onType:\013UNENCRYPTED\0227\n\025encryption_parame" +
"ters\030\006 \001(\0132\030.wallet.ScryptParameters\022\022\n\007" +
"version\030\007 \001(\005:\0011\022$\n\textension\030\n \003(\0132\021.wa" +
"llet.Extension\022\023\n\013description\030\013 \001(\t\022\031\n\021k" +
"ey_rotation_time\030\r \001(\004\022\031\n\004tags\030\020 \003(\0132\013.w" +
"allet.Tag\0226\n\023transaction_signers\030\021 \003(\0132\031",
".wallet.TransactionSigner\022\036\n\023sigsRequire" +
"dToSpend\030\022 \001(\r:\0011\";\n\016EncryptionType\022\017\n\013U" +
"NENCRYPTED\020\001\022\030\n\024ENCRYPTED_SCRYPT_AES\020\002\"R" +
"\n\014ExchangeRate\022\022\n\ncoin_value\030\001 \002(\003\022\022\n\nfi" +
"at_value\030\002 \002(\003\022\032\n\022fiat_currency_code\030\003 \002" +
"(\tB\035\n\023org.bitcoinj.walletB\006Protos"
"_key\030\007 \001(\0132\030.wallet.DeterministicKey\022\032\n\022" +
"deterministic_seed\030\010 \001(\014\022;\n\034encrypted_de" +
"terministic_seed\030\t \001(\0132\025.wallet.Encrypte" +
"dData\"a\n\004Type\022\014\n\010ORIGINAL\020\001\022\030\n\024ENCRYPTED" +
"_SCRYPT_AES\020\002\022\032\n\026DETERMINISTIC_MNEMONIC\020" +
"\003\022\025\n\021DETERMINISTIC_KEY\020\004\"5\n\006Script\022\017\n\007pr" +
"ogram\030\001 \002(\014\022\032\n\022creation_timestamp\030\002 \002(\003\"" +
"\222\001\n\020TransactionInput\022\"\n\032transaction_out_",
"point_hash\030\001 \002(\014\022#\n\033transaction_out_poin" +
"t_index\030\002 \002(\r\022\024\n\014script_bytes\030\003 \002(\014\022\020\n\010s" +
"equence\030\004 \001(\r\022\r\n\005value\030\005 \001(\003\"\177\n\021Transact" +
"ionOutput\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_transaction_index\030\004 \001(\005\"\211\003" +
"\n\025TransactionConfidence\0220\n\004type\030\001 \001(\0162\"." +
"wallet.TransactionConfidence.Type\022\032\n\022app" +
"eared_at_height\030\002 \001(\005\022\036\n\026overriding_tran" +
"saction\030\003 \001(\014\022\r\n\005depth\030\004 \001(\005\022)\n\014broadcas",
"t_by\030\006 \003(\0132\023.wallet.PeerAddress\0224\n\006sourc" +
"e\030\007 \001(\0162$.wallet.TransactionConfidence.S" +
"ource\"O\n\004Type\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDING\020\001" +
"\022\013\n\007PENDING\020\002\022\025\n\021NOT_IN_BEST_CHAIN\020\003\022\010\n\004" +
"DEAD\020\004\"A\n\006Source\022\022\n\016SOURCE_UNKNOWN\020\000\022\022\n\016" +
"SOURCE_NETWORK\020\001\022\017\n\013SOURCE_SELF\020\002\"\264\005\n\013Tr" +
"ansaction\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.Transaction.Pool" +
"\022\021\n\tlock_time\030\004 \001(\r\022\022\n\nupdated_at\030\005 \001(\003\022" +
"3\n\021transaction_input\030\006 \003(\0132\030.wallet.Tran",
"sactionInput\0225\n\022transaction_output\030\007 \003(\013" +
"2\031.wallet.TransactionOutput\022\022\n\nblock_has" +
"h\030\010 \003(\014\022 \n\030block_relativity_offsets\030\013 \003(" +
"\005\0221\n\nconfidence\030\t \001(\0132\035.wallet.Transacti" +
"onConfidence\0225\n\007purpose\030\n \001(\0162\033.wallet.T" +
"ransaction.Purpose:\007UNKNOWN\022+\n\rexchange_" +
"rate\030\014 \001(\0132\024.wallet.ExchangeRate\022\014\n\004memo" +
"\030\r \001(\t\"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\020P" +
"ENDING_INACTIVE\020\022\"\224\001\n\007Purpose\022\013\n\007UNKNOWN",
"\020\000\022\020\n\014USER_PAYMENT\020\001\022\020\n\014KEY_ROTATION\020\002\022\034" +
"\n\030ASSURANCE_CONTRACT_CLAIM\020\003\022\035\n\031ASSURANC" +
"E_CONTRACT_PLEDGE\020\004\022\033\n\027ASSURANCE_CONTRAC" +
"T_STUB\020\005\"N\n\020ScryptParameters\022\014\n\004salt\030\001 \002" +
"(\014\022\020\n\001n\030\002 \001(\003:\00516384\022\014\n\001r\030\003 \001(\005:\0018\022\014\n\001p\030" +
"\004 \001(\005:\0011\"8\n\tExtension\022\n\n\002id\030\001 \002(\t\022\014\n\004dat" +
"a\030\002 \002(\014\022\021\n\tmandatory\030\003 \002(\010\" \n\003Tag\022\013\n\003tag" +
"\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\"5\n\021TransactionSigne" +
"r\022\022\n\nclass_name\030\001 \002(\t\022\014\n\004data\030\002 \001(\014\"\211\005\n\006" +
"Wallet\022\032\n\022network_identifier\030\001 \002(\t\022\034\n\024la",
"st_seen_block_hash\030\002 \001(\014\022\036\n\026last_seen_bl" +
"ock_height\030\014 \001(\r\022!\n\031last_seen_block_time" +
"_secs\030\016 \001(\003\022\030\n\003key\030\003 \003(\0132\013.wallet.Key\022(\n" +
"\013transaction\030\004 \003(\0132\023.wallet.Transaction\022" +
"&\n\016watched_script\030\017 \003(\0132\016.wallet.Script\022" +
"C\n\017encryption_type\030\005 \001(\0162\035.wallet.Wallet" +
".EncryptionType:\013UNENCRYPTED\0227\n\025encrypti" +
"on_parameters\030\006 \001(\0132\030.wallet.ScryptParam" +
"eters\022\022\n\007version\030\007 \001(\005:\0011\022$\n\textension\030\n" +
" \003(\0132\021.wallet.Extension\022\023\n\013description\030\013",
" \001(\t\022\031\n\021key_rotation_time\030\r \001(\004\022\031\n\004tags\030" +
"\020 \003(\0132\013.wallet.Tag\0226\n\023transaction_signer" +
"s\030\021 \003(\0132\031.wallet.TransactionSigner\022\036\n\023si" +
"gsRequiredToSpend\030\022 \001(\r:\0011\";\n\016Encryption" +
"Type\022\017\n\013UNENCRYPTED\020\001\022\030\n\024ENCRYPTED_SCRYP" +
"T_AES\020\002\"R\n\014ExchangeRate\022\022\n\ncoin_value\030\001 " +
"\002(\003\022\022\n\nfiat_value\030\002 \002(\003\022\032\n\022fiat_currency" +
"_code\030\003 \002(\tB\035\n\023org.bitcoinj.walletB\006Prot" +
"os"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@ -18708,7 +19107,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[] { "Type", "SecretBytes", "EncryptedData", "PublicKey", "Label", "CreationTimestamp", "DeterministicKey", });
new java.lang.String[] { "Type", "SecretBytes", "EncryptedData", "PublicKey", "Label", "CreationTimestamp", "DeterministicKey", "DeterministicSeed", "EncryptedDeterministicSeed", });
internal_static_wallet_Script_descriptor =
getDescriptor().getMessageTypes().get(4);
internal_static_wallet_Script_fieldAccessorTable = new

View File

@ -129,9 +129,9 @@ public class DeterministicKeyChainTest {
DeterministicKey key2 = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
DeterministicKey key3 = chain.getKey(KeyChain.KeyPurpose.CHANGE);
List<Protos.Key> keys = chain.serializeToProtobuf();
// 1 root seed, 1 master key, 1 account key, 2 internal keys, 3 derived, 20 lookahead and 5 lookahead threshold.
// 1 mnemonic/seed, 1 master key, 1 account key, 2 internal keys, 3 derived, 20 lookahead and 5 lookahead threshold.
int numItems =
1 // root seed
1 // mnemonic/seed
+ 1 // master key
+ 1 // account key
+ 2 // ext/int parent keys

View File

@ -1,6 +1,7 @@
type: DETERMINISTIC_MNEMONIC
secret_bytes: "aerobic toe save section draw warm cute upon raccoon mother priority pilot taste sweet next traffic fatal sword dentist original crisp team caution rebel"
creation_timestamp: 1389353062000
deterministic_seed: "E\032\356\206\230,\275\263\364=\334^f\307\037\350\321X7R\262z\205\3564\371tp\2639R\342\027 J\266\253\250\320\022\031\233\271~O$\330\260\214\fz\231tI\353\215*\037\355\205\213.\224?"
type: DETERMINISTIC_KEY
secret_bytes: "\270E0\202(\362b\023\276\264\347\226E2\360\221\347\325\233L\203\3276\272\213\2436&\304\373\221\025"

View File

@ -121,6 +121,13 @@ message Key {
optional int64 creation_timestamp = 5;
optional DeterministicKey deterministic_key = 7;
// The seed for a deterministic key hierarchy. Derived from the mnemonic,
// but cached here for quick startup. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
optional bytes deterministic_seed = 8;
// Encrypted version of the seed
optional EncryptedData encrypted_deterministic_seed = 9;
}
message Script {

View File

@ -1,17 +1,13 @@
package com.google.bitcoin.examples;
import java.io.File;
import com.google.bitcoin.core.BlockChain;
import com.google.bitcoin.core.DownloadListener;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.PeerGroup;
import com.google.bitcoin.core.Wallet;
import com.google.bitcoin.core.*;
import com.google.bitcoin.net.discovery.DnsDiscovery;
import com.google.bitcoin.params.TestNet3Params;
import com.google.bitcoin.store.SPVBlockStore;
import com.google.bitcoin.wallet.DeterministicSeed;
import java.io.File;
/**
* The following example shows you how to restore a HD wallet from a previously generated deterministic seed.
* In this example we manually setup the blockchain, peer group, etc. You can also use the WalletAppKit which provides a restoreWalletFromSeed function to load a wallet from a deterministic seed.
@ -27,9 +23,9 @@ public class RestoreFromSeed {
// Here we restore our wallet from a seed with no passphrase. Also have a look at the BackupToMnemonicSeed.java example that shows how to backup a wallet by creating a mnemonic sentence.
String seedCode = "yard impulse luxury drive today throw farm pepper survey wreck glass federal";
String passphrase = "";
Long creationtime = new Long(1409478661);
Long creationtime = 1409478661L;
DeterministicSeed seed = new DeterministicSeed(seedCode, passphrase, creationtime);
DeterministicSeed seed = new DeterministicSeed(seedCode, null, passphrase, creationtime);
// The wallet class provides a easy fromSeed() function that loads a new wallet from a given seed.
Wallet wallet = Wallet.fromSeed(params, seed);

View File

@ -884,7 +884,7 @@ public class WalletTool {
// Parse as mnemonic code.
final List<String> split = ImmutableList.copyOf(Splitter.on(" ").omitEmptyStrings().split(seedStr));
String passphrase = ""; // TODO allow user to specify a passphrase
seed = new DeterministicSeed(split, passphrase, creationTimeSecs);
seed = new DeterministicSeed(split, null, passphrase, creationTimeSecs);
try {
seed.check();
} catch (MnemonicException.MnemonicLengthException e) {

View File

@ -153,7 +153,7 @@ public class WalletSettingsController {
Main.instance.controller.restoreFromSeedAnimation();
long birthday = datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC);
DeterministicSeed seed = new DeterministicSeed(Splitter.on(' ').splitToList(wordsArea.getText()), "", birthday);
DeterministicSeed seed = new DeterministicSeed(Splitter.on(' ').splitToList(wordsArea.getText()), null, "", birthday);
// Shut down bitcoinj and restart it with the new seed.
Main.bitcoin.addListener(new Service.Listener() {
@Override