Re-organize the source tree so people can depend on bitcoinj without pulling in the examples, tools, or dependencies thereof.

This commit is contained in:
Mike Hearn
2012-03-11 20:01:12 +01:00
parent a86ca77a58
commit 216deb2d35
117 changed files with 947 additions and 367 deletions

View File

@@ -0,0 +1,333 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.bitcoin.tools;
import com.google.bitcoin.core.*;
import com.google.bitcoin.discovery.DnsDiscovery;
import com.google.bitcoin.store.BlockStoreException;
import com.google.bitcoin.store.BoundedOverheadBlockStore;
import com.google.bitcoin.utils.BriefLogFormatter;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import joptsimple.util.DateConverter;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.LogManager;
/**
* A command line tool for manipulating wallets and working with Bitcoin.<p>
*
*/
public class WalletTool {
private static final Logger log = LoggerFactory.getLogger(WalletTool.class);
private static final String HELP_TEXT =
"WalletTool: print and manipulate wallets\n\n" +
"Usage:\n" +
">>> GENERAL OPTIONS\n" +
" --debuglog Enables logging from the core library.\n" +
" --wallet=<file> Specifies what wallet file to load and save.\n" +
" --chain=<file> Specifies the name of the file that stores the block chain.\n" +
" --force Overrides any safety checks on the requested action.\n" +
" --date Provide a date in form YYYY/MM/DD to any action that requires one.\n" +
" --peer=1.2.3.4 Use the given IP address for connections instead of peer discovery.\n" +
"\n>>> ACTIONS\n" +
" --action=DUMP Prints the given wallet in textual form to stdout.\n" +
" --action=CREATE Makes a new wallet in the file specified by --wallet.\n" +
" Will complain and require --force if the wallet already exists.\n" +
" --action=ADD_KEY Adds a new key to the wallet, either specified or freshly generated.\n" +
" If --date is specified, that's the creation date.\n" +
" If --privkey is specified, use as a hex encoded private key.\n" +
" Don't specify --pubkey in that case, it will be derived automatically.\n" +
" If --pubkey is specified, use as a hex encoded non-compressed public key.\n" +
" --action=DELETE_KEY Removes the key specified by --pubkey or --addr from the wallet.\n" +
" --action=SYNC Sync the wallet with the latest block chain (download new transactions).\n" +
" If the chain file does not exist this will RESET the wallet.\n" +
" --action=RESET Deletes all transactions from the wallet, for if you want to replay the chain.\n";
private static OptionSpec<String> walletFileName;
private static OptionSpec<ActionEnum> actionFlag;
private static OptionSpec<NetworkEnum> netFlag;
private static OptionSpec<Date> dateFlag;
private static NetworkParameters params;
private static File walletFile;
private static OptionSet options;
public enum ActionEnum {
DUMP,
CREATE,
ADD_KEY,
DELETE_KEY,
SYNC,
RESET
};
public enum NetworkEnum {
PROD,
TEST
}
public static void main(String[] args) throws Exception {
OptionParser parser = new OptionParser();
parser.accepts("help");
parser.accepts("force");
parser.accepts("debuglog");
walletFileName = parser.accepts("wallet")
.withRequiredArg()
.defaultsTo("wallet");
actionFlag = parser.accepts("action")
.withRequiredArg()
.ofType(ActionEnum.class)
.defaultsTo(ActionEnum.DUMP);
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<String> chainFlag = parser.accepts("chain").withRequiredArg();
// For addkey/delkey.
parser.accepts("pubkey").withRequiredArg();
parser.accepts("privkey").withRequiredArg();
parser.accepts("addr").withRequiredArg();
parser.accepts("peer").withRequiredArg();
options = parser.parse(args);
if (args.length == 0 || options.hasArgument("help") || options.nonOptionArguments().size() > 0) {
System.out.println(HELP_TEXT);
return;
}
if (options.has("debuglog")) {
BriefLogFormatter.init();
log.info("Starting up ...");
} else {
// Disable logspam unless there is a flag.
LogManager.getLogManager().getLogger("").setLevel(Level.SEVERE);
}
File chainFileName;
switch (netFlag.value(options)) {
case PROD:
params = NetworkParameters.prodNet();
chainFileName = new File("prodnet.chain");
break;
case TEST:
params = NetworkParameters.testNet();
chainFileName = new File("testnet.chain");
break;
default:
throw new RuntimeException("Unreachable.");
}
// Allow the user to override the name of the chain used.
if (options.has(chainFlag)) {
chainFileName = new File(chainFlag.value(options));
}
ActionEnum action = actionFlag.value(options);
walletFile = new File(walletFileName.value(options));
if (action == ActionEnum.CREATE) {
createWallet(options, params, walletFile);
return; // We're done.
}
if (!walletFile.exists()) {
System.err.println("Specified wallet file " + walletFile + " does not exist. Try --action=CREATE");
return;
}
Wallet wallet;
try {
wallet = Wallet.loadFromFile(walletFile);
} catch (Exception e) {
System.err.println("Failed to load wallet '" + walletFile + "': " + e.getMessage());
e.printStackTrace();
return;
}
// What should we do?
switch (action) {
case DUMP: dumpWallet(wallet); break;
case ADD_KEY: addKey(wallet); break;
case DELETE_KEY: deleteKey(wallet); break;
case SYNC: syncChain(wallet, chainFileName); break;
case RESET: reset(wallet); break;
}
saveWallet(walletFile, wallet);
}
private static void reset(Wallet wallet) {
// Delete the transactions and save. In future, reset the chain head pointer.
wallet.clearTransactions(0);
saveWallet(walletFile, wallet);
}
private static void syncChain(final Wallet wallet, File chainFileName) {
try {
// Will create a fresh chain if one doesn't exist or there is an issue with this one.
System.out.println("Connecting ..." );
final BoundedOverheadBlockStore store = new BoundedOverheadBlockStore(params, chainFileName);
final BlockChain chain = new BlockChain(params, wallet, store);
wallet.addEventListener(new AbstractWalletEventListener() {
@Override
public void onChange() {
saveWallet(walletFile, wallet);
}
});
int startTransactions = wallet.getTransactions(true, true).size();
PeerGroup peers = connect(wallet, chain);
DownloadListener listener = new DownloadListener();
peers.startBlockChainDownload(listener);
try {
listener.await();
} catch (InterruptedException e) {
System.err.println("Chain download interrupted, quitting ...");
System.exit(1);
}
peers.stop();
int endTransactions = wallet.getTransactions(true, true).size();
if (endTransactions > startTransactions) {
System.out.println("Synced " + (endTransactions - startTransactions) + " transactions.");
}
} catch (BlockStoreException e) {
System.err.println("Error reading block chain file " + chainFileName + ": " + e.getMessage());
e.printStackTrace();
}
}
private static PeerGroup connect(Wallet wallet, BlockChain chain) {
PeerGroup peers = new PeerGroup(params, chain);
peers.setUserAgent("WalletTool", "1.0");
peers.addWallet(wallet);
peers.setFastCatchupTimeSecs(wallet.getEarliestKeyCreationTime());
if (options.has("peer")) {
String peer = (String) options.valueOf("peer");
try {
peers.addAddress(new PeerAddress(InetAddress.getByName(peer), params.port));
} catch (UnknownHostException e) {
System.err.println("Could not understand peer domain name/IP address: " + peer + ": " + e.getMessage());
System.exit(1);
}
} else {
peers.addPeerDiscovery(new DnsDiscovery(params));
}
peers.start();
return peers;
}
private static void createWallet(OptionSet options, NetworkParameters params, File walletFile) throws IOException {
if (walletFile.exists() && !options.has("force")) {
System.err.println("Wallet creation requested but " + walletFile + " already exists, use --force");
return;
}
new Wallet(params).saveToFile(walletFile);
// Don't add any keys by default.
return;
}
private static void saveWallet(File walletFile, Wallet wallet) {
// Save the new state of the wallet to a temp file then rename, in case anything goes wrong.
File tmp;
try {
// Create tmp in same directory as wallet to ensure we create on the same drive/volume.
tmp = File.createTempFile("wallet", null, walletFile.getParentFile());
tmp.deleteOnExit();
wallet.saveToFile(tmp);
tmp.renameTo(walletFile);
} catch (IOException e) {
System.err.println("Failed to save wallet! Old wallet should be left untouched.");
e.printStackTrace();
System.exit(1);
}
}
private static void addKey(Wallet wallet) {
ECKey key;
long creationTimeSeconds = 0;
if (options.has(dateFlag)) {
creationTimeSeconds = dateFlag.value(options).getTime() / 1000;
}
if (options.has("privkey")) {
String data = (String) options.valueOf("privkey");
key = new ECKey(new BigInteger(1, Hex.decode(data)));
if (options.has("pubkey")) {
// Give the user a hint.
System.out.println("You don't have to specify --pubkey when a private key is supplied.");
}
key.setCreationTimeSeconds(creationTimeSeconds);
} else if (options.has("pubkey")) {
byte[] pubkey = Hex.decode((String)options.valueOf("pubkey"));
key = new ECKey(null, pubkey);
key.setCreationTimeSeconds(creationTimeSeconds);
} else {
// Freshly generated key.
key = new ECKey();
}
if (wallet.findKeyFromPubKey(key.getPubKey()) != null) {
System.err.println("That key already exists in this wallet.");
return;
}
wallet.addKey(key);
System.out.println("addr:" + key.toAddress(params) + " " + key);
}
private static void deleteKey(Wallet wallet) {
String pubkey = (String) options.valueOf("pubkey");
String addr = (String) options.valueOf("addr");
if (pubkey == null && addr == null) {
System.err.println("One of --pubkey or --addr must be specified.");
return;
}
ECKey key = null;
if (pubkey != null) {
key = wallet.findKeyFromPubKey(Hex.decode(pubkey));
} else if (addr != null) {
try {
Address address = new Address(wallet.getParams(), addr);
key = wallet.findKeyFromPubHash(address.getHash160());
} catch (AddressFormatException e) {
System.err.println(addr + " does not parse as a Bitcoin address of the right network parameters.");
return;
}
}
if (key == null) {
System.err.println("Wallet does not seem to contain that key.");
return;
}
wallet.keychain.remove(key);
}
private static void dumpWallet(Wallet wallet) {
System.out.println(wallet.toString());
}
}

