Support for bundling an optional info Protobuf ByteString with a PaymentAck message

This commit is contained in:
cyberzac 2014-07-31 10:24:38 +02:00 committed by Mike Hearn
parent f1dd47443f
commit eff9ac2ecc
13 changed files with 793 additions and 105 deletions

View File

@ -17,7 +17,7 @@ public class NativePaymentChannelServerConnectionEventHandler extends ServerConn
public native void channelOpen(Sha256Hash channelId);
@Override
public native void paymentIncrease(Coin by, Coin to, ByteString info);
public native ByteString paymentIncrease(Coin by, Coin to, ByteString info);
@Override
public native void channelClosed(PaymentChannelCloseException.CloseReason reason);

View File

@ -84,7 +84,7 @@ public interface IPaymentChannelClient {
* (see {@link PaymentChannelClientConnection#getChannelOpenFuture()} for the second)
* @return a future that completes when the server acknowledges receipt and acceptance of the payment.
*/
ListenableFuture<Coin> incrementPayment(Coin size, @Nullable ByteString info) throws ValueOutOfRangeException, IllegalStateException;
ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size, @Nullable ByteString info) throws ValueOutOfRangeException, IllegalStateException;
/**
* Implements the connection between this client and the server, providing an interface which allows messages to be

View File

@ -90,7 +90,7 @@ public class PaymentChannelClient implements IPaymentChannelClient {
@GuardedBy("lock") private long minPayment;
@GuardedBy("lock") SettableFuture<Coin> increasePaymentFuture;
@GuardedBy("lock") SettableFuture<PaymentIncrementAck> increasePaymentFuture;
@GuardedBy("lock") Coin lastPaymentActualAmount;
/**
@ -290,7 +290,7 @@ public class PaymentChannelClient implements IPaymentChannelClient {
receiveChannelOpen();
return;
case PAYMENT_ACK:
receivePaymentAck();
receivePaymentAck(msg.getPaymentAck());
return;
case CLOSE:
receiveClose(msg);
@ -462,13 +462,13 @@ public class PaymentChannelClient implements IPaymentChannelClient {
* you wait for the previous increase payment future to complete before incrementing the payment again.
*
* @param size How many satoshis to increment the payment by (note: not the new total).
* @return a future that completes when the server acknowledges receipt and acceptance of the payment.
* @throws ValueOutOfRangeException If the size is negative or would pay more than this channel's total value
* ({@link PaymentChannelClientConnection#state()}.getTotalValue())
* @throws IllegalStateException If the channel has been closed or is not yet open
* (see {@link PaymentChannelClientConnection#getChannelOpenFuture()} for the second)
* @return a future that completes when the server acknowledges receipt and acceptance of the payment.
*/
public ListenableFuture<Coin> incrementPayment(Coin size) throws ValueOutOfRangeException, IllegalStateException {
public ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size) throws ValueOutOfRangeException, IllegalStateException {
return incrementPayment(size, null);
}
@ -481,14 +481,14 @@ public class PaymentChannelClient implements IPaymentChannelClient {
*
* @param size How many satoshis to increment the payment by (note: not the new total).
* @param info Information about this update, used to extend this protocol.
* @return a future that completes when the server acknowledges receipt and acceptance of the payment.
* @throws ValueOutOfRangeException If the size is negative or would pay more than this channel's total value
* ({@link PaymentChannelClientConnection#state()}.getTotalValue())
* @throws IllegalStateException If the channel has been closed or is not yet open
* (see {@link PaymentChannelClientConnection#getChannelOpenFuture()} for the second)
* @return a future that completes when the server acknowledges receipt and acceptance of the payment.
*/
@Override
public ListenableFuture<Coin> incrementPayment(Coin size, @Nullable ByteString info) throws ValueOutOfRangeException, IllegalStateException {
public ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size, @Nullable ByteString info) throws ValueOutOfRangeException, IllegalStateException {
lock.lock();
try {
if (state() == null || !connectionOpen || step != InitStep.CHANNEL_OPEN)
@ -523,8 +523,8 @@ public class PaymentChannelClient implements IPaymentChannelClient {
}
}
private void receivePaymentAck() {
SettableFuture<Coin> future;
private void receivePaymentAck(Protos.PaymentAck paymentAck) {
SettableFuture<PaymentIncrementAck> future;
Coin value;
lock.lock();
@ -539,6 +539,6 @@ public class PaymentChannelClient implements IPaymentChannelClient {
}
// Ensure the future runs without the client lock held.
future.set(value);
future.set(new PaymentIncrementAck(value, paymentAck.getInfo()));
}
}

View File

@ -136,7 +136,7 @@ public class PaymentChannelClientConnection {
* @throws IllegalStateException If the channel has been closed or is not yet open
* (see {@link PaymentChannelClientConnection#getChannelOpenFuture()} for the second)
*/
public ListenableFuture<Coin> incrementPayment(Coin size) throws ValueOutOfRangeException, IllegalStateException {
public ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size) throws ValueOutOfRangeException, IllegalStateException {
return channelClient.incrementPayment(size, null);
}
/**
@ -149,7 +149,7 @@ public class PaymentChannelClientConnection {
* @throws IllegalStateException If the channel has been closed or is not yet open
* (see {@link PaymentChannelClientConnection#getChannelOpenFuture()} for the second)
*/
public ListenableFuture<Coin> incrementPayment(Coin size, ByteString info) throws ValueOutOfRangeException, IllegalStateException {
public ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size, ByteString info) throws ValueOutOfRangeException, IllegalStateException {
return channelClient.incrementPayment(size, info);
}

View File

@ -101,8 +101,10 @@ public class PaymentChannelServer {
* @param by The increase in total payment
* @param to The new total payment to us (not including fees which may be required to claim the payment)
* @param info Information about this payment increase, used to extend this protocol.
* @return An ack message that will be included in the PaymentAck message to the client. Use null for no ack message.
*/
public void paymentIncrease(Coin by, Coin to, @Nullable ByteString info);
@Nullable
public ByteString paymentIncrease(Coin by, Coin to, @Nullable ByteString info);
}
private final ServerConnection conn;
@ -315,14 +317,16 @@ public class PaymentChannelServer {
boolean stillUsable = state.incrementPayment(refundSize, msg.getSignature().toByteArray());
Coin bestPaymentChange = state.getBestValueToMe().subtract(lastBestPayment);
ByteString ackInfo = null;
if (bestPaymentChange.signum() > 0) {
ByteString info = (msg.hasInfo()) ? msg.getInfo() : null;
conn.paymentIncrease(bestPaymentChange, state.getBestValueToMe(), info);
ackInfo = conn.paymentIncrease(bestPaymentChange, state.getBestValueToMe(), info);
}
if (sendAck) {
Protos.TwoWayChannelMessage.Builder ack = Protos.TwoWayChannelMessage.newBuilder();
ack.setType(Protos.TwoWayChannelMessage.MessageType.PAYMENT_ACK);
if (ackInfo != null) ack.setPaymentAck(ack.getPaymentAckBuilder().setInfo(ackInfo));
conn.sendToClient(ack.build());
}

View File

@ -83,8 +83,8 @@ public class PaymentChannelServerListener {
eventHandler.channelOpen(contractHash);
}
@Override public void paymentIncrease(Coin by, Coin to, @Nullable ByteString info) {
eventHandler.paymentIncrease(by, to, info);
@Override public ByteString paymentIncrease(Coin by, Coin to, @Nullable ByteString info) {
return eventHandler.paymentIncrease(by, to, info);
}
});

View File

@ -0,0 +1,28 @@
package com.google.bitcoin.protocols.channels;
import com.google.bitcoin.core.Coin;
import com.google.protobuf.ByteString;
import javax.annotation.Nullable;
/**
* An acknowledgement of a payment increase
*/
public class PaymentIncrementAck {
private final Coin value;
@Nullable private final ByteString info;
public PaymentIncrementAck(Coin value, @Nullable ByteString info) {
this.value = value;
this.info = info;
}
public Coin getValue() {
return value;
}
@Nullable
public ByteString getInfo() {
return info;
}
}

View File

@ -71,8 +71,10 @@ public abstract class ServerConnectionEventHandler {
* @param by The increase in total payment
* @param to The new total payment to us (not including fees which may be required to claim the payment)
* @param info Information about this payment increase, used to extend this protocol.
* @return acknowledgment information to be sent to the client.
*/
public abstract void paymentIncrease(Coin by, Coin to, ByteString info);
@Nullable
public abstract ByteString paymentIncrease(Coin by, Coin to, ByteString info);
/**
* <p>Called when the channel was closed for some reason. May be called without a call to

File diff suppressed because it is too large Load Diff

View File

@ -83,6 +83,7 @@ message TwoWayChannelMessage {
optional ReturnRefund return_refund = 6;
optional ProvideContract provide_contract = 7;
optional UpdatePayment update_payment = 8;
optional PaymentAck payment_ack = 11;
optional Settlement settlement = 9;
optional Error error = 10;
@ -214,6 +215,12 @@ message UpdatePayment {
}
// This message is sent as an acknowledgement of an UpdatePayment message
message PaymentAck {
// Information about this update. Used to extend this protocol
optional bytes info = 1;
}
message Settlement {
// A copy of the fully signed final contract that settles the channel. The client can verify
// the transaction is correct and then commit it to their wallet.

View File

@ -130,8 +130,9 @@ public class ChannelConnectionTest extends TestWithWallet {
}
@Override
public void paymentIncrease(Coin by, Coin to, ByteString info) {
public ByteString paymentIncrease(Coin by, Coin to, ByteString info) {
q.add(new ChannelTestUtils.UpdatePair(to, info));
return null;
}
@Override
@ -682,20 +683,20 @@ public class ChannelConnectionTest extends TestWithWallet {
pair.clientRecorder.checkInitiated();
assertNull(pair.serverRecorder.q.poll());
assertNull(pair.clientRecorder.q.poll());
ListenableFuture<Coin> future = client.incrementPayment(CENT);
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT));
pair.serverRecorder.q.take();
client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.PAYMENT_ACK));
assertTrue(future.isDone());
client.incrementPayment(CENT);
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT));
pair.serverRecorder.q.take();
client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.PAYMENT_ACK));
client.incrementPayment(CENT);
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT));
pair.serverRecorder.q.take();
client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.PAYMENT_ACK));
for (int i = 0; i < 3; i++) {
ListenableFuture<PaymentIncrementAck> future = client.incrementPayment(CENT);
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT));
pair.serverRecorder.q.take();
final Protos.TwoWayChannelMessage msg = pair.serverRecorder.checkNextMsg(MessageType.PAYMENT_ACK);
final Protos.PaymentAck paymentAck = msg.getPaymentAck();
assertTrue("No PaymentAck.Info", paymentAck.hasInfo());
assertEquals("Wrong PaymentAck info", ByteString.copyFromUtf8(CENT.toPlainString()), paymentAck.getInfo());
client.receiveMessage(msg);
assertTrue(future.isDone());
final PaymentIncrementAck paymentIncrementAck = future.get();
assertEquals("Wrong value returned from increasePayment", CENT, paymentIncrementAck.getValue());
assertEquals("Wrong info returned from increasePayment", ByteString.copyFromUtf8(CENT.toPlainString()), paymentIncrementAck.getInfo());
}
// Settle it and verify it's considered to be settled.
broadcastTxPause.release();

View File

@ -37,8 +37,9 @@ public class ChannelTestUtils {
}
@Override
public void paymentIncrease(Coin by, Coin to, @Nullable ByteString info) {
public ByteString paymentIncrease(Coin by, Coin to, @Nullable ByteString info) {
q.add(new UpdatePair(to, info));
return ByteString.copyFromUtf8(by.toPlainString());
}
public Protos.TwoWayChannelMessage getNextMsg() throws InterruptedException {

View File

@ -102,8 +102,9 @@ public class ExamplePaymentChannelServer implements PaymentChannelServerListener
}
@Override
public void paymentIncrease(Coin by, Coin to, ByteString info) {
public ByteString paymentIncrease(Coin by, Coin to, ByteString info) {
log.info("Client {} paid increased payment by {} for a total of " + to.toString(), clientAddress, by);
return null;
}
@Override