mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-03 05:57:21 +00:00
Payment channels: tweaks to the examples.
Print wallet state at various points. Demonstrate disconnecting vs closing channels.
This commit is contained in:
@@ -16,10 +16,7 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.examples;
|
package com.google.bitcoin.examples;
|
||||||
|
|
||||||
import com.google.bitcoin.core.ECKey;
|
import com.google.bitcoin.core.*;
|
||||||
import com.google.bitcoin.core.NetworkParameters;
|
|
||||||
import com.google.bitcoin.core.Utils;
|
|
||||||
import com.google.bitcoin.core.Wallet;
|
|
||||||
import com.google.bitcoin.kits.WalletAppKit;
|
import com.google.bitcoin.kits.WalletAppKit;
|
||||||
import com.google.bitcoin.params.TestNet3Params;
|
import com.google.bitcoin.params.TestNet3Params;
|
||||||
import com.google.bitcoin.protocols.channels.PaymentChannelClientConnection;
|
import com.google.bitcoin.protocols.channels.PaymentChannelClientConnection;
|
||||||
@@ -29,14 +26,17 @@ import com.google.bitcoin.utils.BriefLogFormatter;
|
|||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.Uninterruptibles;
|
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static com.google.bitcoin.core.Utils.CENT;
|
||||||
|
import static java.math.BigInteger.TEN;
|
||||||
|
import static java.math.BigInteger.ZERO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple client that connects to the given host, opens a channel, and pays one cent.
|
* Simple client that connects to the given host, opens a channel, and pays one cent.
|
||||||
@@ -44,7 +44,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|||||||
public class ExamplePaymentChannelClient {
|
public class ExamplePaymentChannelClient {
|
||||||
private static final org.slf4j.Logger log = LoggerFactory.getLogger(ExamplePaymentChannelClient.class);
|
private static final org.slf4j.Logger log = LoggerFactory.getLogger(ExamplePaymentChannelClient.class);
|
||||||
private WalletAppKit appKit;
|
private WalletAppKit appKit;
|
||||||
private final BigInteger maxAcceptableRequestedAmount;
|
private final BigInteger channelSize;
|
||||||
private final ECKey myKey;
|
private final ECKey myKey;
|
||||||
private final NetworkParameters params;
|
private final NetworkParameters params;
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ public class ExamplePaymentChannelClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ExamplePaymentChannelClient() {
|
public ExamplePaymentChannelClient() {
|
||||||
maxAcceptableRequestedAmount = Utils.COIN;
|
channelSize = CENT;
|
||||||
myKey = new ECKey();
|
myKey = new ECKey();
|
||||||
params = TestNet3Params.get();
|
params = TestNet3Params.get();
|
||||||
}
|
}
|
||||||
@@ -77,6 +77,9 @@ public class ExamplePaymentChannelClient {
|
|||||||
// We now have active network connections and a fully synced wallet.
|
// We now have active network connections and a fully synced wallet.
|
||||||
// Add a new key which will be used for the multisig contract.
|
// Add a new key which will be used for the multisig contract.
|
||||||
appKit.wallet().addKey(myKey);
|
appKit.wallet().addKey(myKey);
|
||||||
|
appKit.wallet().allowSpendingUnconfirmedTransactions();
|
||||||
|
|
||||||
|
System.out.println(appKit.wallet());
|
||||||
|
|
||||||
// Create the object which manages the payment channels protocol, client side. Tell it where the server to
|
// Create the object which manages the payment channels protocol, client side. Tell it where the server to
|
||||||
// connect to is, along with some reasonable network timeouts, the wallet and our temporary key. We also have
|
// connect to is, along with some reasonable network timeouts, the wallet and our temporary key. We also have
|
||||||
@@ -86,53 +89,69 @@ public class ExamplePaymentChannelClient {
|
|||||||
// the wallet, then it'll re-use that one instead.
|
// the wallet, then it'll re-use that one instead.
|
||||||
final int timeoutSecs = 15;
|
final int timeoutSecs = 15;
|
||||||
final InetSocketAddress server = new InetSocketAddress(host, 4242);
|
final InetSocketAddress server = new InetSocketAddress(host, 4242);
|
||||||
PaymentChannelClientConnection client = null;
|
|
||||||
|
|
||||||
while (client == null) {
|
waitForSufficientBalance(channelSize);
|
||||||
try {
|
final String channelID = host;
|
||||||
final String channelID = host;
|
// Do this twice as each one sends 1/10th of a bitcent 5 times, so to send a bitcent, we do it twice. This
|
||||||
client = new PaymentChannelClientConnection(
|
// demonstrates resuming a channel that wasn't closed yet. It should close automatically once we run out
|
||||||
server, timeoutSecs, appKit.wallet(), myKey, maxAcceptableRequestedAmount, channelID);
|
// of money on the channel.
|
||||||
} catch (ValueOutOfRangeException e) {
|
log.info("Round one ...");
|
||||||
// We don't have enough money in our wallet yet. Wait and try again.
|
openAndSend(timeoutSecs, server, channelID);
|
||||||
waitForSufficientBalance(maxAcceptableRequestedAmount);
|
log.info("Round two ...");
|
||||||
}
|
log.info(appKit.wallet().toString());
|
||||||
}
|
openAndSend(timeoutSecs, server, channelID);
|
||||||
|
log.info("Waiting ...");
|
||||||
|
Thread.sleep(60 * 60 * 1000); // 1 hour.
|
||||||
|
log.info("Stopping ...");
|
||||||
|
appKit.stopAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openAndSend(int timeoutSecs, InetSocketAddress server, String channelID) throws IOException, ValueOutOfRangeException, InterruptedException {
|
||||||
|
PaymentChannelClientConnection client = new PaymentChannelClientConnection(
|
||||||
|
server, timeoutSecs, appKit.wallet(), myKey, channelSize, channelID);
|
||||||
// Opening the channel requires talking to the server, so it's asynchronous.
|
// Opening the channel requires talking to the server, so it's asynchronous.
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
Futures.addCallback(client.getChannelOpenFuture(), new FutureCallback<PaymentChannelClientConnection>() {
|
Futures.addCallback(client.getChannelOpenFuture(), new FutureCallback<PaymentChannelClientConnection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(PaymentChannelClientConnection client) {
|
public void onSuccess(PaymentChannelClientConnection client) {
|
||||||
// Success! We should be able to try making micropayments now. Try doing it 10 times.
|
// Success! We should be able to try making micropayments now. Try doing it 5 times.
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
try {
|
try {
|
||||||
client.incrementPayment(Utils.CENT);
|
client.incrementPayment(CENT.divide(TEN));
|
||||||
} catch (ValueOutOfRangeException e) {
|
} catch (ValueOutOfRangeException e) {
|
||||||
log.error("Failed to increment payment by a CENT, remaining value is {}", client.state().getValueRefunded());
|
log.error("Failed to increment payment by a CENT, remaining value is {}", client.state().getValueRefunded());
|
||||||
System.exit(-3);
|
System.exit(-3);
|
||||||
}
|
}
|
||||||
log.info("Successfully sent payment of one CENT, total remaining on channel is now {}", client.state().getValueRefunded());
|
log.info("Successfully sent payment of one CENT, total remaining on channel is now {}", client.state().getValueRefunded());
|
||||||
Uninterruptibles.sleepUninterruptibly(500, MILLISECONDS);
|
|
||||||
}
|
}
|
||||||
// Now tell the server we're done so they should broadcast the final transaction and refund us what's
|
if (client.state().getValueRefunded().equals(ZERO)) {
|
||||||
// left. If we never do this then eventually the server will time out and do it anyway and if the
|
// Now tell the server we're done so they should broadcast the final transaction and refund us what's
|
||||||
// server goes away for longer, then eventually WE will time out and the refund tx will get broadcast
|
// left. If we never do this then eventually the server will time out and do it anyway and if the
|
||||||
// by ourselves.
|
// server goes away for longer, then eventually WE will time out and the refund tx will get broadcast
|
||||||
log.info("Closing channel!");
|
// by ourselves.
|
||||||
client.close();
|
log.info("Closing channel for good");
|
||||||
|
client.close();
|
||||||
|
} else {
|
||||||
|
// Just unplug from the server but leave the channel open so it can resume later.
|
||||||
|
client.disconnectWithoutChannelClose();
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable throwable) {
|
public void onFailure(Throwable throwable) {
|
||||||
log.error("Failed to open connection", throwable);
|
log.error("Failed to open connection", throwable);
|
||||||
|
latch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
latch.await();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForSufficientBalance(BigInteger amount) {
|
private void waitForSufficientBalance(BigInteger amount) {
|
||||||
// Not enough money in the wallet.
|
// Not enough money in the wallet.
|
||||||
BigInteger amountPlusFee = amount.add(Wallet.SendRequest.DEFAULT_FEE_PER_KB);
|
BigInteger amountPlusFee = amount.add(Wallet.SendRequest.DEFAULT_FEE_PER_KB);
|
||||||
ListenableFuture<BigInteger> balanceFuture = appKit.wallet().getBalanceFuture(amountPlusFee, Wallet.BalanceType.AVAILABLE);
|
// ESTIMATED because we don't really need to wait for confirmation.
|
||||||
|
ListenableFuture<BigInteger> balanceFuture = appKit.wallet().getBalanceFuture(amountPlusFee, Wallet.BalanceType.ESTIMATED);
|
||||||
if (!balanceFuture.isDone()) {
|
if (!balanceFuture.isDone()) {
|
||||||
System.out.println("Please send " + Utils.bitcoinValueToFriendlyString(amountPlusFee) +
|
System.out.println("Please send " + Utils.bitcoinValueToFriendlyString(amountPlusFee) +
|
||||||
" BTC to " + myKey.toAddress(params));
|
" BTC to " + myKey.toAddress(params));
|
||||||
|
|||||||
@@ -16,10 +16,7 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.examples;
|
package com.google.bitcoin.examples;
|
||||||
|
|
||||||
import com.google.bitcoin.core.NetworkParameters;
|
import com.google.bitcoin.core.*;
|
||||||
import com.google.bitcoin.core.Sha256Hash;
|
|
||||||
import com.google.bitcoin.core.Utils;
|
|
||||||
import com.google.bitcoin.core.VerificationException;
|
|
||||||
import com.google.bitcoin.kits.WalletAppKit;
|
import com.google.bitcoin.kits.WalletAppKit;
|
||||||
import com.google.bitcoin.params.TestNet3Params;
|
import com.google.bitcoin.params.TestNet3Params;
|
||||||
import com.google.bitcoin.protocols.channels.*;
|
import com.google.bitcoin.protocols.channels.*;
|
||||||
@@ -62,9 +59,11 @@ public class ExamplePaymentChannelServer implements PaymentChannelServerListener
|
|||||||
};
|
};
|
||||||
appKit.startAndWait();
|
appKit.startAndWait();
|
||||||
|
|
||||||
|
System.out.println(appKit.wallet());
|
||||||
|
|
||||||
// We provide a peer group, a wallet, a timeout in seconds, the amount we require to start a channel and
|
// We provide a peer group, a wallet, a timeout in seconds, the amount we require to start a channel and
|
||||||
// an implementation of HandlerFactory, which we just implement ourselves.
|
// an implementation of HandlerFactory, which we just implement ourselves.
|
||||||
new PaymentChannelServerListener(appKit.peerGroup(), appKit.wallet(), 15, Utils.COIN, this).bindAndStart(4242);
|
new PaymentChannelServerListener(appKit.peerGroup(), appKit.wallet(), 15, Utils.CENT, this).bindAndStart(4242);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user