3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-14 19:25:51 +00:00

Allow to create wallets with an arbitrary account path. Adds a test for BIP44 wallets (account zero only).

Authors: Nelson MELINA <nelson.melina@mycelium.com>, Giuseppe Magnotta <giuseppe.magnotta@gmail.com>
This commit is contained in:
Nelson MELINA 2017-02-20 12:18:35 +01:00 committed by Andreas Schildbach
parent 61e5d714c3
commit e036f8174a
11 changed files with 866 additions and 86 deletions

View File

@ -81,11 +81,21 @@ public final class HDKeyDerivation {
/**
* @throws HDDerivationException if privKeyBytes is invalid (0 or >= n).
*/
public static DeterministicKey createMasterPrivKeyFromBytes(byte[] privKeyBytes, byte[] chainCode) throws HDDerivationException {
public static DeterministicKey createMasterPrivKeyFromBytes(byte[] privKeyBytes, byte[] chainCode)
throws HDDerivationException {
// childNumberPath is an empty list because we are creating the root key.
return createMasterPrivKeyFromBytes(privKeyBytes, chainCode, ImmutableList.<ChildNumber> of());
}
/**
* @throws HDDerivationException if privKeyBytes is invalid (0 or >= n).
*/
public static DeterministicKey createMasterPrivKeyFromBytes(byte[] privKeyBytes, byte[] chainCode,
ImmutableList<ChildNumber> childNumberPath) throws HDDerivationException {
BigInteger priv = new BigInteger(1, privKeyBytes);
assertNonZero(priv, "Generated master key is invalid.");
assertLessThanN(priv, "Generated master key is invalid.");
return new DeterministicKey(ImmutableList.<ChildNumber>of(), chainCode, priv, null);
return new DeterministicKey(childNumberPath, chainCode, priv, null);
}
public static DeterministicKey createMasterPubKeyFromBytes(byte[] pubKeyBytes, byte[] chainCode) {

View File

@ -18,6 +18,8 @@ package org.bitcoinj.wallet;
import org.bitcoinj.crypto.*;
import com.google.common.collect.ImmutableList;
/**
* Default factory for creating keychains while de-serializing.
*/
@ -32,17 +34,25 @@ public class DefaultKeyChainFactory implements KeyChainFactory {
return chain;
}
@Override
public DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed,
KeyCrypter crypter, boolean isMarried, ImmutableList<ChildNumber> accountPath) {
DeterministicKeyChain chain;
if (isMarried)
chain = new MarriedKeyChain(seed, crypter);
else
chain = new DeterministicKeyChain(seed, crypter, accountPath);
return chain;
}
@Override
public DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey,
boolean isFollowingKey, boolean isMarried) throws UnreadableWalletException {
if (!accountKey.getPath().equals(DeterministicKeyChain.ACCOUNT_ZERO_PATH))
throw new UnreadableWalletException("Expecting account key but found key with path: " +
HDUtils.formatPath(accountKey.getPath()));
DeterministicKeyChain chain;
if (isMarried)
chain = new MarriedKeyChain(accountKey);
else
chain = new DeterministicKeyChain(accountKey, isFollowingKey);
chain = new DeterministicKeyChain(accountKey, isFollowingKey, accountKey.getPath());
return chain;
}
}

View File

