WalletTool: allow creation of a wallet from a given [word] seed.

This commit is contained in:
Mike Hearn 2014-03-26 23:50:19 +01:00
parent 1ff5d05200
commit a1fcca3883
3 changed files with 53 additions and 18 deletions

View file

@ -208,6 +208,10 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
this(params, new KeyChainGroup());
}
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed) {
return new Wallet(params, new KeyChainGroup(seed));
}
// TODO: When this class moves to the Wallet package, along with the protobuf serializer, then hide this.
/** For internal use only. */
public Wallet(NetworkParameters params, KeyChainGroup keyChainGroup) {

View file

@ -42,6 +42,9 @@ public class MnemonicCode {
public static String BIP39_ENGLISH_SHA256 = "ad90bf3beb7b0eb7e5acd74727dc0da96e0a280a258354e7293fb7e211ac03db";
/** UNIX time for when the BIP39 standard was finalised. This can be used as a default seed birthday. */
public static long BIP39_STANDARDISATION_TIME_SECS = 1381276800;
private static final int PBKDF2_ROUNDS = 2048;
public MnemonicCode() throws IOException {

View file

@ -19,6 +19,8 @@ package com.google.bitcoin.tools;
import com.google.bitcoin.core.*;
import com.google.bitcoin.crypto.KeyCrypterException;
import com.google.bitcoin.crypto.MnemonicCode;
import com.google.bitcoin.crypto.MnemonicException;
import com.google.bitcoin.net.discovery.DnsDiscovery;
import com.google.bitcoin.params.MainNetParams;
import com.google.bitcoin.params.RegTestParams;
@ -30,7 +32,9 @@ import com.google.bitcoin.store.*;
import com.google.bitcoin.uri.BitcoinURI;
import com.google.bitcoin.uri.BitcoinURIParseException;
import com.google.bitcoin.utils.BriefLogFormatter;
import com.google.bitcoin.wallet.DeterministicSeed;
import com.google.common.base.Charsets;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Resources;
import com.google.common.util.concurrent.ListenableFuture;
@ -67,12 +71,13 @@ import java.util.logging.LogManager;
public class WalletTool {
private static final Logger log = LoggerFactory.getLogger(WalletTool.class);
private static OptionSet options;
private static OptionSpec<Date> dateFlag;
private static OptionSpec<Integer> unixtimeFlag;
private static OptionSpec<String> seedFlag;
private static NetworkParameters params;
private static File walletFile;
private static OptionSet options;
private static BlockStore store;
private static AbstractBlockChain chain;
private static PeerGroup peers;
@ -176,23 +181,13 @@ public class WalletTool {
parser.accepts("help");
parser.accepts("force");
parser.accepts("debuglog");
OptionSpec<String> walletFileName = parser.accepts("wallet")
.withRequiredArg()
.defaultsTo("wallet");
OptionSpec<NetworkEnum> netFlag = parser.accepts("net")
.withOptionalArg()
.ofType(NetworkEnum.class)
.defaultsTo(NetworkEnum.PROD);
dateFlag = parser.accepts("date")
.withRequiredArg()
.ofType(Date.class)
OptionSpec<String> walletFileName = parser.accepts("wallet").withRequiredArg().defaultsTo("wallet");
seedFlag = parser.accepts("seed").withRequiredArg();
OptionSpec<NetworkEnum> netFlag = parser.accepts("net").withOptionalArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.PROD);
dateFlag = parser.accepts("date").withRequiredArg().ofType(Date.class)
.withValuesConvertedBy(DateConverter.datePattern("yyyy/MM/dd"));
OptionSpec<WaitForEnum> waitForFlag = parser.accepts("waitfor")
.withRequiredArg()
.ofType(WaitForEnum.class);
OptionSpec<ValidationMode> modeFlag = parser.accepts("mode")
.withRequiredArg()
.ofType(ValidationMode.class)
OptionSpec<WaitForEnum> waitForFlag = parser.accepts("waitfor").withRequiredArg().ofType(WaitForEnum.class);
OptionSpec<ValidationMode> modeFlag = parser.accepts("mode").withRequiredArg().ofType(ValidationMode.class)
.defaultsTo(ValidationMode.SPV);
OptionSpec<String> chainFlag = parser.accepts("chain").withRequiredArg();
// For addkey/delkey.
@ -798,7 +793,40 @@ public class WalletTool {
System.err.println("Wallet creation requested but " + walletFile + " already exists, use --force");
return;
}
wallet = new Wallet(params);
if (options.has(seedFlag)) {
long creationTimeSecs = MnemonicCode.BIP39_STANDARDISATION_TIME_SECS;
if (options.has(dateFlag))
creationTimeSecs = options.valueOf(dateFlag).getTime() / 1000;
String seedStr = options.valueOf(seedFlag);
DeterministicSeed seed;
if (seedStr.contains(" ")) {
// Parse as mnemonic code.
final List<String> split = ImmutableList.copyOf(Splitter.on(" ").omitEmptyStrings().split(seedStr));
try {
seed = new DeterministicSeed(split, creationTimeSecs);
} catch (MnemonicException.MnemonicLengthException e) {
System.err.println("The seed did not have 12 words in, perhaps you need quotes around it?");
return;
} catch (MnemonicException.MnemonicWordException e) {
System.err.println("The seed contained an unrecognised word: " + e.badWord);
return;
} catch (MnemonicException.MnemonicChecksumException e) {
System.err.println("The seed did not pass checksumming, perhaps one of the words is wrong?");
return;
}
} else {
// Parse as hex or base58
byte[] bits = Utils.parseAsHexOrBase58(seedStr);
if (bits.length != 16) {
System.err.println("The given hex/base58 string is not 16 bytes");
return;
}
seed = new DeterministicSeed(bits, creationTimeSecs);
}
wallet = Wallet.fromSeed(params, seed);
} else {
wallet = new Wallet(params);
}
if (password != null)
wallet.encrypt(password);
wallet.saveToFile(walletFile);