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

Fix #1078. Add ability to load Wallet without loading its transactions. Use new methods with wallet-tool reset (where transactions are deleted anyway) and with wallet-tool sync (if the --force option is provided).

This commit is contained in:
Matt Bogosian 2015-09-04 08:18:51 -07:00 committed by Andreas Schildbach
parent 7c8c2a4b02
commit 66a851bd30
3 changed files with 90 additions and 39 deletions

View File

@ -68,7 +68,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
* You can extend the wallet format with additional fields specific to your application if you want, but make sure
* to either put the extra data in the provided extension areas, or select tag numbers that are unlikely to be used
* by anyone else.<p>
*
*
* @author Miron Cuperman
* @author Andreas Schildbach
*/
@ -235,7 +235,7 @@ public class WalletProtobufSerializer {
private static Protos.Transaction makeTxProto(WalletTransaction wtx) {
Transaction tx = wtx.getTransaction();
Protos.Transaction.Builder txBuilder = Protos.Transaction.newBuilder();
txBuilder.setPool(getProtoPool(wtx))
.setHash(hashToByteString(tx.getHash()))
.setVersion((int) tx.getVersion());
@ -243,11 +243,11 @@ public class WalletProtobufSerializer {
if (tx.getUpdateTime() != null) {
txBuilder.setUpdatedAt(tx.getUpdateTime().getTime());
}
if (tx.getLockTime() > 0) {
txBuilder.setLockTime((int)tx.getLockTime());
}
// Handle inputs.
for (TransactionInput input : tx.getInputs()) {
Protos.TransactionInput.Builder inputBuilder = Protos.TransactionInput.newBuilder()
@ -260,7 +260,7 @@ public class WalletProtobufSerializer {
inputBuilder.setValue(input.getValue().value);
txBuilder.addTransactionInput(inputBuilder);
}
// Handle outputs.
for (TransactionOutput output : tx.getOutputs()) {
Protos.TransactionOutput.Builder outputBuilder = Protos.TransactionOutput.newBuilder()
@ -275,7 +275,7 @@ public class WalletProtobufSerializer {
}
txBuilder.addTransactionOutput(outputBuilder);
}
// Handle which blocks tx was seen in.
final Map<Sha256Hash, Integer> appearsInHashes = tx.getAppearsInHashes();
if (appearsInHashes != null) {
@ -284,7 +284,7 @@ public class WalletProtobufSerializer {
txBuilder.addBlockRelativityOffsets(entry.getValue());
}
}
if (tx.hasConfidence()) {
TransactionConfidence confidence = tx.getConfidence();
Protos.TransactionConfidence.Builder confidenceBuilder = Protos.TransactionConfidence.newBuilder();
@ -315,7 +315,7 @@ public class WalletProtobufSerializer {
if (tx.getMemo() != null)
txBuilder.setMemo(tx.getMemo());
return txBuilder.build();
}
@ -389,13 +389,33 @@ public class WalletProtobufSerializer {
* @throws UnreadableWalletException thrown in various error conditions (see description).
*/
public Wallet readWallet(InputStream input, @Nullable WalletExtension... walletExtensions) throws UnreadableWalletException {
return readWallet(input, false, walletExtensions);
}
/**
* <p>Loads wallet data from the given protocol buffer and inserts it into the given Wallet object. This is primarily
* useful when you wish to pre-register extension objects. Note that if loading fails the provided Wallet object
* may be in an indeterminate state and should be thrown away. Do not simply call this method again on the same
* Wallet object with {@code forceReset} set {@code true}. It won't work.</p>
*
* <p>If {@code forceReset} is {@code true}, then no transactions are loaded from the wallet, and it is configured
* to replay transactions from the blockchain (as if the wallet had been loaded and {@link Wallet.reset}
* had been called immediately thereafter).
*
* <p>A wallet can be unreadable for various reasons, such as inability to open the file, corrupt data, internally
* inconsistent data, a wallet extension marked as mandatory that cannot be handled and so on. You should always
* handle {@link UnreadableWalletException} and communicate failure to the user in an appropriate manner.</p>
*
* @throws UnreadableWalletException thrown in various error conditions (see description).
*/
public Wallet readWallet(InputStream input, boolean forceReset, @Nullable WalletExtension[] extensions) throws UnreadableWalletException {
try {
Protos.Wallet walletProto = parseToProto(input);
final String paramsID = walletProto.getNetworkIdentifier();
NetworkParameters params = NetworkParameters.fromID(paramsID);
if (params == null)
throw new UnreadableWalletException("Unknown network parameters ID " + paramsID);
return readWallet(params, walletExtensions, walletProto);
return readWallet(params, extensions, walletProto, forceReset);
} catch (IOException e) {
throw new UnreadableWalletException("Could not parse input stream to protobuf", e);
} catch (IllegalStateException e) {
@ -416,6 +436,27 @@ public class WalletProtobufSerializer {
*/
public Wallet readWallet(NetworkParameters params, @Nullable WalletExtension[] extensions,
Protos.Wallet walletProto) throws UnreadableWalletException {
return readWallet(params, extensions, walletProto, false);
}
/**
* <p>Loads wallet data from the given protocol buffer and inserts it into the given Wallet object. This is primarily
* useful when you wish to pre-register extension objects. Note that if loading fails the provided Wallet object
* may be in an indeterminate state and should be thrown away. Do not simply call this method again on the same
* Wallet object with {@code forceReset} set {@code true}. It won't work.</p>
*
* <p>If {@code forceReset} is {@code true}, then no transactions are loaded from the wallet, and it is configured
* to replay transactions from the blockchain (as if the wallet had been loaded and {@link Wallet.reset}
* had been called immediately thereafter).
*
* <p>A wallet can be unreadable for various reasons, such as inability to open the file, corrupt data, internally
* inconsistent data, a wallet extension marked as mandatory that cannot be handled and so on. You should always
* handle {@link UnreadableWalletException} and communicate failure to the user in an appropriate manner.</p>
*
* @throws UnreadableWalletException thrown in various error conditions (see description).
*/
public Wallet readWallet(NetworkParameters params, @Nullable WalletExtension[] extensions,
Protos.Wallet walletProto, boolean forceReset) throws UnreadableWalletException {
if (walletProto.getVersion() > CURRENT_WALLET_VERSION)
throw new UnreadableWalletException.FutureVersion();
if (!walletProto.getNetworkIdentifier().equals(params.getId()))
@ -450,33 +491,40 @@ public class WalletProtobufSerializer {
wallet.setDescription(walletProto.getDescription());
}
// Read all transactions and insert into the txMap.
for (Protos.Transaction txProto : walletProto.getTransactionList()) {
readTransaction(txProto, wallet.getParams());
}
// Update transaction outputs to point to inputs that spend them
for (Protos.Transaction txProto : walletProto.getTransactionList()) {
WalletTransaction wtx = connectTransactionOutputs(txProto);
wallet.addWalletTransaction(wtx);
}
// Update the lastBlockSeenHash.
if (!walletProto.hasLastSeenBlockHash()) {
if (forceReset) {
// Should mirror Wallet.reset()
wallet.setLastBlockSeenHash(null);
} else {
wallet.setLastBlockSeenHash(byteStringToHash(walletProto.getLastSeenBlockHash()));
}
if (!walletProto.hasLastSeenBlockHeight()) {
wallet.setLastBlockSeenHeight(-1);
wallet.setLastBlockSeenTimeSecs(0);
} else {
wallet.setLastBlockSeenHeight(walletProto.getLastSeenBlockHeight());
}
// Will default to zero if not present.
wallet.setLastBlockSeenTimeSecs(walletProto.getLastSeenBlockTimeSecs());
// Read all transactions and insert into the txMap.
for (Protos.Transaction txProto : walletProto.getTransactionList()) {
readTransaction(txProto, wallet.getParams());
}
if (walletProto.hasKeyRotationTime()) {
wallet.setKeyRotationTime(new Date(walletProto.getKeyRotationTime() * 1000));
// Update transaction outputs to point to inputs that spend them
for (Protos.Transaction txProto : walletProto.getTransactionList()) {
WalletTransaction wtx = connectTransactionOutputs(txProto);
wallet.addWalletTransaction(wtx);
}
// Update the lastBlockSeenHash.
if (!walletProto.hasLastSeenBlockHash()) {
wallet.setLastBlockSeenHash(null);
} else {
wallet.setLastBlockSeenHash(byteStringToHash(walletProto.getLastSeenBlockHash()));
}
if (!walletProto.hasLastSeenBlockHeight()) {
wallet.setLastBlockSeenHeight(-1);
} else {
wallet.setLastBlockSeenHeight(walletProto.getLastSeenBlockHeight());
}
// Will default to zero if not present.
wallet.setLastBlockSeenTimeSecs(walletProto.getLastSeenBlockTimeSecs());
if (walletProto.hasKeyRotationTime()) {
wallet.setKeyRotationTime(new Date(walletProto.getKeyRotationTime() * 1000));
}
}
loadExtensions(wallet, extensions != null ? extensions : new WalletExtension[0], walletProto);
@ -554,7 +602,7 @@ public class WalletProtobufSerializer {
if (txProto.hasUpdatedAt()) {
tx.setUpdateTime(new Date(txProto.getUpdatedAt()));
}
for (Protos.TransactionOutput outputProto : txProto.getTransactionOutputList()) {
Coin value = Coin.valueOf(outputProto.getValue());
byte[] scriptBytes = outputProto.getScriptBytes().toByteArray();
@ -656,7 +704,7 @@ public class WalletProtobufSerializer {
input.connect(output);
}
}
if (txProto.hasConfidence()) {
Protos.TransactionConfidence confidenceProto = txProto.getConfidence();
TransactionConfidence confidence = tx.getConfidence();
@ -737,7 +785,7 @@ public class WalletProtobufSerializer {
/**
* Cheap test to see if input stream is a wallet. This checks for a magic value at the beginning of the stream.
*
*
* @param is
* input stream to test
* @return true if input stream is a wallet

View File

@ -311,11 +311,14 @@ public class WalletTool {
InputStream walletInputStream = null;
try {
boolean forceReset = action == ActionEnum.RESET
|| (action == ActionEnum.SYNC
&& options.has("force"));
WalletProtobufSerializer loader = new WalletProtobufSerializer();
if (options.has("ignore-mandatory-extensions"))
loader.setRequireMandatoryExtensions(false);
walletInputStream = new BufferedInputStream(new FileInputStream(walletFile));
wallet = loader.readWallet(walletInputStream);
wallet = loader.readWallet(walletInputStream, forceReset, (WalletExtension[])(null));
if (!wallet.getParams().equals(params)) {
System.err.println("Wallet does not match requested network parameters: " +
wallet.getParams().getId() + " vs " + params.getId());
@ -372,7 +375,7 @@ public class WalletTool {
System.err.println("************** WALLET IS INCONSISTENT *****************");
return;
}
saveWallet(walletFile);
if (options.has(waitForFlag)) {

View File

@ -24,7 +24,7 @@ Usage: wallet-tool --flags action-name
current-receive-addr Prints the current receive address, deriving one if needed. Addresses derived with this action are
independent of addresses derived with the add-key action.
sync Sync the wallet with the latest block chain (download new transactions).
If the chain file does not exist this will RESET the wallet.
If the chain file does not exist or if --force is present, this will RESET the wallet.
reset Deletes all transactions from the wallet, for if you want to replay the chain.
send Creates and broadcasts a transaction from the given wallet.
Requires either --output or --payment-request to be specified.
@ -82,4 +82,4 @@ will be printed. Waiting occurs after the --action is performed, if any is speci
--waitfor=EVER Never quit.
--waitfor=WALLET_TX Any transaction that sends coins to or from the wallet.
--waitfor=BLOCK A new block that builds on the best chain.
--waitfor=BALANCE Waits until the wallets balance meets the --condition.\n";
--waitfor=BALANCE Waits until the wallets balance meets the --condition.\n";