@ -105,6 +105,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
private DeterministicHierarchy hierarchy;
@Nullable private DeterministicKey rootKey;
@Nullable private DeterministicSeed seed;
@Nullable private ImmutableList<ChildNumber> accountPath;
// Paths through the key tree. External keys are ones that are communicated to other parties. Internal keys are
// keys created for change addresses, coinbases, mixing, etc - anything that isn't communicated. The distinction
@ -113,13 +114,11 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
// a payment request that can generate lots of addresses independently.
// The account path may be overridden by subclasses.
public static final ImmutableList<ChildNumber> ACCOUNT_ZERO_PATH = ImmutableList.of(ChildNumber.ZERO_HARDENED);
// m / 44' / 0' / 0'
public static final ImmutableList<ChildNumber> BIP44_ACCOUNT_ZERO_PATH = ImmutableList.of(new ChildNumber(44, true),
ChildNumber.ZERO_HARDENED, ChildNumber.ZERO_HARDENED);
public static final ImmutableList<ChildNumber> EXTERNAL_SUBPATH = ImmutableList.of(ChildNumber.ZERO);
public static final ImmutableList<ChildNumber> INTERNAL_SUBPATH = ImmutableList.of(ChildNumber.ONE);
public static final ImmutableList<ChildNumber> EXTERNAL_PATH = HDUtils.concat(ACCOUNT_ZERO_PATH, EXTERNAL_SUBPATH);
public static final ImmutableList<ChildNumber> INTERNAL_PATH = HDUtils.concat(ACCOUNT_ZERO_PATH, INTERNAL_SUBPATH);
// m / 44' / 0' / 0'
public static final ImmutableList<ChildNumber> BIP44_ACCOUNT_ZERO_PATH =
ImmutableList.of(new ChildNumber(44, true), ChildNumber.ZERO_HARDENED, ChildNumber.ZERO_HARDENED);
// We try to ensure we have at least this many keys ready and waiting to be handed out via getKey().
// See docs for getLookaheadSize() for more info on what this is for. The -1 value means it hasn't been calculated
@ -307,17 +306,33 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
* if the starting seed is the same.
*/
protected DeterministicKeyChain(DeterministicSeed seed) {
this(seed, null);
this(seed, null, ACCOUNT_ZERO_PATH);
}
/**
* Creates a deterministic key chain starting from the given seed. This deterministic Key chain
* will follow the account path defined.
*/
public DeterministicKeyChain(DeterministicSeed seed, ImmutableList<ChildNumber> accountPath) {
this(seed, null, accountPath);
}
/**
* Creates a deterministic key chain that watches the given (public only) root key. You can use this to calculate
* balances and generally follow along, but spending is not possible with such a chain. Currently you can't use
* this method to watch an arbitrary fragment of some other tree, this limitation may be removed in future.
* balances and generally follow along, but spending is not possible with such a chain.
*/
public DeterministicKeyChain(DeterministicKey watchingKey) {
this(watchingKey, ACCOUNT_ZERO_PATH);
}
/**
* Creates a deterministic key chain that watches the given (public only) root key. You can use this to calculate
* balances and generally follow along, but spending is not possible with such a chain.
*/
public DeterministicKeyChain(DeterministicKey watchingKey, ImmutableList<ChildNumber> accountPath) {
checkArgument(watchingKey.isPubKeyOnly(), "Private subtrees not currently supported: if you got this key from DKC.getWatchingKey() then use .dropPrivate().dropParent() on it first.");
checkArgument(watchingKey.getPath().size() == getAccountPath().size(), "You can only watch an account key currently");
checkArgument(watchingKey.getPath().size() == accountPath.size(), "You can only watch an account key currently");
setAccountPath(accountPath);
basicKeyChain = new BasicKeyChain();
this.seed = null;
this.rootKey = null;
@ -332,7 +347,17 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
* <p>Watch key has to be an account key.</p>
*/
protected DeterministicKeyChain(DeterministicKey watchKey, boolean isFollowing) {
this(watchKey);
this(watchKey, isFollowing, ACCOUNT_ZERO_PATH);
}
/**
* <p>Creates a deterministic key chain with the given watch key. If <code>isFollowing</code> flag is set then this keychain follows
* some other keychain. In a married wallet following keychain represents "spouse's" keychain.</p>
* <p>Watch key has to be an account key.</p>
*/
protected DeterministicKeyChain(DeterministicKey watchKey, boolean isFollowing,
ImmutableList<ChildNumber> accountPath) {
this(watchKey, accountPath);
this.isFollowing = isFollowing;
}
@ -349,13 +374,30 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
* Creates a key chain that watches the given account key.
*/
public static DeterministicKeyChain watch(DeterministicKey accountKey) {
return new DeterministicKeyChain(accountKey);
return watch(accountKey, ACCOUNT_ZERO_PATH);
}
/**
* Creates a key chain that watches the given account key.
*/
public static DeterministicKeyChain watch(DeterministicKey accountKey, ImmutableList<ChildNumber> accountPath) {
return new DeterministicKeyChain(accountKey, accountPath);
}
/**
* For use in {@link KeyChainFactory} during deserialization.
*/
protected DeterministicKeyChain(DeterministicSeed seed, @Nullable KeyCrypter crypter) {
this(seed, crypter, ACCOUNT_ZERO_PATH);
}
/**
* Creates a deterministic key chain with an encrypted deterministic seed using the provided account path.
* Using {@link org.bitcoinj.crypto.KeyCrypter KeyCrypter} to decrypt.
*/
protected DeterministicKeyChain(DeterministicSeed seed, @Nullable KeyCrypter crypter,
ImmutableList<ChildNumber> accountPath) {
setAccountPath(accountPath);
this.seed = seed;
basicKeyChain = new BasicKeyChain(crypter);
if (!seed.isEncrypted()) {
@ -421,9 +463,16 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
/** Override in subclasses to use a different account derivation path */
protected ImmutableList<ChildNumber> getAccountPath() {
if (accountPath != null)
return accountPath;
return ACCOUNT_ZERO_PATH;
}
/** Store account path of this DeterministicKey */
protected void setAccountPath(ImmutableList<ChildNumber> accountPath) {
this.accountPath = accountPath;
}
private DeterministicKey encryptNonLeaf(KeyParameter aesKey, DeterministicKeyChain chain,
DeterministicKey parent, ImmutableList<ChildNumber> path) {
DeterministicKey key = chain.hierarchy.get(path, false, false);
@ -729,6 +778,9 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
Protos.Key.Builder mnemonicEntry = BasicKeyChain.serializeEncryptableItem(seed);
mnemonicEntry.setType(Protos.Key.Type.DETERMINISTIC_MNEMONIC);
serializeSeedEncryptableItem(seed, mnemonicEntry);
for (ChildNumber childNumber : getAccountPath()) {
mnemonicEntry.addAccountPath(childNumber.i());
}
entries.add(mnemonicEntry.build());
}
Map<ECKey, Protos.Key.Builder> keys = basicKeyChain.serializeToEditableProtobufs();
@ -778,11 +830,18 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
int lookaheadSize = -1;
int sigsRequiredToSpend = 1;
List<ChildNumber> accountPath = newArrayList();
PeekingIterator<Protos.Key> iter = Iterators.peekingIterator(keys.iterator());
while (iter.hasNext()) {
Protos.Key key = iter.next();
final Protos.Key.Type t = key.getType();
if (t == Protos.Key.Type.DETERMINISTIC_MNEMONIC) {
accountPath = newArrayList();
for (int i : key.getAccountPathList()) {
accountPath.add(new ChildNumber(i));
}
if (accountPath.isEmpty())
accountPath = ACCOUNT_ZERO_PATH;
if (chain != null) {
checkState(lookaheadSize >= 0);
chain.setLookaheadSize(lookaheadSize);
@ -855,7 +914,8 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
chain = factory.makeWatchingKeyChain(key, iter.peek(), accountKey, isFollowingKey, isMarried);
isWatchingAccountKey = true;
} else {
chain = factory.makeKeyChain(key, iter.peek(), seed, crypter, isMarried);
chain = factory.makeKeyChain(key, iter.peek(), seed, crypter, isMarried,
ImmutableList.<ChildNumber> builder().addAll(accountPath).build());
chain.lookaheadSize = LAZY_CALCULATE_LOOKAHEAD;
// If the seed is encrypted, then the chain is incomplete at this point. However, we will load
// it up below as we parse in the keys. We just need to check at the end that we've loaded
@ -896,19 +956,25 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
// been rederived and inserted at this point. In the encrypted case though,
// we can't rederive and we must reinsert, potentially building the hierarchy object
// if need be.
if (path.size() == 0) {
if (path.isEmpty()) {
// Master key.
if (chain.rootKey == null) {
chain.rootKey = detkey;
chain.hierarchy = new DeterministicHierarchy(detkey);
}
} else if (path.size() == chain.getAccountPath().size() + 1) {
// Constant 0 is used for external chain and constant 1 for internal chain
// (also known as change addresses). https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
if (detkey.getChildNumber().num() == 0) {
// External chain is used for addresses that are meant to be visible outside of the wallet
// (e.g. for receiving payments).
chain.externalParentKey = detkey;
chain.issuedExternalKeys = key.getDeterministicKey().getIssuedSubkeys();
lookaheadSize = Math.max(lookaheadSize, key.getDeterministicKey().getLookaheadSize());
sigsRequiredToSpend = key.getDeterministicKey().getSigsRequiredToSpend();
} else if (detkey.getChildNumber().num() == 1) {
// Internal chain is used for addresses which are not meant to be visible outside of the
// wallet and is used for return transaction change.
chain.internalParentKey = detkey;
chain.issuedInternalKeys = key.getDeterministicKey().getIssuedSubkeys();
}

View File

@ -16,6 +16,8 @@
package org.bitcoinj.wallet;
import com.google.common.collect.ImmutableList;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.KeyCrypter;
@ -34,6 +36,20 @@ public interface KeyChainFactory {
*/
DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed, KeyCrypter crypter, boolean isMarried);
/**
* Make a keychain (but not a watching one) with the specified account path
*
* @param key the protobuf for the root key
* @param firstSubKey the protobuf for the first child key (normally the parent of the external subchain)
* @param seed the seed
* @param crypter the encrypted/decrypter
* @param isMarried whether the keychain is leading in a marriage
* @param accountPath the specified account path
*/
DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed,
KeyCrypter crypter, boolean isMarried,
ImmutableList<ChildNumber> accountPath);
/**
* Make a watching keychain.
*

View File

@ -84,6 +84,14 @@ public class KeyChainGroup implements KeyBag {
this(params, null, ImmutableList.of(new DeterministicKeyChain(seed)), null, null);
}
/**
* Creates a keychain group with no basic chain, and an HD chain initialized from the given seed. Account path is
* provided.
*/
public KeyChainGroup(NetworkParameters params, DeterministicSeed seed, ImmutableList<ChildNumber> accountPath) {
this(params, null, ImmutableList.of(new DeterministicKeyChain(seed, accountPath)), null, null);
}
/**
* Creates a keychain group with no basic chain, and an HD chain that is watching the given watching key.
* This HAS to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
@ -92,6 +100,14 @@ public class KeyChainGroup implements KeyBag {
this(params, null, ImmutableList.of(DeterministicKeyChain.watch(watchKey)), null, null);
}
/**
* Creates a keychain group with no basic chain, and an HD chain that is watching the given watching key.
* This HAS to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
*/
public KeyChainGroup(NetworkParameters params, DeterministicKey watchKey, ImmutableList<ChildNumber> accountPath) {
this(params, null, ImmutableList.of(DeterministicKeyChain.watch(watchKey, accountPath)), null, null);
}
// Used for deserialization.
private KeyChainGroup(NetworkParameters params, @Nullable BasicKeyChain basicKeyChain, List<DeterministicKeyChain> chains,
@Nullable EnumMap<KeyChain.KeyPurpose, DeterministicKey> currentKeys, @Nullable KeyCrypter crypter) {

View File

@ -2476,6 +2476,31 @@ public final class Protos {
* </pre>
*/
org.bitcoinj.wallet.Protos.EncryptedDataOrBuilder getEncryptedDeterministicSeedOrBuilder();
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
java.util.List<java.lang.Integer> getAccountPathList();
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
int getAccountPathCount();
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
int getAccountPath(int index);
}
/**
* Protobuf type {@code wallet.Key}
@ -2614,6 +2639,27 @@ public final class Protos {
bitField0_ |= 0x00000100;
break;
}
case 80: {
if (!((mutable_bitField0_ & 0x00000200) == 0x00000200)) {
accountPath_ = new java.util.ArrayList<java.lang.Integer>();
mutable_bitField0_ |= 0x00000200;
}
accountPath_.add(input.readUInt32());
break;
}
case 82: {
int length = input.readRawVarint32();
int limit = input.pushLimit(length);
if (!((mutable_bitField0_ & 0x00000200) == 0x00000200) && input.getBytesUntilLimit() > 0) {
accountPath_ = new java.util.ArrayList<java.lang.Integer>();
mutable_bitField0_ |= 0x00000200;
}
while (input.getBytesUntilLimit() > 0) {
accountPath_.add(input.readUInt32());
}
input.popLimit(limit);
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@ -2622,6 +2668,9 @@ public final class Protos {
throw new com.google.protobuf.InvalidProtocolBufferException(
e.getMessage()).setUnfinishedMessage(this);
} finally {
if (((mutable_bitField0_ & 0x00000200) == 0x00000200)) {
accountPath_ = java.util.Collections.unmodifiableList(accountPath_);
}
this.unknownFields = unknownFields.build();
makeExtensionsImmutable();
}
@ -3056,6 +3105,41 @@ public final class Protos {
return encryptedDeterministicSeed_;
}
public static final int ACCOUNT_PATH_FIELD_NUMBER = 10;
private java.util.List<java.lang.Integer> accountPath_;
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public java.util.List<java.lang.Integer>
getAccountPathList() {
return accountPath_;
}
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public int getAccountPathCount() {
return accountPath_.size();
}
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public int getAccountPath(int index) {
return accountPath_.get(index);
}
private int accountPathMemoizedSerializedSize = -1;
private void initFields() {
type_ = org.bitcoinj.wallet.Protos.Key.Type.ORIGINAL;
secretBytes_ = com.google.protobuf.ByteString.EMPTY;
@ -3066,6 +3150,7 @@ public final class Protos {
deterministicKey_ = org.bitcoinj.wallet.Protos.DeterministicKey.getDefaultInstance();
deterministicSeed_ = com.google.protobuf.ByteString.EMPTY;
encryptedDeterministicSeed_ = org.bitcoinj.wallet.Protos.EncryptedData.getDefaultInstance();
accountPath_ = java.util.Collections.emptyList();
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@ -3129,6 +3214,13 @@ public final class Protos {
if (((bitField0_ & 0x00000100) == 0x00000100)) {
output.writeMessage(9, encryptedDeterministicSeed_);
}
if (getAccountPathList().size() > 0) {
output.writeRawVarint32(82);
output.writeRawVarint32(accountPathMemoizedSerializedSize);
}
for (int i = 0; i < accountPath_.size(); i++) {
output.writeUInt32NoTag(accountPath_.get(i));
}
getUnknownFields().writeTo(output);
}
@ -3174,6 +3266,20 @@ public final class Protos {
size += com.google.protobuf.CodedOutputStream
.computeMessageSize(9, encryptedDeterministicSeed_);
}
{
int dataSize = 0;
for (int i = 0; i < accountPath_.size(); i++) {
dataSize += com.google.protobuf.CodedOutputStream
.computeUInt32SizeNoTag(accountPath_.get(i));
}
size += dataSize;
if (!getAccountPathList().isEmpty()) {
size += 1;
size += com.google.protobuf.CodedOutputStream
.computeInt32SizeNoTag(dataSize);
}
accountPathMemoizedSerializedSize = dataSize;
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@ -3333,6 +3439,8 @@ public final class Protos {
encryptedDeterministicSeedBuilder_.clear();
}
bitField0_ = (bitField0_ & ~0x00000100);
accountPath_ = java.util.Collections.emptyList();
bitField0_ = (bitField0_ & ~0x00000200);
return this;
}
@ -3409,6 +3517,11 @@ public final class Protos {
} else {
result.encryptedDeterministicSeed_ = encryptedDeterministicSeedBuilder_.build();
}
if (((bitField0_ & 0x00000200) == 0x00000200)) {
accountPath_ = java.util.Collections.unmodifiableList(accountPath_);
bitField0_ = (bitField0_ & ~0x00000200);
}
result.accountPath_ = accountPath_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@ -3454,6 +3567,16 @@ public final class Protos {
if (other.hasEncryptedDeterministicSeed()) {
mergeEncryptedDeterministicSeed(other.getEncryptedDeterministicSeed());
}
if (!other.accountPath_.isEmpty()) {
if (accountPath_.isEmpty()) {
accountPath_ = other.accountPath_;
bitField0_ = (bitField0_ & ~0x00000200);
} else {
ensureAccountPathIsMutable();
accountPath_.addAll(other.accountPath_);
}
onChanged();
}
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@ -4275,6 +4398,100 @@ public final class Protos {
return encryptedDeterministicSeedBuilder_;
}
private java.util.List<java.lang.Integer> accountPath_ = java.util.Collections.emptyList();
private void ensureAccountPathIsMutable() {
if (!((bitField0_ & 0x00000200) == 0x00000200)) {
accountPath_ = new java.util.ArrayList<java.lang.Integer>(accountPath_);
bitField0_ |= 0x00000200;
}
}
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public java.util.List<java.lang.Integer>
getAccountPathList() {
return java.util.Collections.unmodifiableList(accountPath_);
}
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public int getAccountPathCount() {
return accountPath_.size();
}
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public int getAccountPath(int index) {
return accountPath_.get(index);
}
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public Builder setAccountPath(
int index, int value) {
ensureAccountPathIsMutable();
accountPath_.set(index, value);
onChanged();
return this;
}
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public Builder addAccountPath(int value) {
ensureAccountPathIsMutable();
accountPath_.add(value);
onChanged();
return this;
}
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public Builder addAllAccountPath(
java.lang.Iterable<? extends java.lang.Integer> values) {
ensureAccountPathIsMutable();
com.google.protobuf.AbstractMessageLite.Builder.addAll(
values, accountPath_);
onChanged();
return this;
}
/**
* <code>repeated uint32 account_path = 10 [packed = true];</code>
*
* <pre>
* The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
* </pre>
*/
public Builder clearAccountPath() {
accountPath_ = java.util.Collections.emptyList();
bitField0_ = (bitField0_ & ~0x00000200);
onChanged();
return this;
}
// @@protoc_insertion_point(builder_scope:wallet.Key)
}
@ -19003,78 +19220,79 @@ public final class Protos {
"ode\030\001 \002(\014\022\014\n\004path\030\002 \003(\r\022\026\n\016issued_subkey" +
"s\030\003 \001(\r\022\026\n\016lookahead_size\030\004 \001(\r\022\023\n\013isFol" +
"lowing\030\005 \001(\010\022\036\n\023sigsRequiredToSpend\030\006 \001(" +
"\r:\0011\"\232\003\n\003Key\022\036\n\004type\030\001 \002(\0162\020.wallet.Key." +
"\r:\0011\"\264\003\n\003Key\022\036\n\004type\030\001 \002(\0162\020.wallet.Key." +
"Type\022\024\n\014secret_bytes\030\002 \001(\014\022-\n\016encrypted_",
"data\030\006 \001(\0132\025.wallet.EncryptedData\022\022\n\npub" +
"lic_key\030\003 \001(\014\022\r\n\005label\030\004 \001(\t\022\032\n\022creation" +
"_timestamp\030\005 \001(\003\0223\n\021deterministic_key\030\007 " +
"\001(\0132\030.wallet.DeterministicKey\022\032\n\022determi" +
"nistic_seed\030\010 \001(\014\022;\n\034encrypted_determini" +
"stic_seed\030\t \001(\0132\025.wallet.EncryptedData\"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\021DE" +
"TERMINISTIC_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\020Tra",
"nsactionInput\022\"\n\032transaction_out_point_h" +
"ash\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\021TransactionOutp" +
"ut\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\032sp" +
"ent_by_transaction_index\030\004 \001(\005\"\267\003\n\025Trans" +
"actionConfidence\0220\n\004type\030\001 \001(\0162\".wallet." +
"TransactionConfidence.Type\022\032\n\022appeared_a" +
"t_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\022\033\n\023last_broadca" +
"sted_at\030\010 \001(\003\0224\n\006source\030\007 \001(\0162$.wallet.T" +
"ransactionConfidence.Source\"`\n\004Type\022\013\n\007U" +
"NKNOWN\020\000\022\014\n\010BUILDING\020\001\022\013\n\007PENDING\020\002\022\025\n\021N" +
"OT_IN_BEST_CHAIN\020\003\022\010\n\004DEAD\020\004\022\017\n\013IN_CONFL" +
"ICT\020\005\"A\n\006Source\022\022\n\016SOURCE_UNKNOWN\020\000\022\022\n\016S" +
"OURCE_NETWORK\020\001\022\017\n\013SOURCE_SELF\020\002\"\303\005\n\013Tra" +
"nsaction\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\021transaction_input\030\006 \003(\0132\030.wallet.Trans" +
"actionInput\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\nconfidence\030\t \001(\0132\035.wallet.Transactio" +
"nConfidence\0225\n\007purpose\030\n \001(\0162\033.wallet.Tr" +
"ansaction.Purpose:\007UNKNOWN\022+\n\rexchange_r" +
"ate\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\020PE" +
"NDING_INACTIVE\020\022\"\243\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\031ASSURANCE" +
"_CONTRACT_PLEDGE\020\004\022\033\n\027ASSURANCE_CONTRACT" +
"_STUB\020\005\022\r\n\tRAISE_FEE\020\006\"N\n\020ScryptParamete" +
"rs\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\002i" +
"d\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\021Tr",
"ansactionSigner\022\022\n\nclass_name\030\001 \002(\t\022\014\n\004d" +
"ata\030\002 \001(\014\"\351\004\n\006Wallet\022\032\n\022network_identifi" +
"er\030\001 \002(\t\022\034\n\024last_seen_block_hash\030\002 \001(\014\022\036" +
"\n\026last_seen_block_height\030\014 \001(\r\022!\n\031last_s" +
"een_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.walle" +
"t.Transaction\022&\n\016watched_script\030\017 \003(\0132\016." +
"wallet.Script\022C\n\017encryption_type\030\005 \001(\0162\035" +
".wallet.Wallet.EncryptionType:\013UNENCRYPT" +
"ED\0227\n\025encryption_parameters\030\006 \001(\0132\030.wall",
"et.ScryptParameters\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\023tran" +
"saction_signers\030\021 \003(\0132\031.wallet.Transacti" +
"onSigner\";\n\016EncryptionType\022\017\n\013UNENCRYPTE" +
"D\020\001\022\030\n\024ENCRYPTED_SCRYPT_AES\020\002\"R\n\014Exchang" +
"eRate\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\006Protos"
"stic_seed\030\t \001(\0132\025.wallet.EncryptedData\022\030" +
"\n\014account_path\030\n \003(\rB\002\020\001\"a\n\004Type\022\014\n\010ORIG" +
"INAL\020\001\022\030\n\024ENCRYPTED_SCRYPT_AES\020\002\022\032\n\026DETE" +
"RMINISTIC_MNEMONIC\020\003\022\025\n\021DETERMINISTIC_KE" +
"Y\020\004\"5\n\006Script\022\017\n\007program\030\001 \002(\014\022\032\n\022creati",
"on_timestamp\030\002 \002(\003\"\222\001\n\020TransactionInput\022" +
"\"\n\032transaction_out_point_hash\030\001 \002(\014\022#\n\033t" +
"ransaction_out_point_index\030\002 \002(\r\022\024\n\014scri" +
"pt_bytes\030\003 \002(\014\022\020\n\010sequence\030\004 \001(\r\022\r\n\005valu" +
"e\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_tr" +
"ansaction_hash\030\003 \001(\014\022\"\n\032spent_by_transac" +
"tion_index\030\004 \001(\005\"\267\003\n\025TransactionConfiden" +
"ce\0220\n\004type\030\001 \001(\0162\".wallet.TransactionCon" +
"fidence.Type\022\032\n\022appeared_at_height\030\002 \001(\005",
"\022\036\n\026overriding_transaction\030\003 \001(\014\022\r\n\005dept" +
"h\030\004 \001(\005\022)\n\014broadcast_by\030\006 \003(\0132\023.wallet.P" +
"eerAddress\022\033\n\023last_broadcasted_at\030\010 \001(\003\022" +
"4\n\006source\030\007 \001(\0162$.wallet.TransactionConf" +
"idence.Source\"`\n\004Type\022\013\n\007UNKNOWN\020\000\022\014\n\010BU" +
"ILDING\020\001\022\013\n\007PENDING\020\002\022\025\n\021NOT_IN_BEST_CHA" +
"IN\020\003\022\010\n\004DEAD\020\004\022\017\n\013IN_CONFLICT\020\005\"A\n\006Sourc" +
"e\022\022\n\016SOURCE_UNKNOWN\020\000\022\022\n\016SOURCE_NETWORK\020" +
"\001\022\017\n\013SOURCE_SELF\020\002\"\303\005\n\013Transaction\022\017\n\007ve" +
"rsion\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\021transaction_" +
"input\030\006 \003(\0132\030.wallet.TransactionInput\0225\n" +
"\022transaction_output\030\007 \003(\0132\031.wallet.Trans" +
"actionOutput\022\022\n\nblock_hash\030\010 \003(\014\022 \n\030bloc" +
"k_relativity_offsets\030\013 \003(\005\0221\n\nconfidence" +
"\030\t \001(\0132\035.wallet.TransactionConfidence\0225\n" +
"\007purpose\030\n \001(\0162\033.wallet.Transaction.Purp" +
"ose:\007UNKNOWN\022+\n\rexchange_rate\030\014 \001(\0132\024.wa" +
"llet.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\020PENDING_INACTIVE" +
"\020\022\"\243\001\n\007Purpose\022\013\n\007UNKNOWN\020\000\022\020\n\014USER_PAYM" +
"ENT\020\001\022\020\n\014KEY_ROTATION\020\002\022\034\n\030ASSURANCE_CON" +
"TRACT_CLAIM\020\003\022\035\n\031ASSURANCE_CONTRACT_PLED" +
"GE\020\004\022\033\n\027ASSURANCE_CONTRACT_STUB\020\005\022\r\n\tRAI" +
"SE_FEE\020\006\"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\"\351\004\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\";\n\016En" +
"cryptionType\022\017\n\013UNENCRYPTED\020\001\022\030\n\024ENCRYPT" +
"ED_SCRYPT_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.wall",
"etB\006Protos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
@ -19111,7 +19329,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", "DeterministicSeed", "EncryptedDeterministicSeed", });
new java.lang.String[] { "Type", "SecretBytes", "EncryptedData", "PublicKey", "Label", "CreationTimestamp", "DeterministicKey", "DeterministicSeed", "EncryptedDeterministicSeed", "AccountPath", });
internal_static_wallet_Script_descriptor =
getDescriptor().getMessageTypes().get(4);
internal_static_wallet_Script_fieldAccessorTable = new

View File

@ -266,10 +266,26 @@ public class Wallet extends BaseTaggableObject
this(context, new KeyChainGroup(context.getParams()));
}
/**
* @param params network parameters
* @param seed deterministic seed
* @return a wallet from a deterministic seed with a
* {@link org.bitcoinj.wallet.DeterministicKeyChain#ACCOUNT_ZERO_PATH 0 hardened path}
*/
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed) {
return new Wallet(params, new KeyChainGroup(params, seed));
}
/**
* @param params network parameters
* @param seed deterministic seed
* @param accountPath account path
* @return an instance of a wallet from a deterministic seed.
*/
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed, ImmutableList<ChildNumber> accountPath) {
return new Wallet(params, new KeyChainGroup(params, seed, accountPath));
}
/**
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. A
* watching key corresponds to account zero in the recommended BIP32 key hierarchy.
@ -278,6 +294,13 @@ public class Wallet extends BaseTaggableObject
return new Wallet(params, new KeyChainGroup(params, watchKey));
}
/**
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. The account path is specified.
*/
public static Wallet fromWatchingKey(NetworkParameters params, DeterministicKey watchKey, ImmutableList<ChildNumber> accountPath) {
return new Wallet(params, new KeyChainGroup(params, watchKey, accountPath));
}
/**
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. A
* watching key corresponds to account zero in the recommended BIP32 key hierarchy. The key is specified in base58
@ -290,6 +313,18 @@ public class Wallet extends BaseTaggableObject
return fromWatchingKey(params, watchKey);
}
/**
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. The
* account path is specified. The key is specified in base58 notation and the creation time of the key. If you don't
* know the creation time, you can pass {@link DeterministicHierarchy#BIP32_STANDARDISATION_TIME_SECS}.
*/
public static Wallet fromWatchingKeyB58(NetworkParameters params, String watchKeyB58, long creationTimeSeconds,
ImmutableList<ChildNumber> accountPath) {
final DeterministicKey watchKey = DeterministicKey.deserializeB58(null, watchKeyB58, params);
watchKey.setCreationTimeSeconds(creationTimeSeconds);
return fromWatchingKey(params, watchKey, accountPath);
}
/**
* Creates a wallet containing a given set of keys. All further keys will be derived from the oldest key.
*/

View File

@ -39,6 +39,7 @@ import static org.junit.Assert.*;
public class DeterministicKeyChainTest {
private DeterministicKeyChain chain;
private DeterministicKeyChain bip44chain;
private final byte[] ENTROPY = Sha256Hash.hash("don't use a string seed like this in real life".getBytes());
@Before
@ -50,6 +51,11 @@ public class DeterministicKeyChainTest {
chain = new DeterministicKeyChain(ENTROPY, "", secs);
chain.setLookaheadSize(10);
assertEquals(secs, checkNotNull(chain.getSeed()).getCreationTimeSeconds());
bip44chain = new DeterministicKeyChain(new DeterministicSeed(ENTROPY, "", secs),
ImmutableList.of(new ChildNumber(44, true), new ChildNumber(1, true), ChildNumber.ZERO_HARDENED));
bip44chain.setLookaheadSize(10);
assertEquals(secs, checkNotNull(bip44chain.getSeed()).getCreationTimeSeconds());
}
@Test
@ -131,6 +137,12 @@ public class DeterministicKeyChainTest {
List<Protos.Key> keys = chain1.serializeToProtobuf();
KeyChainFactory factory = new KeyChainFactory() {
@Override
public DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed,
KeyCrypter crypter, boolean isMarried, ImmutableList<ChildNumber> accountPath) {
return new AccountOneChain(crypter, seed);
}
@Override
public DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed, KeyCrypter crypter, boolean isMarried) {
return new AccountOneChain(crypter, seed);
@ -248,6 +260,43 @@ public class DeterministicKeyChainTest {
assertEquals(oldLookaheadSize, chain.getLookaheadSize());
}
@Test
public void serializeUnencryptedBIP44() throws UnreadableWalletException {
bip44chain.maybeLookAhead();
DeterministicKey key1 = bip44chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
DeterministicKey key2 = bip44chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
DeterministicKey key3 = bip44chain.getKey(KeyChain.KeyPurpose.CHANGE);
List<Protos.Key> keys = bip44chain.serializeToProtobuf();
// 1 mnemonic/seed, 1 master key, 1 account key, 2 internal keys, 3 derived, 20 lookahead and 5 lookahead
// threshold.
int numItems = 3 // mnemonic/seed
+ 1 // master key
+ 1 // account key
+ 2 // ext/int parent keys
+ (bip44chain.getLookaheadSize() + bip44chain.getLookaheadThreshold()) * 2 // lookahead zone on each chain
;
assertEquals(numItems, keys.size());
// Get another key that will be lost during round-tripping, to ensure we can derive it again.
DeterministicKey key4 = bip44chain.getKey(KeyChain.KeyPurpose.CHANGE);
final String EXPECTED_SERIALIZATION = checkSerialization(keys, "deterministic-wallet-bip44-serialization.txt");
// Round trip the data back and forth to check it is preserved.
int oldLookaheadSize = bip44chain.getLookaheadSize();
bip44chain = DeterministicKeyChain.fromProtobuf(keys, null).get(0);
assertEquals(EXPECTED_SERIALIZATION, protoToString(bip44chain.serializeToProtobuf()));
assertEquals(key1, bip44chain.findKeyFromPubHash(key1.getPubKeyHash()));
assertEquals(key2, bip44chain.findKeyFromPubHash(key2.getPubKeyHash()));
assertEquals(key3, bip44chain.findKeyFromPubHash(key3.getPubKeyHash()));
assertEquals(key4, bip44chain.getKey(KeyChain.KeyPurpose.CHANGE));
key1.sign(Sha256Hash.ZERO_HASH);
key2.sign(Sha256Hash.ZERO_HASH);
key3.sign(Sha256Hash.ZERO_HASH);
key4.sign(Sha256Hash.ZERO_HASH);
assertEquals(oldLookaheadSize, bip44chain.getLookaheadSize());
}
@Test(expected = IllegalStateException.class)
public void notEncrypted() {
chain.toDecrypted("fail");

View File

@ -0,0 +1,356 @@
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?"
account_path: 2147483692
account_path: 2147483649
account_path: 2147483648
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"
public_key: "\002\342$\253\332\031\352\324q\316M\251}\274\267\370X$\366>Q\316\005\330\376\353f!WHLL\a"
creation_timestamp: 1389353062000
deterministic_key {
chain_code: "XL\240FW\203\316\230\334\374J\003\357=\215\001\206\365\207Z\006m\334X`\236,;_\304\000^"
}
type: DETERMINISTIC_KEY
secret_bytes: "\370\\\267\361L\021K\214\215\272yRP\234(\304\365\303h\251\250\0236\270\344\210\300\330\363(=\332"
public_key: "\002\026n\227\337\232\000\032Q\204\374o-o\277\367;\027.\312Z\024\353\271\300\353j5\375O\270W\266"
deterministic_key {
chain_code: "\367vcS\210\365\365\302\353\321\240\307\327\203\350H\334E\336 \236\037\256\265pT\235\035\221\235\"~"
path: 2147483692
}
type: DETERMINISTIC_KEY
secret_bytes: "\274\244m\336\241`\024\301;Y\024m)\245\375\212\261B\005TnE\217\r \315\323\322\354w\227\225"
public_key: "\003\243q\304\333\310{\001\317\250\266\264\250\353\271\217\r\322l\274M\235\306\347\224\nax\023\334\273\226\321"
deterministic_key {
chain_code: "\344\021F\354\025\3341T\233\'\276&Q\324\325w\362\362n\004\320\210)\033\023\303\244T\371\331\373f"
path: 2147483692
path: 2147483649
}
type: DETERMINISTIC_KEY
secret_bytes: "\023\354\032\244\374<y\254aq\a\362=\345\204\n^;5\213M\267\311\234\037RkX\235\363ae"
public_key: "\002T5VO\274\362m\300\320\352\005\r!\307\024\250\307C\324\323\215[\232@\254S\270\217\362\370\214\362"
deterministic_key {
chain_code: "F\336\2067\377M\026)%\357ZL#\203\320\324\217-\3305\310\244\n\205\277E\323L\250ww\314"
path: 2147483692
path: 2147483649
path: 2147483648
}
type: DETERMINISTIC_KEY
secret_bytes: "\362\305\242\3637\2748Z?]\035s\272\253J\300\033\250\022r\350\020\277U\036K<\335\237\333/\303"
public_key: "\003\2471\326i\331A\337|\373\276\3214\257\363\266Q\315x\341\317\200\243\234\336<s}\261\240,\233\371"
deterministic_key {
chain_code: "Pd\032#\325\213\332\307\3755\020\316\276\002\037\262\241\213\211k\376\254\220R\351\270\"\260V\260\362\257"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
issued_subkeys: 2
lookahead_size: 10
sigsRequiredToSpend: 1
}
type: DETERMINISTIC_KEY
secret_bytes: "\311\327\205Q\005\346\030\365\026\0331\356\346\036\234\024\b\322\202\3726I\351\001 \373\200\003\260\276\216\000"
public_key: "\003\334\214L\003Zq\365\212P\203b~l\367C@T\341\300\216\037\375\002\224\t=\301:\266l\364"
deterministic_key {
chain_code: "\036\277^\322!\227i^Z\212\347\272\365C\016\342\371\236a\022\\\n\037\304\264\021\335\344\340=\234T"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
issued_subkeys: 1
lookahead_size: 10
sigsRequiredToSpend: 1
}
type: DETERMINISTIC_KEY
public_key: "\002\232\236W\2235py\021\331i\350\026H\235\nO\217\231\361M/}5\211v\023Kc\253\2307"
deterministic_key {
chain_code: "\032\366\331\360\276Yo\243;!\023\005\305\246\354\337N\203\302\264\250\355\275\346\271!L\\\252\270\364t"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 0
}
type: DETERMINISTIC_KEY
public_key: "\003\231\254]q\030g3\316u\322h\304Ao4\246f\026\266\374\nY\233\022\034\243\'\000\030\256\342\006"
deterministic_key {
chain_code: "\364\201U66\201$7l%Sy\365]A\265\277&\370\256\364\347\356\334T\323\375\347mB0\374"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 1
}
type: DETERMINISTIC_KEY
public_key: "\002/ \336M\347\357pl@z\204\3240\027\366\0170\307\337\327\312_$\n\'\216\237W\017\263bg"
deterministic_key {
chain_code: "i\206:\003\205BLMI\376\347S~\315\0306\255\274>\220\017\302\241u\017DvD\2662\342\316"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 2
}
type: DETERMINISTIC_KEY
public_key: "\003 1\316\277\355_\343\214}\a\205\233\325\232\241n\256\325\300\2369\020\nh\335\0243\355\362$?7"
deterministic_key {
chain_code: "\334(\\u\022\245\370\t*\372\315\330\365\256Ms\254J_{B\035[f\333\351\272\261\363\373_\023"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 3
}
type: DETERMINISTIC_KEY
public_key: "\003\354\fhI\2731\026\222\v\274\027\357\327\033X\324\270\323\252}\314}\221\213\272\\\362k\352\334#"
deterministic_key {
chain_code: "#\351\"(\t\245\006\351\354f\334\216(\272\252\200\226\337\370\260XO\375\016/\377\306\263yE\222\311"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 4
}
type: DETERMINISTIC_KEY
public_key: "\002-7\rx2zP\r(B\247\350\026\205\210w\251G\b\254\213\000\227\271Q\272\342\357\304>G"
deterministic_key {
chain_code: "US\242J\307\2672<\373l\217\200[\316\352\361*~\324\f\304\267oD\273\300_\340K\247V\370"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 5
}
type: DETERMINISTIC_KEY
public_key: "\002Y\260\332\377;T\263\335\331\004\020kv\207E=\311|\270*hP>)\340\272\203LD\036\313\271"
deterministic_key {
chain_code: "\r34\'I\027\266\272\300\003\366f\274\333\260\006\311\3556!\227\216\301\361\247\354\025\305\321\376\274\214"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 6
}
type: DETERMINISTIC_KEY
public_key: "\002I\367x\235p9\334\234\034\366\247&\321\237\217\241V\252\017`w\212\301\000\305-\312\003\352`\302V"
deterministic_key {
chain_code: "\257\001\375\203E\315\221W\316&\035\244\306\037\351\361\027\020\346\305^Z\274O\212\363P\036\273n\367\326"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 7
}
type: DETERMINISTIC_KEY
public_key: "\0029\373\030u\305\214S\345/\373y%\t\252~\267\f\016t|\354\020\356\306\313\317\027\325\376\232kh"
deterministic_key {
chain_code: "\260\203\277\231\352\265y\020\356r_bO\374l\347\002\032i\216Ct\260\221-\207\200\243\364;\247I"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 8
}
type: DETERMINISTIC_KEY
public_key: "\002[\304\301.\342\253\256\364\025\'\017\356-t\340R\250Z\327\374\250\r\331\221`\334\362a[q\260\271"
deterministic_key {
chain_code: "\376\277#\275\035S\362`\323\246<b~D\253\250p\263\256\364\257\256\201\016\223[\305\261\2758\353\203"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 9
}
type: DETERMINISTIC_KEY
public_key: "\002\253\213j!\316\\\372-tA\331\362T\363\252)\302\2620\246=X#\201\274\212\331Gi9\362\366"
deterministic_key {
chain_code: "S`\237\003Q\021\201\301\341Ij\"\270:\335\374;\320\037\350m\037\331\343\3208\211E\276y\215."
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 10
}
type: DETERMINISTIC_KEY
public_key: "\002\231\323\307\3348\267\357\310\323\nWf$\326\334\v\360\273\357\207\0207\036)\270Api\346\321\270\351"
deterministic_key {
chain_code: "\232Y6\020\317@\r\251]\204m\37796\v5\006\300\t\342\246.\231r\336Vt\370\373\023_M"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 11
}
type: DETERMINISTIC_KEY
public_key: "\003\3647\fs\313\367\022\317\207\276\305\370W\b \276\274\343\344r\351\303\035\235\227X\a\347Q2\325\351"
deterministic_key {
chain_code: "\030F|\234$0\315\215\203\234\037\0054\344M\253\343\214S\360\274_\330\2663\364L\025\250\v\236\247"
path: 2147483692
path: 2147483649
path: 2147483648
path: 0
path: 12
}
type: DETERMINISTIC_KEY
public_key: "\003\273\t\2539\246\023^H\232\202}Hi+\226Q\337\371\002\021\201\276\261*\236\353\'\247\325\367t^"
deterministic_key {
chain_code: "\233\314\253j\213|Y\344\200k\217\357*\\\030\347`\016\266\033g|\273\330\261\226cj\204\351\234Z"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 0
}
type: DETERMINISTIC_KEY
public_key: "\002\266}M\017f0\350\357\232\037\2713\203N\nei\202_\t\034\0343X,\006Y\260\356\340\031W"
deterministic_key {
chain_code: "\2569jU\231\242\230rH\202\025\356C\373\327\374-\305\313\277Jy5mx\034\350\2264\321\327J"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 1
}
type: DETERMINISTIC_KEY
public_key: "\002\221o\254\027\3547s\260&\217b\252r\fo)~\2107\016\255\273\223\245A\234\247ay\204\021H"
deterministic_key {
chain_code: "\265\233\233\354DG\226\326\215\024\230\334\262J\257LL\363R\316$\357\347\017v\371rpA\244PK"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 2
}
type: DETERMINISTIC_KEY
public_key: "\003\301\036A\004O\323\350\240\227>C73n\326P]{\260@\327\242\'\263$H\271\371\371YIJ"
deterministic_key {
chain_code: "9\214\315\275\300\206\253U;\235\002fA\016\215\222\235K\253\311\3648w20\2005\343\310\\:"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 3
}
type: DETERMINISTIC_KEY
public_key: "\002!\373+L\341\025\265\232\a\247J?v\274\273|v\035g\033\211\026\332\233\37378c\226\020\304\360"
deterministic_key {
chain_code: "i\003w\037b!Y]\214l\373]`x\355Je\\\v\205\n\310\254s\301\272\246\315\024}\366\037"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 4
}
type: DETERMINISTIC_KEY
public_key: "\002p\261|\230%\350\a\347?-}\317\274W\210\032\331\006\350\320\016\331\300\024\302\321[O\210E\231\342"
deterministic_key {
chain_code: "\367\263\236B#W\223\3206\3644!?Im\250\277\vY\322\302\254\212A\227\352\244\003\031)\374b"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 5
}
type: DETERMINISTIC_KEY
public_key: "\003\355\213b\333\3157\262iu\361\274\252\271\223\346\276^\350\260q\272m\025.\256\353\006\005\020\255&\017"
deterministic_key {
chain_code: "\003\265\376U\001\240P\'X\364\326\326\275\375s\306\225\373\264\306H5[\356\b\301>\227\325\323\315\344"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 6
}
type: DETERMINISTIC_KEY
public_key: "\003\317\215\311\246\322\335\226,\355\243\274E\270\027\307I\264\344\260\350_\230\372\034\340\363\3113T\222\274c"
deterministic_key {
chain_code: "r\203\252\217hI\312S\323\377](\331C\2711\214T.\031\277\333\267,U|\323x\006\003\263\233"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 7
}
type: DETERMINISTIC_KEY
public_key: "\002\277\306\215\221N\224\b=\v\001\216Ui\033v\250\326\361\221\332\215\343$\344A\306\357f\236\330\241\337"
deterministic_key {
chain_code: "1n@\331(\246#4\262\017\006\360a\206i\270\211n\344\363\343Y\0000\372,\231\352\252f\272!"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 8
}
type: DETERMINISTIC_KEY
public_key: "\002!\032\210\034\267\234\t\311T\'(~c-dKt;\366\030fI5[\026\242\372\310\342\'\205"
deterministic_key {
chain_code: "\004AX{\301\030\035K\353\353S\223m\271\352\323\272\'\202=_5\322\240J.\227\370[gZ?"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 9
}
type: DETERMINISTIC_KEY
public_key: "\002KZ\215?\f\365\"o\364\035\n\240\276_\335\\\256\277\212J\247\201A\325\220\361\356\213/!\301\224"
deterministic_key {
chain_code: "\247\233H)F\252\276\242\370\350\263\270\b:^\247d]\232\316QUA2\n\262\321U\003\r]"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 10
}
type: DETERMINISTIC_KEY
public_key: "\002\204U\310%\024\"\363\267\340\220\031\341koQ\210\037\022\224Y\354\016\370\360\374\346\216\354@B\247\233"
deterministic_key {
chain_code: "w\265\277h\352\025\351\274\233\310;rk\264`*-H-r\026\326\237%\230\034\005\236_6#a"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 11
}
type: DETERMINISTIC_KEY
public_key: "\002\213\241A\022c\322\031\367O\273\375\3354\'Vh\371\362\202\220\253\366\206:\033\347\300\227<6\252\034"
deterministic_key {
chain_code: "R\222\341\341.\352\306O\340+\276\266#K\211\022\264\203\225\240\246\263\023l\327\356 \350\342\242]F"
path: 2147483692
path: 2147483649
path: 2147483648
path: 1
path: 12
}

View File

@ -2,6 +2,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?"
account_path: 2147483648
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

@ -132,6 +132,9 @@ message Key {
// Encrypted version of the seed
optional EncryptedData encrypted_deterministic_seed = 9;
// The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
repeated uint32 account_path = 10 [packed = true];
}
message Script {