/** Finds an inactive channel with the given id and returns it, or returns null. */ @Nullable StoredClientChannel getUsableChannelForServerID(Sha256Hash id) { lock.lock(); try { Set<StoredClientChannel> setChannels = mapChannels.get(id); for (StoredClientChannel channel : setChannels) { synchronized (channel) { // Check if the channel is usable (has money, inactive) and if so, activate it. log.info( "Considering channel {} contract {}", channel.hashCode(), channel.contract.getHash()); if (channel.close != null || channel.valueToMe.equals(Coin.ZERO)) { log.info(" ... but is closed or empty"); continue; } if (!channel.active) { log.info(" ... activating"); channel.active = true; return channel; } log.info(" ... but is already active"); } } } finally { lock.unlock(); } return null; }
/** * Returns the number of seconds from now until this servers next channel will expire, or zero if * no unexpired channels found. */ public long getSecondsUntilExpiry(Sha256Hash id) { lock.lock(); try { final Set<StoredClientChannel> setChannels = mapChannels.get(id); final long nowSeconds = Utils.currentTimeSeconds(); int earliestTime = Integer.MAX_VALUE; for (StoredClientChannel channel : setChannels) { synchronized (channel) { if (channel.expiryTimeSeconds() > nowSeconds) earliestTime = Math.min(earliestTime, (int) channel.expiryTimeSeconds()); } } return earliestTime == Integer.MAX_VALUE ? 0 : earliestTime - nowSeconds; } finally { lock.unlock(); } }
@Override public void deserializeWalletExtension(Wallet containingWallet, byte[] data) throws Exception { lock.lock(); try { checkState(this.containingWallet == null || this.containingWallet == containingWallet); this.containingWallet = containingWallet; NetworkParameters params = containingWallet.getParams(); ClientState.StoredClientPaymentChannels states = ClientState.StoredClientPaymentChannels.parseFrom(data); for (ClientState.StoredClientPaymentChannel storedState : states.getChannelsList()) { Transaction refundTransaction = params .getDefaultSerializer() .makeTransaction(storedState.getRefundTransaction().toByteArray()); refundTransaction.getConfidence().setSource(TransactionConfidence.Source.SELF); ECKey myKey = (storedState.getMyKey().isEmpty()) ? containingWallet.findKeyFromPubKey(storedState.getMyPublicKey().toByteArray()) : ECKey.fromPrivate(storedState.getMyKey().toByteArray()); StoredClientChannel channel = new StoredClientChannel( Sha256Hash.wrap(storedState.getId().toByteArray()), params .getDefaultSerializer() .makeTransaction(storedState.getContractTransaction().toByteArray()), refundTransaction, myKey, Coin.valueOf(storedState.getValueToMe()), Coin.valueOf(storedState.getRefundFees()), false); if (storedState.hasCloseTransactionHash()) { Sha256Hash closeTxHash = Sha256Hash.wrap(storedState.getCloseTransactionHash().toByteArray()); channel.close = containingWallet.getTransaction(closeTxHash); } putChannel(channel, false); } } finally { lock.unlock(); } }
// Adds this channel and optionally notifies the wallet of an update to this extension (used // during deserialize) private void putChannel(final StoredClientChannel channel, boolean updateWallet) { lock.lock(); try { mapChannels.put(channel.id, channel); channelTimeoutHandler.schedule( new TimerTask() { @Override public void run() { TransactionBroadcaster announcePeerGroup = getAnnouncePeerGroup(); removeChannel(channel); announcePeerGroup.broadcastTransaction(channel.contract); announcePeerGroup.broadcastTransaction(channel.refund); } // Add the difference between real time and Utils.now() so that test-cases can use a // mock clock. }, new Date( channel.expiryTimeSeconds() * 1000 + (System.currentTimeMillis() - Utils.currentTimeMillis()))); } finally { lock.unlock(); } if (updateWallet) updatedChannel(channel); }
/** * Notifies the set of stored states that a channel has been updated. Use to notify the wallet of * an update to this wallet extension. */ void updatedChannel(final StoredClientChannel channel) { log.info("Stored client channel {} was updated", channel.hashCode()); containingWallet.addOrUpdateExtension(this); }