206
tools/tools.iml Normal file
View File

@@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="lib" />
<orderEntry type="library" name="Maven: net.sf.jopt-simple:jopt-simple:4.3" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15:1.46" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.6.2" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-jdk14:1.6.2" level="project" />
</component>
<component name="org.twodividedbyzero.idea.findbugs">
<option name="_basePreferences">
<map>
<entry key="property.analysisEffortLevel" value="default" />
<entry key="property.analyzeAfterCompile" value="false" />
<entry key="property.exportAsHtml" value="true" />
<entry key="property.exportAsXml" value="true" />
<entry key="property.exportBaseDir" value="" />
<entry key="property.exportCreateArchiveDir" value="false" />
<entry key="property.exportOpenBrowser" value="true" />
<entry key="property.minPriorityToReport" value="Medium" />
<entry key="property.runAnalysisInBackground" value="false" />
<entry key="property.showHiddenDetectors" value="false" />
<entry key="property.toolWindowToFront" value="true" />
</map>
</option>
<option name="_detectors">
<map>
<entry key="AppendingToAnObjectOutputStream" value="true" />
<entry key="BCPMethodReturnCheck" value="false" />
<entry key="BadAppletConstructor" value="false" />
<entry key="BadResultSetAccess" value="true" />
<entry key="BadSyntaxForRegularExpression" value="true" />
<entry key="BadUseOfReturnValue" value="true" />
<entry key="BadlyOverriddenAdapter" value="true" />
<entry key="BooleanReturnNull" value="true" />
<entry key="BuildInterproceduralCallGraph" value="false" />
<entry key="BuildObligationPolicyDatabase" value="true" />
<entry key="CallToUnsupportedMethod" value="false" />
<entry key="CalledMethods" value="true" />
<entry key="CheckCalls" value="false" />
<entry key="CheckExpectedWarnings" value="false" />
<entry key="CheckImmutableAnnotation" value="true" />
<entry key="CheckTypeQualifiers" value="true" />
<entry key="CloneIdiom" value="true" />
<entry key="ComparatorIdiom" value="true" />
<entry key="ConfusedInheritance" value="true" />
<entry key="ConfusionBetweenInheritedAndOuterMethod" value="true" />
<entry key="CrossSiteScripting" value="true" />
<entry key="DoInsideDoPrivileged" value="true" />
<entry key="DontCatchIllegalMonitorStateException" value="true" />
<entry key="DontIgnoreResultOfPutIfAbsent" value="true" />
<entry key="DontUseEnum" value="true" />
<entry key="DroppedException" value="true" />
<entry key="DumbMethodInvocations" value="true" />
<entry key="DumbMethods" value="true" />
<entry key="DuplicateBranches" value="true" />
<entry key="EmptyZipFileEntry" value="true" />
<entry key="EqStringTest" value="false" />
<entry key="EqualsOperandShouldHaveClassCompatibleWithThis" value="true" />
<entry key="FieldItemSummary" value="true" />
<entry key="FinalizerNullsFields" value="true" />
<entry key="FindBadCast" value="false" />
<entry key="FindBadCast2" value="true" />
<entry key="FindBadEqualsImplementation" value="false" />
<entry key="FindBadForLoop" value="true" />
<entry key="FindBugsSummaryStats" value="true" />
<entry key="FindCircularDependencies" value="false" />
<entry key="FindDeadLocalStores" value="true" />
<entry key="FindDoubleCheck" value="true" />
<entry key="FindEmptySynchronizedBlock" value="true" />
<entry key="FindFieldSelfAssignment" value="true" />
<entry key="FindFinalizeInvocations" value="true" />
<entry key="FindFloatEquality" value="true" />
<entry key="FindFloatMath" value="false" />
<entry key="FindHEmismatch" value="true" />
<entry key="FindInconsistentSync2" value="true" />
<entry key="FindJSR166LockMonitorenter" value="true" />
<entry key="FindLocalSelfAssignment2" value="true" />
<entry key="FindMaskedFields" value="true" />
<entry key="FindMismatchedWaitOrNotify" value="true" />
<entry key="FindNakedNotify" value="true" />
<entry key="FindNonSerializableStoreIntoSession" value="true" />
<entry key="FindNonSerializableValuePassedToWriteObject" value="true" />
<entry key="FindNonShortCircuit" value="true" />
<entry key="FindNullDeref" value="true" />
<entry key="FindNullDerefsInvolvingNonShortCircuitEvaluation" value="true" />
<entry key="FindOpenStream" value="true" />
<entry key="FindPuzzlers" value="true" />
<entry key="FindRefComparison" value="true" />
<entry key="FindReturnRef" value="true" />
<entry key="FindRunInvocations" value="true" />
<entry key="FindSelfComparison" value="true" />
<entry key="FindSelfComparison2" value="true" />
<entry key="FindSleepWithLockHeld" value="true" />
<entry key="FindSpinLoop" value="true" />
<entry key="FindSqlInjection" value="true" />
<entry key="FindTwoLockWait" value="true" />
<entry key="FindUncalledPrivateMethods" value="true" />
<entry key="FindUnconditionalWait" value="true" />
<entry key="FindUninitializedGet" value="true" />
<entry key="FindUnrelatedTypesInGenericContainer" value="true" />
<entry key="FindUnreleasedLock" value="true" />
<entry key="FindUnsatisfiedObligation" value="true" />
<entry key="FindUnsyncGet" value="true" />
<entry key="FindUselessControlFlow" value="true" />
<entry key="FormatStringChecker" value="true" />
<entry key="HugeSharedStringConstants" value="true" />
<entry key="IDivResultCastToDouble" value="true" />
<entry key="IncompatMask" value="true" />
<entry key="InconsistentAnnotations" value="true" />
<entry key="InefficientMemberAccess" value="false" />
<entry key="InefficientToArray" value="true" />
<entry key="InfiniteLoop" value="true" />
<entry key="InfiniteRecursiveLoop" value="true" />
<entry key="InfiniteRecursiveLoop2" value="false" />
<entry key="InheritanceUnsafeGetResource" value="true" />
<entry key="InitializationChain" value="true" />
<entry key="InstantiateStaticClass" value="true" />
<entry key="InvalidJUnitTest" value="true" />
<entry key="IteratorIdioms" value="true" />
<entry key="LazyInit" value="true" />
<entry key="LoadOfKnownNullValue" value="true" />
<entry key="LockedFields" value="false" />
<entry key="LostLoggerDueToWeakReference" value="true" />
<entry key="MethodReturnCheck" value="true" />
<entry key="Methods" value="true" />
<entry key="MultithreadedInstanceAccess" value="true" />
<entry key="MutableLock" value="true" />
<entry key="MutableStaticFields" value="true" />
<entry key="Naming" value="true" />
<entry key="Noise" value="false" />
<entry key="NoiseNullDeref" value="false" />
<entry key="NoteAnnotationRetention" value="true" />
<entry key="NoteCheckReturnValue" value="true" />
<entry key="NoteCheckReturnValueAnnotations" value="true" />
<entry key="NoteDirectlyRelevantTypeQualifiers" value="true" />
<entry key="NoteJCIPAnnotation" value="true" />
<entry key="NoteNonNullAnnotations" value="true" />
<entry key="NoteNonnullReturnValues" value="true" />
<entry key="NoteSuppressedWarnings" value="true" />
<entry key="NoteUnconditionalParamDerefs" value="true" />
<entry key="NumberConstructor" value="true" />
<entry key="OverridingEqualsNotSymmetrical" value="true" />
<entry key="PreferZeroLengthArrays" value="true" />
<entry key="PublicSemaphores" value="false" />
<entry key="QuestionableBooleanAssignment" value="true" />
<entry key="ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass" value="true" />
<entry key="ReadReturnShouldBeChecked" value="true" />
<entry key="RedundantInterfaces" value="true" />
<entry key="ReflectiveClasses" value="true" />
<entry key="RepeatedConditionals" value="true" />
<entry key="ResolveAllReferences" value="false" />
<entry key="RuntimeExceptionCapture" value="true" />
<entry key="SerializableIdiom" value="true" />
<entry key="StartInConstructor" value="true" />
<entry key="StaticCalendarDetector" value="true" />
<entry key="StringConcatenation" value="true" />
<entry key="SuperfluousInstanceOf" value="true" />
<entry key="SuspiciousThreadInterrupted" value="true" />
<entry key="SwitchFallthrough" value="true" />
<entry key="SynchronizationOnSharedBuiltinConstant" value="true" />
<entry key="SynchronizeAndNullCheckField" value="true" />
<entry key="SynchronizeOnClassLiteralNotGetClass" value="true" />
<entry key="SynchronizingOnContentsOfFieldToProtectField" value="true" />
<entry key="TestASM" value="false" />
<entry key="TestDataflowAnalysis" value="false" />
<entry key="TestingGround" value="false" />
<entry key="TrainFieldStoreTypes" value="true" />
<entry key="TrainNonNullAnnotations" value="true" />
<entry key="TrainUnconditionalDerefParams" value="true" />
<entry key="URLProblems" value="true" />
<entry key="UncallableMethodOfAnonymousClass" value="true" />
<entry key="UnnecessaryMath" value="true" />
<entry key="UnreadFields" value="true" />
<entry key="UseObjectEquals" value="false" />
<entry key="UselessSubclassMethod" value="false" />
<entry key="VarArgsProblems" value="true" />
<entry key="VolatileUsage" value="true" />
<entry key="WaitInLoop" value="true" />
<entry key="WrongMapIterator" value="true" />
<entry key="XMLFactoryBypass" value="true" />
</map>
</option>
<option name="_reportCategories">
<map>
<entry key="BAD_PRACTICE" value="true" />
<entry key="CORRECTNESS" value="true" />
<entry key="EXPERIMENTAL" value="true" />
<entry key="I18N" value="true" />
<entry key="MALICIOUS_CODE" value="true" />
<entry key="MT_CORRECTNESS" value="true" />
<entry key="NOISE" value="false" />
<entry key="PERFORMANCE" value="true" />
<entry key="SECURITY" value="true" />
<entry key="STYLE" value="true" />
</map>
</option>
</component>
</module>