private Wallet restoreWalletFromBackup() { InputStream is = null; try { is = openFileInput(Constants.Files.WALLET_KEY_BACKUP_PROTOBUF); final Wallet wallet = new WalletProtobufSerializer().readWallet(is); if (!wallet.isConsistent()) throw new Error("inconsistent backup"); resetBlockchain(); Toast.makeText(this, R.string.toast_wallet_reset, Toast.LENGTH_LONG).show(); log.info("wallet restored from backup: '" + Constants.Files.WALLET_KEY_BACKUP_PROTOBUF + "'"); return wallet; } catch (final IOException x) { throw new Error("cannot read backup", x); } catch (final UnreadableWalletException x) { throw new Error("cannot read backup", x); } finally { try { is.close(); } catch (final IOException x) { // swallow } } }
private void afterLoadWallet() { wallet.autosaveToFile(walletFile, 10, TimeUnit.SECONDS, new WalletAutosaveEventListener()); // clean up spam wallet.cleanup(); migrateBackup(); }
public void replaceWallet(final Wallet newWallet) { internalResetBlockchain(); // implicitly stops blockchain service wallet.shutdownAutosaveAndWait(); wallet = newWallet; config.maybeIncrementBestChainHeightEver(newWallet.getLastBlockSeenHeight()); afterLoadWallet(); final Intent broadcast = new Intent(ACTION_WALLET_CHANGED); broadcast.setPackage(getPackageName()); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); config.disarmBackupReminder(); }
/** * Called when the connection terminates. Notifies the {@link StoredServerChannel} object that we * can attempt to resume this channel in the future and stops generating messages for the client. * * <p>Note that this <b>MUST</b> still be called even after either {@link * ServerConnection#destroyConnection(CloseReason)} or {@link PaymentChannelServer#close()} is * called to actually handle the connection close logic. */ public void connectionClosed() { lock.lock(); try { log.info("Server channel closed."); connectionOpen = false; try { if (state != null && state.getMultisigContract() != null) { StoredPaymentChannelServerStates channels = (StoredPaymentChannelServerStates) wallet.getExtensions().get(StoredPaymentChannelServerStates.EXTENSION_ID); if (channels != null) { StoredServerChannel storedServerChannel = channels.getChannel(state.getMultisigContract().getHash()); if (storedServerChannel != null) { storedServerChannel.clearConnectedHandler(); } } } } catch (IllegalStateException e) { // Expected when we call getMultisigContract() sometimes } } finally { lock.unlock(); } }
private void protobufSerializeWallet(@Nonnull final Wallet wallet) throws IOException { final long start = System.currentTimeMillis(); wallet.saveToFile(walletFile); // make wallets world accessible in test mode if (Constants.TEST) Io.chmod(walletFile, 0777); log.debug( "wallet saved to: '" + walletFile + "', took " + (System.currentTimeMillis() - start) + "ms"); }
@GuardedBy("lock") private void receiveContractMessage(Protos.TwoWayChannelMessage msg) throws VerificationException { checkState(step == InitStep.WAITING_ON_CONTRACT && msg.hasProvideContract()); log.info("Got contract, broadcasting and responding with CHANNEL_OPEN"); final Protos.ProvideContract providedContract = msg.getProvideContract(); // TODO notify connection handler that timeout should be significantly extended as we wait for // network propagation? final Transaction multisigContract = new Transaction(wallet.getParams(), providedContract.getTx().toByteArray()); step = InitStep.WAITING_ON_MULTISIG_ACCEPTANCE; state .provideMultiSigContract(multisigContract) .addListener( new Runnable() { @Override public void run() { multisigContractPropogated(providedContract, multisigContract.getHash()); } }, Threading.SAME_THREAD); }
@GuardedBy("lock") private void receiveRefundMessage(Protos.TwoWayChannelMessage msg) throws VerificationException { checkState(step == InitStep.WAITING_ON_UNSIGNED_REFUND && msg.hasProvideRefund()); log.info("Got refund transaction, returning signature"); Protos.ProvideRefund providedRefund = msg.getProvideRefund(); state = new PaymentChannelServerState(broadcaster, wallet, myKey, expireTime); byte[] signature = state.provideRefundTransaction( new Transaction(wallet.getParams(), providedRefund.getTx().toByteArray()), providedRefund.getMultisigKey().toByteArray()); step = InitStep.WAITING_ON_CONTRACT; Protos.ReturnRefund.Builder returnRefundBuilder = Protos.ReturnRefund.newBuilder().setSignature(ByteString.copyFrom(signature)); conn.sendToClient( Protos.TwoWayChannelMessage.newBuilder() .setReturnRefund(returnRefundBuilder) .setType(Protos.TwoWayChannelMessage.MessageType.RETURN_REFUND) .build()); }
public void processDirectTransaction(@Nonnull final Transaction tx) throws VerificationException { if (wallet.isTransactionRelevant(tx)) { wallet.receivePending(tx, null); broadcastTransaction(tx); } }
private void loadWalletFromProtobuf() { if (walletFile.exists()) { final long start = System.currentTimeMillis(); FileInputStream walletStream = null; try { walletStream = new FileInputStream(walletFile); wallet = new WalletProtobufSerializer().readWallet(walletStream); if (!wallet.getParams().equals(Constants.NETWORK_PARAMETERS)) throw new UnreadableWalletException( "bad wallet network parameters: " + wallet.getParams().getId()); log.info( "wallet loaded from: '" + walletFile + "', took " + (System.currentTimeMillis() - start) + "ms"); } catch (final FileNotFoundException x) { log.error("problem loading wallet", x); Toast.makeText(WalletApplication.this, x.getClass().getName(), Toast.LENGTH_LONG).show(); wallet = restoreWalletFromBackup(); } catch (final UnreadableWalletException x) { log.error("problem loading wallet", x); Toast.makeText(WalletApplication.this, x.getClass().getName(), Toast.LENGTH_LONG).show(); wallet = restoreWalletFromBackup(); } finally { if (walletStream != null) { try { walletStream.close(); } catch (final IOException x) { // swallow } } } if (!wallet.isConsistent()) { Toast.makeText(this, "inconsistent wallet: " + walletFile, Toast.LENGTH_LONG).show(); wallet = restoreWalletFromBackup(); } if (!wallet.getParams().equals(Constants.NETWORK_PARAMETERS)) throw new Error("bad wallet network parameters: " + wallet.getParams().getId()); } else { wallet = new Wallet(Constants.NETWORK_PARAMETERS); backupWallet(); config.armBackupReminder(); log.info("new wallet created"); } }
@GuardedBy("lock") private void receiveVersionMessage(Protos.TwoWayChannelMessage msg) throws VerificationException { checkState(step == InitStep.WAITING_ON_CLIENT_VERSION && msg.hasClientVersion()); final Protos.ClientVersion clientVersion = msg.getClientVersion(); final int major = clientVersion.getMajor(); if (major != SERVER_MAJOR_VERSION) { error( "This server needs protocol version " + SERVER_MAJOR_VERSION + " , client offered " + major, Protos.Error.ErrorCode.NO_ACCEPTABLE_VERSION, CloseReason.NO_ACCEPTABLE_VERSION); return; } Protos.ServerVersion.Builder versionNegotiationBuilder = Protos.ServerVersion.newBuilder() .setMajor(SERVER_MAJOR_VERSION) .setMinor(SERVER_MINOR_VERSION); conn.sendToClient( Protos.TwoWayChannelMessage.newBuilder() .setType(Protos.TwoWayChannelMessage.MessageType.SERVER_VERSION) .setServerVersion(versionNegotiationBuilder) .build()); ByteString reopenChannelContractHash = clientVersion.getPreviousChannelContractHash(); if (reopenChannelContractHash != null && reopenChannelContractHash.size() == 32) { Sha256Hash contractHash = new Sha256Hash(reopenChannelContractHash.toByteArray()); log.info("New client that wants to resume {}", contractHash); StoredPaymentChannelServerStates channels = (StoredPaymentChannelServerStates) wallet.getExtensions().get(StoredPaymentChannelServerStates.EXTENSION_ID); if (channels != null) { StoredServerChannel storedServerChannel = channels.getChannel(contractHash); if (storedServerChannel != null) { final PaymentChannelServer existingHandler = storedServerChannel.setConnectedHandler(this, false); if (existingHandler != this) { log.warn(" ... and that channel is already in use, disconnecting other user."); existingHandler.close(); storedServerChannel.setConnectedHandler(this, true); } log.info("Got resume version message, responding with VERSIONS and CHANNEL_OPEN"); state = storedServerChannel.getOrCreateState(wallet, broadcaster); step = InitStep.CHANNEL_OPEN; conn.sendToClient( Protos.TwoWayChannelMessage.newBuilder() .setType(Protos.TwoWayChannelMessage.MessageType.CHANNEL_OPEN) .build()); conn.channelOpen(contractHash); return; } else { log.error(" ... but we do not have any record of that contract! Resume failed."); } } else { log.error(" ... but we do not have any stored channels! Resume failed."); } } log.info( "Got initial version message, responding with VERSIONS and INITIATE: min value={}", minAcceptedChannelSize.value); myKey = new ECKey(); wallet.freshReceiveKey(); expireTime = Utils.currentTimeSeconds() + truncateTimeWindow(clientVersion.getTimeWindowSecs()); step = InitStep.WAITING_ON_UNSIGNED_REFUND; Protos.Initiate.Builder initiateBuilder = Protos.Initiate.newBuilder() .setMultisigKey(ByteString.copyFrom(myKey.getPubKey())) .setExpireTimeSecs(expireTime) .setMinAcceptedChannelSize(minAcceptedChannelSize.value) .setMinPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value); conn.sendToClient( Protos.TwoWayChannelMessage.newBuilder() .setInitiate(initiateBuilder) .setType(Protos.TwoWayChannelMessage.MessageType.INITIATE) .build()); }