mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-13 02:35:52 +00:00
Payment channels: Added ClientChannelProperties and ServerChannelProperties to allow configuration of the channels.
This commit is contained in:
parent
7e3dbd5f5d
commit
2e0e81d3a2
@ -23,6 +23,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import org.bitcoin.paymentchannel.Protos;
|
||||
import org.bitcoinj.wallet.SendRequest;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -147,6 +148,41 @@ public interface IPaymentChannelClient {
|
||||
void channelOpen(boolean wasInitiated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Client payment channel properties.
|
||||
*/
|
||||
interface ClientChannelProperties {
|
||||
/**
|
||||
* Modify the sendRequest used for the contract.
|
||||
* @param sendRequest the current sendRequest.
|
||||
* @return the modified sendRequest.
|
||||
*/
|
||||
SendRequest modifyContractSendRequest(SendRequest sendRequest);
|
||||
|
||||
/**
|
||||
* The maximum acceptable min payment. If the server suggests a higher amount
|
||||
* the channel creation will be aborted.
|
||||
*/
|
||||
Coin acceptableMinPayment();
|
||||
|
||||
/**
|
||||
* The time in seconds, relative to now, on how long this channel should be kept open. Note that is is
|
||||
* a proposal to the server. The server may in turn propose something different.
|
||||
* See {@link org.bitcoinj.protocols.channels.IPaymentChannelClient.ClientConnection#acceptExpireTime(long)}
|
||||
*
|
||||
*/
|
||||
long timeWindow();
|
||||
|
||||
/**
|
||||
* An enum indicating which versions to support:
|
||||
* VERSION_1: use only version 1 of the protocol
|
||||
* VERSION_2_ALLOW_1: suggest version 2 but allow downgrade to version 1
|
||||
* VERSION_2: suggest version 2 and enforce use of version 2
|
||||
*
|
||||
*/
|
||||
PaymentChannelClient.VersionSelector versionSelector();
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementor of this interface creates payment channel clients that "talk back" with the given connection.
|
||||
* The client might be a PaymentChannelClient, or an RPC interface, or something else entirely.
|
||||
|
@ -20,6 +20,7 @@ package org.bitcoinj.protocols.channels;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.protocols.channels.PaymentChannelCloseException.CloseReason;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.SendRequest;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@ -56,6 +57,7 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
private static final org.slf4j.Logger log = LoggerFactory.getLogger(PaymentChannelClient.class);
|
||||
|
||||
protected final ReentrantLock lock = Threading.lock("channelclient");
|
||||
protected final ClientChannelProperties clientChannelProperties;
|
||||
|
||||
// Used to track the negotiated version number
|
||||
@GuardedBy("lock") private int majorVersion;
|
||||
@ -167,36 +169,8 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
* @param conn A callback listener which represents the connection to the server (forwards messages we generate to
|
||||
* the server)
|
||||
*/
|
||||
public PaymentChannelClient(Wallet wallet, ECKey myKey, Coin maxValue, Sha256Hash serverId,
|
||||
ClientConnection conn) {
|
||||
this(wallet,myKey,maxValue,serverId, conn, VersionSelector.VERSION_2_ALLOW_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new channel manager which waits for {@link PaymentChannelClient#connectionOpen()} before acting.
|
||||
* A default time window of {@link #DEFAULT_TIME_WINDOW} will be used.
|
||||
*
|
||||
* @param wallet The wallet which will be paid from, and where completed transactions will be committed.
|
||||
* Must already have a {@link StoredPaymentChannelClientStates} object in its extensions set.
|
||||
* @param myKey A freshly generated keypair used for the multisig contract and refund output.
|
||||
* @param maxValue The maximum value the server is allowed to request that we lock into this channel until the
|
||||
* refund transaction unlocks. Note that if there is a previously open channel, the refund
|
||||
* transaction used in this channel may be larger than maxValue. Thus, maxValue is not a method for
|
||||
* limiting the amount payable through this channel.
|
||||
* @param serverId An arbitrary hash representing this channel. This must uniquely identify the server. If an
|
||||
* existing stored channel exists in the wallet's {@link StoredPaymentChannelClientStates}, then an
|
||||
* attempt will be made to resume that channel.
|
||||
* @param conn A callback listener which represents the connection to the server (forwards messages we generate to
|
||||
* the server)
|
||||
* @param versionSelector An enum indicating which versions to support:
|
||||
* VERSION_1: use only version 1 of the protocol
|
||||
* VERSION_2_ALLOW_1: suggest version 2 but allow downgrade to version 1
|
||||
* VERSION_2: suggest version 2 and enforce use of version 2
|
||||
*
|
||||
*/
|
||||
public PaymentChannelClient(Wallet wallet, ECKey myKey, Coin maxValue, Sha256Hash serverId,
|
||||
ClientConnection conn, VersionSelector versionSelector) {
|
||||
this(wallet,myKey,maxValue,serverId, DEFAULT_TIME_WINDOW, null, conn, versionSelector);
|
||||
public PaymentChannelClient(Wallet wallet, ECKey myKey, Coin maxValue, Sha256Hash serverId, ClientConnection conn) {
|
||||
this(wallet,myKey,maxValue,serverId, null, conn);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,16 +186,13 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
* @param serverId An arbitrary hash representing this channel. This must uniquely identify the server. If an
|
||||
* existing stored channel exists in the wallet's {@link StoredPaymentChannelClientStates}, then an
|
||||
* attempt will be made to resume that channel.
|
||||
* @param timeWindow The time in seconds, relative to now, on how long this channel should be kept open. Note that is is
|
||||
* a proposal to the server. The server may in turn propose something different.
|
||||
* See {@link org.bitcoinj.protocols.channels.IPaymentChannelClient.ClientConnection#acceptExpireTime(long)}
|
||||
* @param userKeySetup Key derived from a user password, used to decrypt myKey, if it is encrypted, during setup.
|
||||
* @param conn A callback listener which represents the connection to the server (forwards messages we generate to
|
||||
* the server)
|
||||
*/
|
||||
public PaymentChannelClient(Wallet wallet, ECKey myKey, Coin maxValue, Sha256Hash serverId, long timeWindow,
|
||||
public PaymentChannelClient(Wallet wallet, ECKey myKey, Coin maxValue, Sha256Hash serverId,
|
||||
@Nullable KeyParameter userKeySetup, ClientConnection conn) {
|
||||
this(wallet, myKey, maxValue, serverId, timeWindow, userKeySetup, conn, VersionSelector.VERSION_2_ALLOW_1);
|
||||
this(wallet, myKey, maxValue, serverId, userKeySetup, defaultChannelProperties, conn);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,28 +208,28 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
* @param serverId An arbitrary hash representing this channel. This must uniquely identify the server. If an
|
||||
* existing stored channel exists in the wallet's {@link StoredPaymentChannelClientStates}, then an
|
||||
* attempt will be made to resume that channel.
|
||||
* @param timeWindow The time in seconds, relative to now, on how long this channel should be kept open. Note that is is
|
||||
* a proposal to the server. The server may in turn propose something different.
|
||||
* See {@link org.bitcoinj.protocols.channels.IPaymentChannelClient.ClientConnection#acceptExpireTime(long)}
|
||||
* @param userKeySetup Key derived from a user password, used to decrypt myKey, if it is encrypted, during setup.
|
||||
* @param clientChannelProperties Modify the channel's properties. You may extend {@link DefaultClientChannelProperties}
|
||||
* @param conn A callback listener which represents the connection to the server (forwards messages we generate to
|
||||
* the server)
|
||||
* @param versionSelector An enum indicating which versions to support:
|
||||
* VERSION_1: use only version 1 of the protocol
|
||||
* VERSION_2_ALLOW_1: suggest version 2 but allow downgrade to version 1
|
||||
* VERSION_2: suggest version 2 and enforce use of version 2
|
||||
*/
|
||||
public PaymentChannelClient(Wallet wallet, ECKey myKey, Coin maxValue, Sha256Hash serverId, long timeWindow,
|
||||
@Nullable KeyParameter userKeySetup, ClientConnection conn, VersionSelector versionSelector) {
|
||||
public PaymentChannelClient(Wallet wallet, ECKey myKey, Coin maxValue, Sha256Hash serverId,
|
||||
@Nullable KeyParameter userKeySetup, @Nullable ClientChannelProperties clientChannelProperties,
|
||||
ClientConnection conn) {
|
||||
this.wallet = checkNotNull(wallet);
|
||||
this.myKey = checkNotNull(myKey);
|
||||
this.maxValue = checkNotNull(maxValue);
|
||||
this.serverId = checkNotNull(serverId);
|
||||
checkState(timeWindow >= 0);
|
||||
this.timeWindow = timeWindow;
|
||||
this.conn = checkNotNull(conn);
|
||||
this.userKeySetup = userKeySetup;
|
||||
this.versionSelector = versionSelector;
|
||||
if (clientChannelProperties == null) {
|
||||
this.clientChannelProperties = defaultChannelProperties;
|
||||
} else {
|
||||
this.clientChannelProperties = clientChannelProperties;
|
||||
}
|
||||
this.timeWindow = clientChannelProperties.timeWindow();
|
||||
checkState(timeWindow >= 0);
|
||||
this.versionSelector = clientChannelProperties.versionSelector();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -299,12 +270,12 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
|
||||
// For now we require a hard-coded value. In future this will have to get more complex and dynamic as the fees
|
||||
// start to float.
|
||||
final long MIN_PAYMENT = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value;
|
||||
if (initiate.getMinPayment() != MIN_PAYMENT) {
|
||||
log.error("Server requested a min payment of {} but we expected {}", initiate.getMinPayment(), MIN_PAYMENT);
|
||||
final long maxMin = clientChannelProperties.acceptableMinPayment().value;
|
||||
if (initiate.getMinPayment() > maxMin) {
|
||||
log.error("Server requested a min payment of {} but we only accept up to {}", initiate.getMinPayment(), maxMin);
|
||||
errorBuilder.setCode(Protos.Error.ErrorCode.MIN_PAYMENT_TOO_LARGE);
|
||||
errorBuilder.setExpectedValue(MIN_PAYMENT);
|
||||
missing = Coin.valueOf(initiate.getMinPayment() - MIN_PAYMENT);
|
||||
errorBuilder.setExpectedValue(maxMin);
|
||||
missing = Coin.valueOf(initiate.getMinPayment() - maxMin);
|
||||
return CloseReason.SERVER_REQUESTED_TOO_MUCH_VALUE;
|
||||
}
|
||||
|
||||
@ -322,7 +293,7 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
return CloseReason.NO_ACCEPTABLE_VERSION;
|
||||
}
|
||||
try {
|
||||
state.initiate(userKeySetup);
|
||||
state.initiate(userKeySetup, clientChannelProperties);
|
||||
} catch (ValueOutOfRangeException e) {
|
||||
log.error("Value out of range when trying to initiate", e);
|
||||
errorBuilder.setCode(Protos.Error.ErrorCode.CHANNEL_VALUE_TOO_LARGE);
|
||||
@ -754,4 +725,28 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
// Ensure the future runs without the client lock held.
|
||||
future.set(new PaymentIncrementAck(value, paymentAck.getInfo()));
|
||||
}
|
||||
|
||||
public static class DefaultClientChannelProperties implements ClientChannelProperties {
|
||||
|
||||
@Override
|
||||
public SendRequest modifyContractSendRequest(SendRequest sendRequest) {
|
||||
return sendRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coin acceptableMinPayment() { return Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; }
|
||||
|
||||
@Override
|
||||
public long timeWindow() {
|
||||
return DEFAULT_TIME_WINDOW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VersionSelector versionSelector() {
|
||||
return VersionSelector.VERSION_2_ALLOW_1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static DefaultClientChannelProperties defaultChannelProperties = new DefaultClientChannelProperties();
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.bitcoinj.net.NioClient;
|
||||
import org.bitcoinj.net.ProtobufConnection;
|
||||
import org.bitcoinj.protocols.channels.IPaymentChannelClient.ClientChannelProperties;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
@ -68,44 +69,14 @@ public class PaymentChannelClientConnection {
|
||||
*/
|
||||
public PaymentChannelClientConnection(InetSocketAddress server, int timeoutSeconds, Wallet wallet, ECKey myKey,
|
||||
Coin maxValue, String serverId) throws IOException, ValueOutOfRangeException {
|
||||
this(server, timeoutSeconds, wallet, myKey, maxValue, serverId,
|
||||
PaymentChannelClient.VersionSelector.VERSION_2_ALLOW_1);
|
||||
this(server, timeoutSeconds, wallet, myKey, maxValue, serverId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to open a new connection to and open a payment channel with the given host and port, blocking until the
|
||||
* connection is open. The server is requested to keep the channel open for
|
||||
* {@link org.bitcoinj.protocols.channels.PaymentChannelClient#DEFAULT_TIME_WINDOW}
|
||||
* seconds. If the server proposes a longer time the channel will be closed.
|
||||
*
|
||||
* @param server The host/port pair where the server is listening.
|
||||
* @param timeoutSeconds The connection timeout and read timeout during initialization. This should be large enough
|
||||
* to accommodate ECDSA signature operations and network latency.
|
||||
* @param wallet The wallet which will be paid from, and where completed transactions will be committed.
|
||||
* Must be unencrypted. Must already have a {@link StoredPaymentChannelClientStates} object in its extensions set.
|
||||
* @param myKey A freshly generated keypair used for the multisig contract and refund output.
|
||||
* @param maxValue The maximum value this channel is allowed to request
|
||||
* @param serverId A unique ID which is used to attempt reopening of an existing channel.
|
||||
* This must be unique to the server, and, if your application is exposing payment channels to some
|
||||
* API, this should also probably encompass some caller UID to avoid applications opening channels
|
||||
* which were created by others.
|
||||
* @param versionSelector An enum indicating which versions to support:
|
||||
* VERSION_1: use only version 1 of the protocol
|
||||
* VERSION_2_ALLOW_1: suggest version 2 but allow downgrade to version 1
|
||||
* VERSION_2: suggest version 2 and enforce use of version 2
|
||||
* @throws IOException if there's an issue using the network.
|
||||
* @throws ValueOutOfRangeException if the balance of wallet is lower than maxValue.
|
||||
*/
|
||||
public PaymentChannelClientConnection(InetSocketAddress server, int timeoutSeconds, Wallet wallet, ECKey myKey,
|
||||
Coin maxValue, String serverId, PaymentChannelClient.VersionSelector versionSelector) throws IOException, ValueOutOfRangeException {
|
||||
this(server, timeoutSeconds, wallet, myKey, maxValue, serverId,
|
||||
PaymentChannelClient.DEFAULT_TIME_WINDOW, null, versionSelector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to open a new connection to and open a payment channel with the given host and port, blocking until the
|
||||
* connection is open. The server is requested to keep the channel open for {@param timeWindow}
|
||||
* seconds. If the server proposes a longer time the channel will be closed.
|
||||
* connection is open. The server is requested to keep the channel open for
|
||||
* {@link org.bitcoinj.protocols.channels.PaymentChannelClient#DEFAULT_TIME_WINDOW} seconds.
|
||||
* If the server proposes a longer time the channel will be closed.
|
||||
*
|
||||
* @param server The host/port pair where the server is listening.
|
||||
* @param timeoutSeconds The connection timeout and read timeout during initialization. This should be large enough
|
||||
@ -119,16 +90,13 @@ public class PaymentChannelClientConnection {
|
||||
* This must be unique to the server, and, if your application is exposing payment channels to some
|
||||
* API, this should also probably encompass some caller UID to avoid applications opening channels
|
||||
* which were created by others.
|
||||
* @param timeWindow The time in seconds, relative to now, on how long this channel should be kept open.
|
||||
* @param userKeySetup Key derived from a user password, used to decrypt myKey, if it is encrypted, during setup.
|
||||
* @throws IOException if there's an issue using the network.
|
||||
* @throws ValueOutOfRangeException if the balance of wallet is lower than maxValue.
|
||||
*/
|
||||
public PaymentChannelClientConnection(InetSocketAddress server, int timeoutSeconds, Wallet wallet, ECKey myKey,
|
||||
Coin maxValue, String serverId, final long timeWindow,
|
||||
@Nullable KeyParameter userKeySetup) throws IOException, ValueOutOfRangeException {
|
||||
this(server, timeoutSeconds, wallet, myKey, maxValue, serverId,
|
||||
timeWindow, userKeySetup, PaymentChannelClient.VersionSelector.VERSION_2_ALLOW_1);
|
||||
Coin maxValue, String serverId, @Nullable KeyParameter userKeySetup) throws IOException, ValueOutOfRangeException {
|
||||
this(server, timeoutSeconds, wallet, myKey, maxValue, serverId, userKeySetup, PaymentChannelClient.defaultChannelProperties);
|
||||
}
|
||||
|
||||
|
||||
@ -149,24 +117,20 @@ public class PaymentChannelClientConnection {
|
||||
* This must be unique to the server, and, if your application is exposing payment channels to some
|
||||
* API, this should also probably encompass some caller UID to avoid applications opening channels
|
||||
* which were created by others.
|
||||
* @param timeWindow The time in seconds, relative to now, on how long this channel should be kept open.
|
||||
* @param userKeySetup Key derived from a user password, used to decrypt myKey, if it is encrypted, during setup.
|
||||
* @param versionSelector An enum indicating which versions to support:
|
||||
* VERSION_1: use only version 1 of the protocol
|
||||
* VERSION_2_ALLOW_1: suggest version 2 but allow downgrade to version 1
|
||||
* VERSION_2: suggest version 2 and enforce use of version 2
|
||||
* @param clientChannelProperties Modifier to change the channel's configuration.
|
||||
*
|
||||
* @throws IOException if there's an issue using the network.
|
||||
* @throws ValueOutOfRangeException if the balance of wallet is lower than maxValue.
|
||||
*/
|
||||
public PaymentChannelClientConnection(InetSocketAddress server, int timeoutSeconds, Wallet wallet, ECKey myKey,
|
||||
Coin maxValue, String serverId, final long timeWindow,
|
||||
@Nullable KeyParameter userKeySetup, PaymentChannelClient.VersionSelector versionSelector)
|
||||
Coin maxValue, String serverId,
|
||||
@Nullable KeyParameter userKeySetup, final ClientChannelProperties clientChannelProperties)
|
||||
throws IOException, ValueOutOfRangeException {
|
||||
// Glue the object which vends/ingests protobuf messages in order to manage state to the network object which
|
||||
// reads/writes them to the wire in length prefixed form.
|
||||
channelClient = new PaymentChannelClient(wallet, myKey, maxValue, Sha256Hash.of(serverId.getBytes()), timeWindow,
|
||||
userKeySetup, new PaymentChannelClient.ClientConnection() {
|
||||
channelClient = new PaymentChannelClient(wallet, myKey, maxValue, Sha256Hash.of(serverId.getBytes()),
|
||||
userKeySetup, clientChannelProperties, new PaymentChannelClient.ClientConnection() {
|
||||
@Override
|
||||
public void sendToServer(Protos.TwoWayChannelMessage msg) {
|
||||
wireParser.write(msg);
|
||||
@ -180,7 +144,7 @@ public class PaymentChannelClientConnection {
|
||||
|
||||
@Override
|
||||
public boolean acceptExpireTime(long expireTime) {
|
||||
return expireTime <= (timeWindow + Utils.currentTimeSeconds() + 60); // One extra minute to compensate for time skew and latency
|
||||
return expireTime <= (clientChannelProperties.timeWindow() + Utils.currentTimeSeconds() + 60); // One extra minute to compensate for time skew and latency
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -189,7 +153,7 @@ public class PaymentChannelClientConnection {
|
||||
// Inform the API user that we're done and ready to roll.
|
||||
channelOpenFuture.set(PaymentChannelClientConnection.this);
|
||||
}
|
||||
}, versionSelector);
|
||||
});
|
||||
|
||||
// And glue back in the opposite direction - network to the channelClient.
|
||||
wireParser = new ProtobufConnection<Protos.TwoWayChannelMessage>(new ProtobufConnection.Listener<Protos.TwoWayChannelMessage>() {
|
||||
|
@ -24,9 +24,9 @@ import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.crypto.TransactionSignature;
|
||||
import org.bitcoinj.protocols.channels.IPaymentChannelClient.ClientChannelProperties;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.SendRequest;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
import org.bitcoinj.wallet.listeners.WalletCoinsReceivedEventListener;
|
||||
import org.slf4j.Logger;
|
||||
@ -60,14 +60,14 @@ import static com.google.common.base.Preconditions.*;
|
||||
* the given time (within a few hours), the channel must be closed or else the client will broadcast the refund
|
||||
* transaction and take back all the money once the expiry time is reached.</p>
|
||||
*
|
||||
* <p>To begin, the client calls {@link PaymentChannelV1ClientState#initiate()}, which moves the channel into state
|
||||
* <p>To begin, the client calls {@link PaymentChannelClientState#initiate(KeyParameter, ClientChannelProperties)}, which moves the channel into state
|
||||
* INITIATED and creates the initial multi-sig contract and refund transaction. If the wallet has insufficient funds an
|
||||
* exception will be thrown at this point. Once this is done, call
|
||||
* {@link PaymentChannelV1ClientState#getIncompleteRefundTransaction()} and pass the resultant transaction through to the
|
||||
* server. Once you have retrieved the signature, use {@link PaymentChannelV1ClientState#provideRefundSignature(byte[], KeyParameter)}.
|
||||
* You must then call {@link PaymentChannelV1ClientState#storeChannelInWallet(Sha256Hash)} to store the refund transaction
|
||||
* You must then call {@link PaymentChannelClientState#storeChannelInWallet(Sha256Hash)} to store the refund transaction
|
||||
* in the wallet, protecting you against a malicious server attempting to destroy all your coins. At this point, you can
|
||||
* provide the server with the multi-sig contract (via {@link PaymentChannelV1ClientState#getContract()}) safely.
|
||||
* provide the server with the multi-sig contract (via {@link PaymentChannelClientState#getContract()}) safely.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class PaymentChannelClientState {
|
||||
@ -126,9 +126,9 @@ public abstract class PaymentChannelClientState {
|
||||
|
||||
/**
|
||||
* Creates a state object for a payment channel client. It is expected that you be ready to
|
||||
* {@link PaymentChannelV1ClientState#initiate()} after construction (to avoid creating objects for channels which are
|
||||
* {@link PaymentChannelClientState#initiate(KeyParameter, ClientChannelProperties)} after construction (to avoid creating objects for channels which are
|
||||
* not going to finish opening) and thus some parameters provided here are only used in
|
||||
* {@link PaymentChannelV1ClientState#initiate()} to create the Multisig contract and refund transaction.
|
||||
* {@link PaymentChannelClientState#initiate(KeyParameter, ClientChannelProperties)} to create the Multisig contract and refund transaction.
|
||||
*
|
||||
* @param wallet a wallet that contains at least the specified amount of value.
|
||||
* @param myKey a freshly generated private key for this channel.
|
||||
@ -211,37 +211,28 @@ public abstract class PaymentChannelClientState {
|
||||
* Creates the initial multisig contract and incomplete refund transaction which can be requested at the appropriate
|
||||
* time using {@link PaymentChannelV1ClientState#getIncompleteRefundTransaction} and
|
||||
* {@link PaymentChannelV1ClientState#getContract()}. The way the contract is crafted can be adjusted by
|
||||
* overriding {@link PaymentChannelV1ClientState#editContractSendRequest(org.bitcoinj.wallet.Wallet.SendRequest)}.
|
||||
* By default unconfirmed coins are allowed to be used, as for micropayments the risk should be relatively low.
|
||||
*
|
||||
* @throws ValueOutOfRangeException if the value being used is too small to be accepted by the network
|
||||
* @throws InsufficientMoneyException if the wallet doesn't contain enough balance to initiate
|
||||
*/
|
||||
public void initiate() throws ValueOutOfRangeException, InsufficientMoneyException {
|
||||
initiate(null);
|
||||
initiate(null, PaymentChannelClient.defaultChannelProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the initial multisig contract and incomplete refund transaction which can be requested at the appropriate
|
||||
* time using {@link PaymentChannelV1ClientState#getIncompleteRefundTransaction} and
|
||||
* {@link PaymentChannelV1ClientState#getContract()}. The way the contract is crafted can be adjusted by
|
||||
* overriding {@link PaymentChannelV1ClientState#editContractSendRequest(org.bitcoinj.wallet.Wallet.SendRequest)}.
|
||||
* {@link PaymentChannelClientState#getContract()}.
|
||||
* By default unconfirmed coins are allowed to be used, as for micropayments the risk should be relatively low.
|
||||
* @param userKey Key derived from a user password, needed for any signing when the wallet is encrypted.
|
||||
* The wallet KeyCrypter is assumed.
|
||||
* The wallet KeyCrypter is assumed.
|
||||
* @param clientChannelProperties Modify the channel's configuration.
|
||||
*
|
||||
* @throws ValueOutOfRangeException if the value being used is too small to be accepted by the network
|
||||
* @throws InsufficientMoneyException if the wallet doesn't contain enough balance to initiate
|
||||
*/
|
||||
public abstract void initiate(@Nullable KeyParameter userKey) throws ValueOutOfRangeException, InsufficientMoneyException;
|
||||
|
||||
/**
|
||||
* You can override this method in order to control the construction of the initial contract that creates the
|
||||
* channel. For example if you want it to only use specific coins, you can adjust the coin selector here.
|
||||
* The default implementation does nothing.
|
||||
*/
|
||||
protected void editContractSendRequest(SendRequest req) {
|
||||
}
|
||||
public abstract void initiate(@Nullable KeyParameter userKey, ClientChannelProperties clientChannelProperties) throws ValueOutOfRangeException, InsufficientMoneyException;
|
||||
|
||||
/**
|
||||
* Gets the contract which was used to initialize this channel
|
||||
@ -392,7 +383,7 @@ public abstract class PaymentChannelClientState {
|
||||
|
||||
/**
|
||||
* Returns the fees that will be paid if the refund transaction has to be claimed because the server failed to settle
|
||||
* the channel properly. May only be called after {@link PaymentChannelV1ClientState#initiate()}
|
||||
* the channel properly. May only be called after {@link PaymentChannelClientState#initiate(KeyParameter, ClientChannelProperties)}
|
||||
*/
|
||||
public abstract Coin getRefundTxFees();
|
||||
|
||||
|
@ -129,6 +129,25 @@ public class PaymentChannelServer {
|
||||
}
|
||||
private final ServerConnection conn;
|
||||
|
||||
public interface ServerChannelProperties {
|
||||
/**
|
||||
* The size of the payment that the client is requested to pay in the initiate phase.
|
||||
*/
|
||||
Coin getMinPayment();
|
||||
|
||||
/**
|
||||
* The maximum allowed channel time window in seconds.
|
||||
* Note that the server need to be online for the whole time the channel is open.
|
||||
* Failure to do this could cause loss of all payments received on the channel.
|
||||
*/
|
||||
long getMaxTimeWindow();
|
||||
|
||||
/**
|
||||
* The minimum allowed channel time window in seconds, must be larger than 7200.
|
||||
*/
|
||||
long getMinTimeWindow();
|
||||
}
|
||||
|
||||
// Used to track the negotiated version number
|
||||
@GuardedBy("lock") private int majorVersion;
|
||||
|
||||
@ -144,6 +163,10 @@ public class PaymentChannelServer {
|
||||
// The key used for multisig in this channel
|
||||
@GuardedBy("lock") private ECKey myKey;
|
||||
|
||||
// The fee server charges for managing (and settling the channel).
|
||||
// This is will be requested in the setup via the min_payment field in the initiate message.
|
||||
private final Coin minPayment;
|
||||
|
||||
// The minimum accepted channel value
|
||||
private final Coin minAcceptedChannelSize;
|
||||
|
||||
@ -187,7 +210,7 @@ public class PaymentChannelServer {
|
||||
*/
|
||||
public PaymentChannelServer(TransactionBroadcaster broadcaster, Wallet wallet,
|
||||
Coin minAcceptedChannelSize, ServerConnection conn) {
|
||||
this(broadcaster, wallet, minAcceptedChannelSize, DEFAULT_MIN_TIME_WINDOW, DEFAULT_MAX_TIME_WINDOW, conn);
|
||||
this(broadcaster, wallet, minAcceptedChannelSize, new DefaultServerChannelProperties(), conn);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,22 +224,21 @@ public class PaymentChannelServer {
|
||||
* and may cause fees to be require to settle the channel. A reasonable value depends
|
||||
* entirely on the expected maximum for the channel, and should likely be somewhere
|
||||
* between a few bitcents and a bitcoin.
|
||||
* @param minTimeWindow The minimum allowed channel time window in seconds, must be larger than 7200.
|
||||
* @param maxTimeWindow The maximum allowed channel time window in seconds. Note that the server need to be online for the whole time the channel is open.
|
||||
* Failure to do this could cause loss of all payments received on the channel.
|
||||
* @param serverChannelProperties Modify the channel's properties. You may extend {@link DefaultServerChannelProperties}
|
||||
* @param conn A callback listener which represents the connection to the client (forwards messages we generate to
|
||||
* the client and will close the connection on request)
|
||||
*/
|
||||
public PaymentChannelServer(TransactionBroadcaster broadcaster, Wallet wallet,
|
||||
Coin minAcceptedChannelSize, long minTimeWindow, long maxTimeWindow, ServerConnection conn) {
|
||||
Coin minAcceptedChannelSize, ServerChannelProperties serverChannelProperties, ServerConnection conn) {
|
||||
minTimeWindow = serverChannelProperties.getMinTimeWindow();
|
||||
maxTimeWindow = serverChannelProperties.getMaxTimeWindow();
|
||||
if (minTimeWindow > maxTimeWindow) throw new IllegalArgumentException("minTimeWindow must be less or equal to maxTimeWindow");
|
||||
if (minTimeWindow < HARD_MIN_TIME_WINDOW) throw new IllegalArgumentException("minTimeWindow must be larger than" + HARD_MIN_TIME_WINDOW + " seconds");
|
||||
this.broadcaster = checkNotNull(broadcaster);
|
||||
this.wallet = checkNotNull(wallet);
|
||||
this.minPayment = checkNotNull(serverChannelProperties.getMinPayment());
|
||||
this.minAcceptedChannelSize = checkNotNull(minAcceptedChannelSize);
|
||||
this.conn = checkNotNull(conn);
|
||||
this.minTimeWindow = minTimeWindow;
|
||||
this.maxTimeWindow = maxTimeWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -299,7 +321,7 @@ public class PaymentChannelServer {
|
||||
.setMultisigKey(ByteString.copyFrom(myKey.getPubKey()))
|
||||
.setExpireTimeSecs(expireTime)
|
||||
.setMinAcceptedChannelSize(minAcceptedChannelSize.value)
|
||||
.setMinPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value);
|
||||
.setMinPayment(minPayment.value);
|
||||
|
||||
conn.sendToClient(Protos.TwoWayChannelMessage.newBuilder()
|
||||
.setInitiate(initiateBuilder)
|
||||
@ -634,4 +656,27 @@ public class PaymentChannelServer {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend this class and override the values you want to change.
|
||||
*/
|
||||
public static class DefaultServerChannelProperties implements ServerChannelProperties {
|
||||
|
||||
@Override
|
||||
public Coin getMinPayment() {
|
||||
return Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxTimeWindow() {
|
||||
return DEFAULT_MAX_TIME_WINDOW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinTimeWindow() {
|
||||
return DEFAULT_MIN_TIME_WINDOW;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.crypto.TransactionSignature;
|
||||
import org.bitcoinj.protocols.channels.IPaymentChannelClient.ClientChannelProperties;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptBuilder;
|
||||
import org.bitcoinj.wallet.AllowUnconfirmedCoinSelector;
|
||||
@ -71,9 +72,9 @@ public class PaymentChannelV1ClientState extends PaymentChannelClientState {
|
||||
|
||||
/**
|
||||
* Creates a state object for a payment channel client. It is expected that you be ready to
|
||||
* {@link PaymentChannelV1ClientState#initiate()} after construction (to avoid creating objects for channels which are
|
||||
* {@link PaymentChannelClientState#initiate(KeyParameter, ClientChannelProperties)} after construction (to avoid creating objects for channels which are
|
||||
* not going to finish opening) and thus some parameters provided here are only used in
|
||||
* {@link PaymentChannelV1ClientState#initiate()} to create the Multisig contract and refund transaction.
|
||||
* {@link PaymentChannelClientState#initiate(KeyParameter, ClientChannelProperties)} to create the Multisig contract and refund transaction.
|
||||
*
|
||||
* @param wallet a wallet that contains at least the specified amount of value.
|
||||
* @param myKey a freshly generated private key for this channel.
|
||||
@ -115,17 +116,17 @@ public class PaymentChannelV1ClientState extends PaymentChannelClientState {
|
||||
/**
|
||||
* Creates the initial multisig contract and incomplete refund transaction which can be requested at the appropriate
|
||||
* time using {@link PaymentChannelV1ClientState#getIncompleteRefundTransaction} and
|
||||
* {@link PaymentChannelV1ClientState#getContract()}. The way the contract is crafted can be adjusted by
|
||||
* overriding {@link PaymentChannelV1ClientState#editContractSendRequest(org.bitcoinj.core.Wallet.SendRequest)}.
|
||||
* {@link PaymentChannelV1ClientState#getContract()}.
|
||||
* By default unconfirmed coins are allowed to be used, as for micropayments the risk should be relatively low.
|
||||
* @param userKey Key derived from a user password, needed for any signing when the wallet is encrypted.
|
||||
* The wallet KeyCrypter is assumed.
|
||||
* The wallet KeyCrypter is assumed.
|
||||
* @param clientChannelProperties Modify the channel's configuration.
|
||||
*
|
||||
* @throws ValueOutOfRangeException if the value being used is too small to be accepted by the network
|
||||
* @throws InsufficientMoneyException if the wallet doesn't contain enough balance to initiate
|
||||
*/
|
||||
@Override
|
||||
public synchronized void initiate(@Nullable KeyParameter userKey) throws ValueOutOfRangeException, InsufficientMoneyException {
|
||||
public synchronized void initiate(@Nullable KeyParameter userKey, ClientChannelProperties clientChannelProperties) throws ValueOutOfRangeException, InsufficientMoneyException {
|
||||
final NetworkParameters params = wallet.getParams();
|
||||
Transaction template = new Transaction(params);
|
||||
// We always place the client key before the server key because, if either side wants some privacy, they can
|
||||
@ -139,9 +140,9 @@ public class PaymentChannelV1ClientState extends PaymentChannelClientState {
|
||||
throw new ValueOutOfRangeException("totalValue too small to use");
|
||||
SendRequest req = SendRequest.forTx(template);
|
||||
req.coinSelector = AllowUnconfirmedCoinSelector.get();
|
||||
editContractSendRequest(req);
|
||||
req.shuffleOutputs = false; // TODO: Fix things so shuffling is usable.
|
||||
req.aesKey = userKey;
|
||||
req = clientChannelProperties.modifyContractSendRequest(req);
|
||||
if (userKey != null) req.aesKey = userKey;
|
||||
wallet.completeTx(req);
|
||||
Coin multisigFee = req.tx.getFee();
|
||||
multisigContract = req.tx;
|
||||
|
@ -21,6 +21,7 @@ import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.crypto.TransactionSignature;
|
||||
import org.bitcoinj.protocols.channels.IPaymentChannelClient.ClientChannelProperties;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptBuilder;
|
||||
import org.bitcoinj.wallet.AllowUnconfirmedCoinSelector;
|
||||
@ -99,7 +100,7 @@ public class PaymentChannelV2ClientState extends PaymentChannelClientState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void initiate(@Nullable KeyParameter userKey) throws ValueOutOfRangeException, InsufficientMoneyException {
|
||||
public synchronized void initiate(@Nullable KeyParameter userKey, ClientChannelProperties clientChannelProperties) throws ValueOutOfRangeException, InsufficientMoneyException {
|
||||
final NetworkParameters params = wallet.getParams();
|
||||
Transaction template = new Transaction(params);
|
||||
// There is also probably a change output, but we don't bother shuffling them as it's obvious from the
|
||||
@ -113,9 +114,9 @@ public class PaymentChannelV2ClientState extends PaymentChannelClientState {
|
||||
throw new ValueOutOfRangeException("totalValue too small to use");
|
||||
SendRequest req = SendRequest.forTx(template);
|
||||
req.coinSelector = AllowUnconfirmedCoinSelector.get();
|
||||
editContractSendRequest(req);
|
||||
req.shuffleOutputs = false; // TODO: Fix things so shuffling is usable.
|
||||
req.aesKey = userKey;
|
||||
req = clientChannelProperties.modifyContractSendRequest(req);
|
||||
if (userKey != null) req.aesKey = userKey;
|
||||
wallet.completeTx(req);
|
||||
Coin multisigFee = req.tx.getFee();
|
||||
contract = req.tx;
|
||||
|
@ -17,6 +17,7 @@
|
||||
package org.bitcoinj.protocols.channels;
|
||||
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.protocols.channels.PaymentChannelClient.VersionSelector;
|
||||
import org.bitcoinj.testing.TestWithWallet;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
@ -48,6 +49,7 @@ import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.bitcoinj.core.Coin.*;
|
||||
import static org.bitcoinj.protocols.channels.PaymentChannelClient.VersionSelector.*;
|
||||
import static org.bitcoinj.protocols.channels.PaymentChannelCloseException.CloseReason;
|
||||
import static org.bitcoinj.testing.FakeTxBuilder.createFakeBlock;
|
||||
import static org.bitcoin.paymentchannel.Protos.TwoWayChannelMessage.MessageType;
|
||||
@ -75,20 +77,26 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
* version of the channel.
|
||||
*/
|
||||
@Parameterized.Parameters(name = "{index}: ChannelConnectionTest({0})")
|
||||
public static Collection<PaymentChannelClient.VersionSelector> data() {
|
||||
public static Collection<PaymentChannelClient.DefaultClientChannelProperties> data() {
|
||||
return Arrays.asList(
|
||||
PaymentChannelClient.VersionSelector.VERSION_1,
|
||||
PaymentChannelClient.VersionSelector.VERSION_2_ALLOW_1);
|
||||
new PaymentChannelClient.DefaultClientChannelProperties() {
|
||||
@Override
|
||||
public VersionSelector versionSelector() { return VERSION_1;}
|
||||
},
|
||||
new PaymentChannelClient.DefaultClientChannelProperties() {
|
||||
@Override
|
||||
public VersionSelector versionSelector() { return VERSION_2_ALLOW_1;}
|
||||
});
|
||||
}
|
||||
|
||||
@Parameterized.Parameter
|
||||
public PaymentChannelClient.VersionSelector versionSelector;
|
||||
public IPaymentChannelClient.ClientChannelProperties clientChannelProperties;
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if we are using a protocol version that requires the exchange of refunds.
|
||||
*/
|
||||
private boolean useRefunds() {
|
||||
return versionSelector == PaymentChannelClient.VersionSelector.VERSION_1;
|
||||
return clientChannelProperties.versionSelector() == VERSION_1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +104,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
* @return
|
||||
*/
|
||||
private boolean isMultiSigContract() {
|
||||
return versionSelector == PaymentChannelClient.VersionSelector.VERSION_1;
|
||||
return clientChannelProperties.versionSelector() == VERSION_1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -197,7 +205,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
server.bindAndStart(4243);
|
||||
|
||||
PaymentChannelClientConnection client = new PaymentChannelClientConnection(
|
||||
new InetSocketAddress("localhost", 4243), 30, wallet, myKey, COIN, "", PaymentChannelClient.DEFAULT_TIME_WINDOW, userKeySetup, versionSelector);
|
||||
new InetSocketAddress("localhost", 4243), 30, wallet, myKey, COIN, "", userKeySetup, clientChannelProperties);
|
||||
|
||||
// Wait for the multi-sig tx to be transmitted.
|
||||
broadcastTxPause.release();
|
||||
@ -280,7 +288,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
}
|
||||
// Gives the server crap and checks proper error responses are sent.
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
PaymentChannelServer server = pair.server;
|
||||
server.connectionOpen();
|
||||
client.connectionOpen();
|
||||
@ -305,7 +313,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
public void testServerErrorHandling_killSocketOnClose() throws Exception {
|
||||
// Make sure the server closes the socket on CLOSE
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
PaymentChannelServer server = pair.server;
|
||||
server.connectionOpen();
|
||||
client.connectionOpen();
|
||||
@ -323,7 +331,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
public void testServerErrorHandling_killSocketOnError() throws Exception {
|
||||
// Make sure the server closes the socket on ERROR
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
PaymentChannelServer server = pair.server;
|
||||
server.connectionOpen();
|
||||
client.connectionOpen();
|
||||
@ -347,7 +355,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
// Open up a normal channel.
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
pair.server.connectionOpen();
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
PaymentChannelServer server = pair.server;
|
||||
client.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -402,7 +410,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
// Open up a normal channel.
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
pair.server.connectionOpen();
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
PaymentChannelServer server = pair.server;
|
||||
client.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -462,7 +470,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
(StoredPaymentChannelClientStates) wallet.getExtensions().get(StoredPaymentChannelClientStates.EXTENSION_ID);
|
||||
|
||||
pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
server = pair.server;
|
||||
client.connectionOpen();
|
||||
server.connectionOpen();
|
||||
@ -489,7 +497,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
|
||||
// Now open up a new client with the same id and make sure the server disconnects the previous client.
|
||||
pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
server = pair.server;
|
||||
client.connectionOpen();
|
||||
server.connectionOpen();
|
||||
@ -501,7 +509,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
}
|
||||
// Make sure the server allows two simultaneous opens. It will close the first and allow resumption of the second.
|
||||
pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
server = pair.server;
|
||||
client.connectionOpen();
|
||||
server.connectionOpen();
|
||||
@ -585,7 +593,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
public void testClientUnknownVersion() throws Exception {
|
||||
// Tests client rejects unknown version
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
client.connectionOpen();
|
||||
pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION);
|
||||
client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
|
||||
@ -605,7 +613,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
// Tests that clients reject too large time windows
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster, 100);
|
||||
PaymentChannelServer server = pair.server;
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
client.connectionOpen();
|
||||
server.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -630,7 +638,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
public void testValuesAreRespected() throws Exception {
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
PaymentChannelServer server = pair.server;
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
client.connectionOpen();
|
||||
server.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -656,7 +664,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
server = pair.server;
|
||||
final Coin myValue = COIN.multiply(10);
|
||||
client = new PaymentChannelClient(wallet, myKey, myValue, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
client = new PaymentChannelClient(wallet, myKey, myValue, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
client.connectionOpen();
|
||||
server.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -684,7 +692,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
emptyWallet.freshReceiveKey();
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
PaymentChannelServer server = pair.server;
|
||||
PaymentChannelClient client = new PaymentChannelClient(emptyWallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(emptyWallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
client.connectionOpen();
|
||||
server.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -706,7 +714,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
public void testClientRefusesNonCanonicalKey() throws Exception {
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
PaymentChannelServer server = pair.server;
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
client.connectionOpen();
|
||||
server.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -724,7 +732,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
public void testClientResumeNothing() throws Exception {
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
PaymentChannelServer server = pair.server;
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
client.connectionOpen();
|
||||
server.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -738,7 +746,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
@Test
|
||||
public void testClientRandomMessage() throws Exception {
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
|
||||
|
||||
client.connectionOpen();
|
||||
pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION);
|
||||
@ -759,7 +767,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
Sha256Hash someServerId = Sha256Hash.ZERO_HASH;
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
pair.server.connectionOpen();
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
PaymentChannelServer server = pair.server;
|
||||
client.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -792,7 +800,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
client.connectionClosed();
|
||||
|
||||
// Now try opening a new channel with the same server ID and verify the client asks for a new channel.
|
||||
client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
client.connectionOpen();
|
||||
Protos.TwoWayChannelMessage msg = pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION);
|
||||
assertFalse(msg.getClientVersion().hasPreviousChannelContractHash());
|
||||
@ -807,7 +815,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
Sha256Hash someServerId = Sha256Hash.ZERO_HASH;
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
pair.server.connectionOpen();
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
PaymentChannelServer server = pair.server;
|
||||
client.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
@ -861,7 +869,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
Sha256Hash someServerId = Sha256Hash.ZERO_HASH;
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
pair.server.connectionOpen();
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
PaymentChannelServer server = pair.server;
|
||||
client.connectionOpen();
|
||||
final Protos.TwoWayChannelMessage msg = pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION);
|
||||
@ -892,7 +900,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
Sha256Hash someServerId = Sha256Hash.ZERO_HASH;
|
||||
ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
|
||||
pair.server.connectionOpen();
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, pair.clientRecorder, versionSelector);
|
||||
PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder);
|
||||
PaymentChannelServer server = pair.server;
|
||||
client.connectionOpen();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
|
||||
|
@ -34,6 +34,9 @@ import java.util.HashMap;
|
||||
|
||||
import static org.bitcoin.paymentchannel.Protos.TwoWayChannelMessage;
|
||||
import static org.bitcoin.paymentchannel.Protos.TwoWayChannelMessage.MessageType.*;
|
||||
import static org.bitcoinj.protocols.channels.PaymentChannelClient.VersionSelector.VERSION_1;
|
||||
import static org.bitcoinj.protocols.channels.PaymentChannelClient.VersionSelector.VERSION_2;
|
||||
import static org.bitcoinj.protocols.channels.PaymentChannelClient.VersionSelector.VERSION_2_ALLOW_1;
|
||||
import static org.easymock.EasyMock.capture;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
@ -55,16 +58,25 @@ public class PaymentChannelClientTest {
|
||||
* version of the channel.
|
||||
*/
|
||||
@Parameterized.Parameters(name = "{index}: PaymentChannelClientTest({0})")
|
||||
public static Collection<PaymentChannelClient.VersionSelector> data() {
|
||||
public static Collection<PaymentChannelClient.DefaultClientChannelProperties> data() {
|
||||
return Arrays.asList(
|
||||
PaymentChannelClient.VersionSelector.VERSION_1,
|
||||
PaymentChannelClient.VersionSelector.VERSION_2_ALLOW_1,
|
||||
PaymentChannelClient.VersionSelector.VERSION_2
|
||||
new PaymentChannelClient.DefaultClientChannelProperties() {
|
||||
@Override
|
||||
public PaymentChannelClient.VersionSelector versionSelector() { return VERSION_1;}
|
||||
},
|
||||
new PaymentChannelClient.DefaultClientChannelProperties() {
|
||||
@Override
|
||||
public PaymentChannelClient.VersionSelector versionSelector() { return VERSION_2_ALLOW_1;}
|
||||
},
|
||||
new PaymentChannelClient.DefaultClientChannelProperties() {
|
||||
@Override
|
||||
public PaymentChannelClient.VersionSelector versionSelector() { return VERSION_2;}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Parameterized.Parameter
|
||||
public PaymentChannelClient.VersionSelector versionSelector;
|
||||
public IPaymentChannelClient.ClientChannelProperties clientChannelProperties;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
@ -78,7 +90,7 @@ public class PaymentChannelClientTest {
|
||||
|
||||
@Test
|
||||
public void shouldSendClientVersionOnChannelOpen() throws Exception {
|
||||
PaymentChannelClient dut = new PaymentChannelClient(wallet, ecKey, maxValue, serverHash, connection, versionSelector);
|
||||
PaymentChannelClient dut = new PaymentChannelClient(wallet, ecKey, maxValue, serverHash, null, clientChannelProperties, connection);
|
||||
connection.sendToServer(capture(clientVersionCapture));
|
||||
EasyMock.expect(wallet.getExtensions()).andReturn(new HashMap<String, WalletExtension>());
|
||||
replay(connection, wallet);
|
||||
@ -87,10 +99,20 @@ public class PaymentChannelClientTest {
|
||||
}
|
||||
@Test
|
||||
public void shouldSendTimeWindowInClientVersion() throws Exception {
|
||||
long timeWindow = 4000;
|
||||
final long timeWindow = 4000;
|
||||
KeyParameter userKey = null;
|
||||
PaymentChannelClient dut =
|
||||
new PaymentChannelClient(wallet, ecKey, maxValue, serverHash, timeWindow, userKey, connection, versionSelector);
|
||||
new PaymentChannelClient(wallet, ecKey, maxValue, serverHash, userKey, new PaymentChannelClient.DefaultClientChannelProperties() {
|
||||
@Override
|
||||
public long timeWindow() {
|
||||
return timeWindow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentChannelClient.VersionSelector versionSelector() {
|
||||
return clientChannelProperties.versionSelector();
|
||||
}
|
||||
}, connection);
|
||||
connection.sendToServer(capture(clientVersionCapture));
|
||||
EasyMock.expect(wallet.getExtensions()).andReturn(new HashMap<String, WalletExtension>());
|
||||
replay(connection, wallet);
|
||||
@ -104,7 +126,7 @@ public class PaymentChannelClientTest {
|
||||
assertEquals("Wrong type " + type, CLIENT_VERSION, type);
|
||||
final Protos.ClientVersion clientVersion = response.getClientVersion();
|
||||
final int major = clientVersion.getMajor();
|
||||
final int requestedVersion = versionSelector.getRequestedMajorVersion();
|
||||
final int requestedVersion = clientChannelProperties.versionSelector().getRequestedMajorVersion();
|
||||
assertEquals("Wrong major version " + major, requestedVersion, major);
|
||||
final long actualTimeWindow = clientVersion.getTimeWindowSecs();
|
||||
assertEquals("Wrong timeWindow " + actualTimeWindow, expectedTimeWindow, actualTimeWindow );
|
||||
|
@ -92,7 +92,16 @@ public class PaymentChannelServerTest {
|
||||
connection.sendToClient(capture(initiateCapture));
|
||||
|
||||
replay(connection);
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, minTimeWindow, 40000, connection);
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties() {
|
||||
@Override
|
||||
public long getMinTimeWindow() {
|
||||
return minTimeWindow;
|
||||
}
|
||||
@Override
|
||||
public long getMaxTimeWindow() {
|
||||
return 40000;
|
||||
}
|
||||
}, connection);
|
||||
|
||||
dut.connectionOpen();
|
||||
dut.receiveMessage(message);
|
||||
@ -111,7 +120,14 @@ public class PaymentChannelServerTest {
|
||||
connection.sendToClient(capture(initiateCapture));
|
||||
replay(connection);
|
||||
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, 20000, maxTimeWindow, connection);
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){
|
||||
@Override
|
||||
public long getMaxTimeWindow() {
|
||||
return maxTimeWindow;
|
||||
}
|
||||
@Override
|
||||
public long getMinTimeWindow() { return 20000; }
|
||||
}, connection);
|
||||
|
||||
dut.connectionOpen();
|
||||
dut.receiveMessage(message);
|
||||
@ -123,12 +139,24 @@ public class PaymentChannelServerTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotAllowTimeWindowLessThan2h() {
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, 7199, 40000, connection);
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){
|
||||
@Override
|
||||
public long getMaxTimeWindow() { return 40000; }
|
||||
@Override
|
||||
public long getMinTimeWindow() {
|
||||
return 7199;
|
||||
}
|
||||
}, connection);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldNotAllowNegativeTimeWindow() {
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, 40001, 40000, connection);
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){
|
||||
@Override
|
||||
public long getMaxTimeWindow() { return 40000; }
|
||||
@Override
|
||||
public long getMinTimeWindow() { return 40001; }
|
||||
}, connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -139,7 +167,12 @@ public class PaymentChannelServerTest {
|
||||
replay(connection);
|
||||
final int expire = 24 * 60 * 60 - 60; // This the default defined in paymentchannel.proto
|
||||
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, expire, expire, connection);
|
||||
dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){
|
||||
@Override
|
||||
public long getMaxTimeWindow() { return expire; }
|
||||
@Override
|
||||
public long getMinTimeWindow() { return expire; }
|
||||
}, connection);
|
||||
dut.connectionOpen();
|
||||
long expectedExpire = Utils.currentTimeSeconds() + expire;
|
||||
dut.receiveMessage(message);
|
||||
|
@ -817,25 +817,21 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
|
||||
switch (versionSelector) {
|
||||
case VERSION_1:
|
||||
clientState = new PaymentChannelV1ClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), CENT, EXPIRE_TIME) {
|
||||
@Override
|
||||
protected void editContractSendRequest(SendRequest req) {
|
||||
req.coinSelector = wallet.getCoinSelector();
|
||||
}
|
||||
};
|
||||
clientState = new PaymentChannelV1ClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), CENT, EXPIRE_TIME) ;
|
||||
break;
|
||||
case VERSION_2_ALLOW_1:
|
||||
case VERSION_2:
|
||||
clientState = new PaymentChannelV2ClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), CENT, EXPIRE_TIME) {
|
||||
@Override
|
||||
protected void editContractSendRequest(SendRequest req) {
|
||||
req.coinSelector = wallet.getCoinSelector();
|
||||
}
|
||||
};
|
||||
clientState = new PaymentChannelV2ClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), CENT, EXPIRE_TIME);
|
||||
break;
|
||||
}
|
||||
assertEquals(PaymentChannelClientState.State.NEW, clientState.getState());
|
||||
clientState.initiate();
|
||||
clientState.initiate(null, new PaymentChannelClient.DefaultClientChannelProperties() {
|
||||
@Override
|
||||
public SendRequest modifyContractSendRequest(SendRequest sendRequest) {
|
||||
sendRequest.coinSelector = wallet.getCoinSelector();
|
||||
return sendRequest;
|
||||
}
|
||||
});
|
||||
assertEquals(getInitialClientState(), clientState.getState());
|
||||
|
||||
if (useRefunds()) {
|
||||
|
@ -23,10 +23,7 @@ import joptsimple.OptionSpec;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.kits.WalletAppKit;
|
||||
import org.bitcoinj.params.RegTestParams;
|
||||
import org.bitcoinj.protocols.channels.PaymentChannelClient;
|
||||
import org.bitcoinj.protocols.channels.PaymentChannelClientConnection;
|
||||
import org.bitcoinj.protocols.channels.StoredPaymentChannelClientStates;
|
||||
import org.bitcoinj.protocols.channels.ValueOutOfRangeException;
|
||||
import org.bitcoinj.protocols.channels.*;
|
||||
import org.bitcoinj.utils.BriefLogFormatter;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
@ -70,14 +67,21 @@ public class ExamplePaymentChannelClient {
|
||||
parser.printHelpOn(System.err);
|
||||
return;
|
||||
}
|
||||
PaymentChannelClient.VersionSelector versionSelector = PaymentChannelClient.VersionSelector.VERSION_1;
|
||||
IPaymentChannelClient.ClientChannelProperties clientChannelProperties = new PaymentChannelClient.DefaultClientChannelProperties(){
|
||||
@Override
|
||||
public PaymentChannelClient.VersionSelector versionSelector() { return PaymentChannelClient.VersionSelector.VERSION_1; }
|
||||
};
|
||||
|
||||
if (opts.has("version")) {
|
||||
switch (version.value(opts)) {
|
||||
case 1:
|
||||
versionSelector = PaymentChannelClient.VersionSelector.VERSION_1;
|
||||
// Keep the default
|
||||
break;
|
||||
case 2:
|
||||
versionSelector = PaymentChannelClient.VersionSelector.VERSION_2;
|
||||
clientChannelProperties = new PaymentChannelClient.DefaultClientChannelProperties(){
|
||||
@Override
|
||||
public PaymentChannelClient.VersionSelector versionSelector() { return PaymentChannelClient.VersionSelector.VERSION_2; }
|
||||
};
|
||||
break;
|
||||
default:
|
||||
System.err.println("Invalid version - valid versions are 1, 2");
|
||||
@ -85,7 +89,7 @@ public class ExamplePaymentChannelClient {
|
||||
}
|
||||
}
|
||||
NetworkParameters params = net.value(opts).get();
|
||||
new ExamplePaymentChannelClient().run(opts.nonOptionArguments().get(0), versionSelector, params);
|
||||
new ExamplePaymentChannelClient().run(opts.nonOptionArguments().get(0), clientChannelProperties, params);
|
||||
}
|
||||
|
||||
public ExamplePaymentChannelClient() {
|
||||
@ -94,7 +98,7 @@ public class ExamplePaymentChannelClient {
|
||||
params = RegTestParams.get();
|
||||
}
|
||||
|
||||
public void run(final String host, PaymentChannelClient.VersionSelector versionSelector, final NetworkParameters params) throws Exception {
|
||||
public void run(final String host, IPaymentChannelClient.ClientChannelProperties clientChannelProperties, final NetworkParameters params) throws Exception {
|
||||
// Bring up all the objects we need, create/load a wallet, sync the chain, etc. We override WalletAppKit so we
|
||||
// can customize it by adding the extension objects - we have to do this before the wallet file is loaded so
|
||||
// the plugin that knows how to parse all the additional data is present during the load.
|
||||
@ -137,19 +141,19 @@ public class ExamplePaymentChannelClient {
|
||||
// demonstrates resuming a channel that wasn't closed yet. It should close automatically once we run out
|
||||
// of money on the channel.
|
||||
log.info("Round one ...");
|
||||
openAndSend(timeoutSeconds, server, channelID, 5, versionSelector);
|
||||
openAndSend(timeoutSeconds, server, channelID, 5, clientChannelProperties);
|
||||
log.info("Round two ...");
|
||||
log.info(appKit.wallet().toString());
|
||||
openAndSend(timeoutSeconds, server, channelID, 4, versionSelector); // 4 times because the opening of the channel made a payment.
|
||||
openAndSend(timeoutSeconds, server, channelID, 4, clientChannelProperties); // 4 times because the opening of the channel made a payment.
|
||||
log.info("Stopping ...");
|
||||
appKit.stopAsync();
|
||||
appKit.awaitTerminated();
|
||||
}
|
||||
|
||||
private void openAndSend(int timeoutSecs, InetSocketAddress server, String channelID, final int times, PaymentChannelClient.VersionSelector versionSelector) throws IOException, ValueOutOfRangeException, InterruptedException {
|
||||
private void openAndSend(int timeoutSecs, InetSocketAddress server, String channelID, final int times, IPaymentChannelClient.ClientChannelProperties clientChannelProperties) throws IOException, ValueOutOfRangeException, InterruptedException {
|
||||
// Use protocol version 1 for simplicity
|
||||
PaymentChannelClientConnection client = new PaymentChannelClientConnection(
|
||||
server, timeoutSecs, appKit.wallet(), myKey, channelSize, channelID, versionSelector);
|
||||
server, timeoutSecs, appKit.wallet(), myKey, channelSize, channelID, null, clientChannelProperties);
|
||||
// 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>() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user