Skip to content

Commit

Permalink
WalletTool: allow creation of a wallet from a given [word] seed.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikehearn committed May 29, 2014
1 parent 1ff5d05 commit a1fcca3
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 18 deletions.
4 changes: 4 additions & 0 deletions core/src/main/java/com/google/bitcoin/core/Wallet.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ public Wallet(NetworkParameters params) {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
64 changes: 46 additions & 18 deletions tools/src/main/java/com/google/bitcoin/tools/WalletTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

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;
Expand All @@ -30,7 +32,9 @@
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;
Expand Down Expand Up @@ -67,12 +71,13 @@
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;
Expand Down Expand Up @@ -176,23 +181,13 @@ public static void main(String[] args) throws Exception {
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.
Expand Down Expand Up @@ -798,7 +793,40 @@ private static void createWallet(OptionSet options, NetworkParameters params, Fi
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);
Expand Down

0 comments on commit a1fcca3

Please sign in to comment.